Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #include <linux/err.h>
0003 #include <linux/bug.h>
0004 #include <linux/atomic.h>
0005 #include <linux/errseq.h>
0006 #include <linux/log2.h>
0007 
0008 /*
0009  * An errseq_t is a way of recording errors in one place, and allowing any
0010  * number of "subscribers" to tell whether it has changed since a previous
0011  * point where it was sampled.
0012  *
0013  * It's implemented as an unsigned 32-bit value. The low order bits are
0014  * designated to hold an error code (between 0 and -MAX_ERRNO). The upper bits
0015  * are used as a counter. This is done with atomics instead of locking so that
0016  * these functions can be called from any context.
0017  *
0018  * The general idea is for consumers to sample an errseq_t value. That value
0019  * can later be used to tell whether any new errors have occurred since that
0020  * sampling was done.
0021  *
0022  * Note that there is a risk of collisions if new errors are being recorded
0023  * frequently, since we have so few bits to use as a counter.
0024  *
0025  * To mitigate this, one bit is used as a flag to tell whether the value has
0026  * been sampled since a new value was recorded. That allows us to avoid bumping
0027  * the counter if no one has sampled it since the last time an error was
0028  * recorded.
0029  *
0030  * A new errseq_t should always be zeroed out.  A errseq_t value of all zeroes
0031  * is the special (but common) case where there has never been an error. An all
0032  * zero value thus serves as the "epoch" if one wishes to know whether there
0033  * has ever been an error set since it was first initialized.
0034  */
0035 
0036 /* The low bits are designated for error code (max of MAX_ERRNO) */
0037 #define ERRSEQ_SHIFT        ilog2(MAX_ERRNO + 1)
0038 
0039 /* This bit is used as a flag to indicate whether the value has been seen */
0040 #define ERRSEQ_SEEN     (1 << ERRSEQ_SHIFT)
0041 
0042 /* The lowest bit of the counter */
0043 #define ERRSEQ_CTR_INC      (1 << (ERRSEQ_SHIFT + 1))
0044 
0045 /**
0046  * errseq_set - set a errseq_t for later reporting
0047  * @eseq: errseq_t field that should be set
0048  * @err: error to set (must be between -1 and -MAX_ERRNO)
0049  *
0050  * This function sets the error in @eseq, and increments the sequence counter
0051  * if the last sequence was sampled at some point in the past.
0052  *
0053  * Any error set will always overwrite an existing error.
0054  *
0055  * Return: The previous value, primarily for debugging purposes. The
0056  * return value should not be used as a previously sampled value in later
0057  * calls as it will not have the SEEN flag set.
0058  */
0059 errseq_t errseq_set(errseq_t *eseq, int err)
0060 {
0061     errseq_t cur, old;
0062 
0063     /* MAX_ERRNO must be able to serve as a mask */
0064     BUILD_BUG_ON_NOT_POWER_OF_2(MAX_ERRNO + 1);
0065 
0066     /*
0067      * Ensure the error code actually fits where we want it to go. If it
0068      * doesn't then just throw a warning and don't record anything. We
0069      * also don't accept zero here as that would effectively clear a
0070      * previous error.
0071      */
0072     old = READ_ONCE(*eseq);
0073 
0074     if (WARN(unlikely(err == 0 || (unsigned int)-err > MAX_ERRNO),
0075                 "err = %d\n", err))
0076         return old;
0077 
0078     for (;;) {
0079         errseq_t new;
0080 
0081         /* Clear out error bits and set new error */
0082         new = (old & ~(MAX_ERRNO|ERRSEQ_SEEN)) | -err;
0083 
0084         /* Only increment if someone has looked at it */
0085         if (old & ERRSEQ_SEEN)
0086             new += ERRSEQ_CTR_INC;
0087 
0088         /* If there would be no change, then call it done */
0089         if (new == old) {
0090             cur = new;
0091             break;
0092         }
0093 
0094         /* Try to swap the new value into place */
0095         cur = cmpxchg(eseq, old, new);
0096 
0097         /*
0098          * Call it success if we did the swap or someone else beat us
0099          * to it for the same value.
0100          */
0101         if (likely(cur == old || cur == new))
0102             break;
0103 
0104         /* Raced with an update, try again */
0105         old = cur;
0106     }
0107     return cur;
0108 }
0109 EXPORT_SYMBOL(errseq_set);
0110 
0111 /**
0112  * errseq_sample() - Grab current errseq_t value.
0113  * @eseq: Pointer to errseq_t to be sampled.
0114  *
0115  * This function allows callers to initialise their errseq_t variable.
0116  * If the error has been "seen", new callers will not see an old error.
0117  * If there is an unseen error in @eseq, the caller of this function will
0118  * see it the next time it checks for an error.
0119  *
0120  * Context: Any context.
0121  * Return: The current errseq value.
0122  */
0123 errseq_t errseq_sample(errseq_t *eseq)
0124 {
0125     errseq_t old = READ_ONCE(*eseq);
0126 
0127     /* If nobody has seen this error yet, then we can be the first. */
0128     if (!(old & ERRSEQ_SEEN))
0129         old = 0;
0130     return old;
0131 }
0132 EXPORT_SYMBOL(errseq_sample);
0133 
0134 /**
0135  * errseq_check() - Has an error occurred since a particular sample point?
0136  * @eseq: Pointer to errseq_t value to be checked.
0137  * @since: Previously-sampled errseq_t from which to check.
0138  *
0139  * Grab the value that eseq points to, and see if it has changed @since
0140  * the given value was sampled. The @since value is not advanced, so there
0141  * is no need to mark the value as seen.
0142  *
0143  * Return: The latest error set in the errseq_t or 0 if it hasn't changed.
0144  */
0145 int errseq_check(errseq_t *eseq, errseq_t since)
0146 {
0147     errseq_t cur = READ_ONCE(*eseq);
0148 
0149     if (likely(cur == since))
0150         return 0;
0151     return -(cur & MAX_ERRNO);
0152 }
0153 EXPORT_SYMBOL(errseq_check);
0154 
0155 /**
0156  * errseq_check_and_advance() - Check an errseq_t and advance to current value.
0157  * @eseq: Pointer to value being checked and reported.
0158  * @since: Pointer to previously-sampled errseq_t to check against and advance.
0159  *
0160  * Grab the eseq value, and see whether it matches the value that @since
0161  * points to. If it does, then just return 0.
0162  *
0163  * If it doesn't, then the value has changed. Set the "seen" flag, and try to
0164  * swap it into place as the new eseq value. Then, set that value as the new
0165  * "since" value, and return whatever the error portion is set to.
0166  *
0167  * Note that no locking is provided here for concurrent updates to the "since"
0168  * value. The caller must provide that if necessary. Because of this, callers
0169  * may want to do a lockless errseq_check before taking the lock and calling
0170  * this.
0171  *
0172  * Return: Negative errno if one has been stored, or 0 if no new error has
0173  * occurred.
0174  */
0175 int errseq_check_and_advance(errseq_t *eseq, errseq_t *since)
0176 {
0177     int err = 0;
0178     errseq_t old, new;
0179 
0180     /*
0181      * Most callers will want to use the inline wrapper to check this,
0182      * so that the common case of no error is handled without needing
0183      * to take the lock that protects the "since" value.
0184      */
0185     old = READ_ONCE(*eseq);
0186     if (old != *since) {
0187         /*
0188          * Set the flag and try to swap it into place if it has
0189          * changed.
0190          *
0191          * We don't care about the outcome of the swap here. If the
0192          * swap doesn't occur, then it has either been updated by a
0193          * writer who is altering the value in some way (updating
0194          * counter or resetting the error), or another reader who is
0195          * just setting the "seen" flag. Either outcome is OK, and we
0196          * can advance "since" and return an error based on what we
0197          * have.
0198          */
0199         new = old | ERRSEQ_SEEN;
0200         if (new != old)
0201             cmpxchg(eseq, old, new);
0202         *since = new;
0203         err = -(new & MAX_ERRNO);
0204     }
0205     return err;
0206 }
0207 EXPORT_SYMBOL(errseq_check_and_advance);