six locks: Documentation, renaming
authorKent Overstreet <kent.overstreet@linux.dev>
Sun, 21 May 2023 19:40:40 +0000 (15:40 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Sun, 22 Oct 2023 21:10:02 +0000 (17:10 -0400)
 - Expanded and revamped overview documentation in six.h, giving an
   overview of all features
 - docbook-comments for all external interfaces
 - Rename some functions for simplicity, i.e.
   six_lock_ip_type() -> six_lock_ip()

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/btree_locking.h
fs/bcachefs/six.c
fs/bcachefs/six.h

index a897bdc123c3d60880f7e3055aa4a5d7f4dc4ccf..f9bb8736c061a580ff83aa36e886151cf1d2f0e2 100644 (file)
@@ -205,8 +205,8 @@ static inline int __btree_node_lock_nopath(struct btree_trans *trans,
        trans->lock_must_abort  = false;
        trans->locking          = b;
 
-       ret = six_lock_type_ip_waiter(&b->lock, type, &trans->locking_wait,
-                                  bch2_six_check_for_deadlock, trans, ip);
+       ret = six_lock_ip_waiter(&b->lock, type, &trans->locking_wait,
+                                bch2_six_check_for_deadlock, trans, ip);
        WRITE_ONCE(trans->locking, NULL);
        WRITE_ONCE(trans->locking_wait.start_time, 0);
        return ret;
index 2e222eb2a9074c12f923fd0a9623a5b6dad49022..a1f007095ec9d9d6aaaf51d011a84ff1f1d4376c 100644 (file)
@@ -193,10 +193,8 @@ static inline unsigned pcpu_read_count(struct six_lock *lock)
        return read_count;
 }
 
-static int __do_six_trylock_type(struct six_lock *lock,
-                                enum six_lock_type type,
-                                struct task_struct *task,
-                                bool try)
+static int __do_six_trylock(struct six_lock *lock, enum six_lock_type type,
+                           struct task_struct *task, bool try)
 {
        const struct six_lock_vals l[] = LOCK_VALS;
        int ret;
@@ -316,7 +314,7 @@ again:
                        goto unlock;
                saw_one = true;
 
-               ret = __do_six_trylock_type(lock, lock_type, w->task, false);
+               ret = __do_six_trylock(lock, lock_type, w->task, false);
                if (ret <= 0)
                        goto unlock;
 
@@ -355,32 +353,48 @@ static void six_lock_wakeup(struct six_lock *lock, u64 state,
 }
 
 __always_inline
-static bool do_six_trylock_type(struct six_lock *lock,
-                               enum six_lock_type type,
-                               bool try)
+static bool do_six_trylock(struct six_lock *lock, enum six_lock_type type, bool try)
 {
        int ret;
 
-       ret = __do_six_trylock_type(lock, type, current, try);
+       ret = __do_six_trylock(lock, type, current, try);
        if (ret < 0)
                __six_lock_wakeup(lock, -ret - 1);
 
        return ret > 0;
 }
 
-bool six_trylock_ip_type(struct six_lock *lock, enum six_lock_type type,
-                        unsigned long ip)
+/**
+ * six_trylock_ip - attempt to take a six lock without blocking
+ * @lock:      lock to take
+ * @type:      SIX_LOCK_read, SIX_LOCK_intent, or SIX_LOCK_write
+ * @ip:                ip parameter for lockdep/lockstat, i.e. _THIS_IP_
+ *
+ * Return: true on success, false on failure.
+ */
+bool six_trylock_ip(struct six_lock *lock, enum six_lock_type type, unsigned long ip)
 {
-       if (!do_six_trylock_type(lock, type, true))
+       if (!do_six_trylock(lock, type, true))
                return false;
 
        if (type != SIX_LOCK_write)
                six_acquire(&lock->dep_map, 1, type == SIX_LOCK_read, ip);
        return true;
 }
+EXPORT_SYMBOL_GPL(six_trylock_ip);
 
-bool six_relock_ip_type(struct six_lock *lock, enum six_lock_type type,
-                       unsigned seq, unsigned long ip)
+/**
+ * six_relock_ip - attempt to re-take a lock that was held previously
+ * @lock:      lock to take
+ * @type:      SIX_LOCK_read, SIX_LOCK_intent, or SIX_LOCK_write
+ * @seq:       lock sequence number obtained from six_lock_seq() while lock was
+ *             held previously
+ * @ip:                ip parameter for lockdep/lockstat, i.e. _THIS_IP_
+ *
+ * Return: true on success, false on failure.
+ */
+bool six_relock_ip(struct six_lock *lock, enum six_lock_type type,
+                  unsigned seq, unsigned long ip)
 {
        const struct six_lock_vals l[] = LOCK_VALS;
        u64 old, v;
@@ -421,15 +435,15 @@ bool six_relock_ip_type(struct six_lock *lock, enum six_lock_type type,
                if ((old & l[type].lock_fail) || six_state_seq(old) != seq)
                        return false;
        } while ((v = atomic64_cmpxchg_acquire(&lock->state,
-                               old,
-                               old + l[type].lock_val)) != old);
+                                              old,
+                                              old + l[type].lock_val)) != old);
 
        six_set_owner(lock, type, old, current);
        if (type != SIX_LOCK_write)
                six_acquire(&lock->dep_map, 1, type == SIX_LOCK_read, ip);
        return true;
 }
-EXPORT_SYMBOL_GPL(six_relock_ip_type);
+EXPORT_SYMBOL_GPL(six_relock_ip);
 
 #ifdef CONFIG_SIX_LOCK_SPIN_ON_OWNER
 
@@ -512,7 +526,7 @@ static inline bool six_optimistic_spin(struct six_lock *lock, enum six_lock_type
                if (owner && !six_spin_on_owner(lock, owner, end_time))
                        break;
 
-               if (do_six_trylock_type(lock, type, false)) {
+               if (do_six_trylock(lock, type, false)) {
                        osq_unlock(&lock->osq);
                        preempt_enable();
                        return true;
@@ -561,10 +575,10 @@ static inline bool six_optimistic_spin(struct six_lock *lock, enum six_lock_type
 #endif
 
 noinline
-static int __six_lock_type_slowpath(struct six_lock *lock, enum six_lock_type type,
-                                   struct six_lock_waiter *wait,
-                                   six_lock_should_sleep_fn should_sleep_fn, void *p,
-                                   unsigned long ip)
+static int six_lock_slowpath(struct six_lock *lock, enum six_lock_type type,
+                            struct six_lock_waiter *wait,
+                            six_lock_should_sleep_fn should_sleep_fn, void *p,
+                            unsigned long ip)
 {
        u64 old;
        int ret = 0;
@@ -587,10 +601,10 @@ static int __six_lock_type_slowpath(struct six_lock *lock, enum six_lock_type ty
        raw_spin_lock(&lock->wait_lock);
        six_set_bitmask(lock, SIX_STATE_WAITING_READ << type);
        /*
-        * Retry taking the lock after taking waitlist lock, have raced with an
-        * unlock:
+        * Retry taking the lock after taking waitlist lock, in case we raced
+        * with an unlock:
         */
-       ret = __do_six_trylock_type(lock, type, current, false);
+       ret = __do_six_trylock(lock, type, current, false);
        if (ret <= 0) {
                wait->start_time = local_clock();
 
@@ -648,10 +662,40 @@ out:
        return ret;
 }
 
-int six_lock_type_ip_waiter(struct six_lock *lock, enum six_lock_type type,
-                           struct six_lock_waiter *wait,
-                           six_lock_should_sleep_fn should_sleep_fn, void *p,
-                           unsigned long ip)
+/**
+ * six_lock_ip_waiter - take a lock, with full waitlist interface
+ * @lock:      lock to take
+ * @type:      SIX_LOCK_read, SIX_LOCK_intent, or SIX_LOCK_write
+ * @wait:      pointer to wait object, which will be added to lock's waitlist
+ * @should_sleep_fn: callback run after adding to waitlist, immediately prior
+ *             to scheduling
+ * @p:         passed through to @should_sleep_fn
+ * @ip:                ip parameter for lockdep/lockstat, i.e. _THIS_IP_
+ *
+ * This is the most general six_lock() variant, with parameters to support full
+ * cycle detection for deadlock avoidance.
+ *
+ * The code calling this function must implement tracking of held locks, and the
+ * @wait object should be embedded into the struct that tracks held locks -
+ * which must also be accessible in a thread-safe way.
+ *
+ * @should_sleep_fn should invoke the cycle detector; it should walk each
+ * lock's waiters, and for each waiter recursively walk their held locks.
+ *
+ * When this function must block, @wait will be added to @lock's waitlist before
+ * calling trylock, and before calling @should_sleep_fn, and @wait will not be
+ * removed from the lock waitlist until the lock has been successfully acquired,
+ * or we abort.
+ *
+ * @wait.start_time will be monotonically increasing for any given waitlist, and
+ * thus may be used as a loop cursor.
+ *
+ * Return: 0 on success, or the return code from @should_sleep_fn on failure.
+ */
+int six_lock_ip_waiter(struct six_lock *lock, enum six_lock_type type,
+                      struct six_lock_waiter *wait,
+                      six_lock_should_sleep_fn should_sleep_fn, void *p,
+                      unsigned long ip)
 {
        int ret;
 
@@ -660,8 +704,8 @@ int six_lock_type_ip_waiter(struct six_lock *lock, enum six_lock_type type,
        if (type != SIX_LOCK_write)
                six_acquire(&lock->dep_map, 0, type == SIX_LOCK_read, ip);
 
-       ret = do_six_trylock_type(lock, type, true) ? 0
-               : __six_lock_type_slowpath(lock, type, wait, should_sleep_fn, p, ip);
+       ret = do_six_trylock(lock, type, true) ? 0
+               : six_lock_slowpath(lock, type, wait, should_sleep_fn, p, ip);
 
        if (ret && type != SIX_LOCK_write)
                six_release(&lock->dep_map, ip);
@@ -670,7 +714,7 @@ int six_lock_type_ip_waiter(struct six_lock *lock, enum six_lock_type type,
 
        return ret;
 }
-EXPORT_SYMBOL_GPL(six_lock_type_ip_waiter);
+EXPORT_SYMBOL_GPL(six_lock_ip_waiter);
 
 __always_inline
 static void do_six_unlock_type(struct six_lock *lock, enum six_lock_type type)
@@ -700,7 +744,22 @@ static void do_six_unlock_type(struct six_lock *lock, enum six_lock_type type)
        six_lock_wakeup(lock, state, l[type].unlock_wakeup);
 }
 
-void six_unlock_ip_type(struct six_lock *lock, enum six_lock_type type, unsigned long ip)
+/**
+ * six_unlock_ip - drop a six lock
+ * @lock:      lock to unlock
+ * @type:      SIX_LOCK_read, SIX_LOCK_intent, or SIX_LOCK_write
+ * @ip:                ip parameter for lockdep/lockstat, i.e. _THIS_IP_
+ *
+ * When a lock is held multiple times (because six_lock_incement()) was used),
+ * this decrements the 'lock held' counter by one.
+ *
+ * For example:
+ * six_lock_read(&foo->lock);                          read count 1
+ * six_lock_increment(&foo->lock, SIX_LOCK_read);      read count 2
+ * six_lock_unlock(&foo->lock, SIX_LOCK_read);         read count 1
+ * six_lock_unlock(&foo->lock, SIX_LOCK_read);         read count 0
+ */
+void six_unlock_ip(struct six_lock *lock, enum six_lock_type type, unsigned long ip)
 {
        EBUG_ON(type == SIX_LOCK_write &&
                !(atomic64_read(&lock->state) & SIX_LOCK_HELD_intent));
@@ -719,9 +778,14 @@ void six_unlock_ip_type(struct six_lock *lock, enum six_lock_type type, unsigned
 
        do_six_unlock_type(lock, type);
 }
-EXPORT_SYMBOL_GPL(six_unlock_ip_type);
+EXPORT_SYMBOL_GPL(six_unlock_ip);
 
-/* Convert from intent to read: */
+/**
+ * six_lock_downgrade - convert an intent lock to a read lock
+ * @lock:      lock to dowgrade
+ *
+ * @lock will have read count incremented and intent count decremented
+ */
 void six_lock_downgrade(struct six_lock *lock)
 {
        six_lock_increment(lock, SIX_LOCK_read);
@@ -729,6 +793,15 @@ void six_lock_downgrade(struct six_lock *lock)
 }
 EXPORT_SYMBOL_GPL(six_lock_downgrade);
 
+/**
+ * six_lock_tryupgrade - attempt to convert read lock to an intent lock
+ * @lock:      lock to upgrade
+ *
+ * On success, @lock will have intent count incremented and read count
+ * decremented
+ *
+ * Return: true on success, false on failure
+ */
 bool six_lock_tryupgrade(struct six_lock *lock)
 {
        const struct six_lock_vals l[] = LOCK_VALS;
@@ -757,6 +830,17 @@ bool six_lock_tryupgrade(struct six_lock *lock)
 }
 EXPORT_SYMBOL_GPL(six_lock_tryupgrade);
 
+/**
+ * six_trylock_convert - attempt to convert a held lock from one type to another
+ * @lock:      lock to upgrade
+ * @from:      SIX_LOCK_read or SIX_LOCK_intent
+ * @to:                SIX_LOCK_read or SIX_LOCK_intent
+ *
+ * On success, @lock will have intent count incremented and read count
+ * decremented
+ *
+ * Return: true on success, false on failure
+ */
 bool six_trylock_convert(struct six_lock *lock,
                         enum six_lock_type from,
                         enum six_lock_type to)
@@ -775,9 +859,16 @@ bool six_trylock_convert(struct six_lock *lock,
 }
 EXPORT_SYMBOL_GPL(six_trylock_convert);
 
-/*
- * Increment read/intent lock count, assuming we already have it read or intent
- * locked:
+/**
+ * six_lock_increment - increase held lock count on a lock that is already held
+ * @lock:      lock to increment
+ * @type:      SIX_LOCK_read or SIX_LOCK_intent
+ *
+ * @lock must already be held, with a lock type that is greater than or equal to
+ * @type
+ *
+ * A corresponding six_unlock_type() call will be required for @lock to be fully
+ * unlocked.
  */
 void six_lock_increment(struct six_lock *lock, enum six_lock_type type)
 {
@@ -809,6 +900,16 @@ void six_lock_increment(struct six_lock *lock, enum six_lock_type type)
 }
 EXPORT_SYMBOL_GPL(six_lock_increment);
 
+/**
+ * six_lock_wakeup_all - wake up all waiters on @lock
+ * @lock:      lock to wake up waiters for
+ *
+ * Wakeing up waiters will cause them to re-run should_sleep_fn, which may then
+ * abort the lock operation.
+ *
+ * This function is never needed in a bug-free program; it's only useful in
+ * debug code, e.g. to determine if a cycle detector is at fault.
+ */
 void six_lock_wakeup_all(struct six_lock *lock)
 {
        u64 state = atomic64_read(&lock->state);
@@ -825,8 +926,11 @@ void six_lock_wakeup_all(struct six_lock *lock)
 }
 EXPORT_SYMBOL_GPL(six_lock_wakeup_all);
 
-/*
- * Returns lock held counts, for both read and intent
+/**
+ * six_lock_counts - return held lock counts, for each lock type
+ * @lock:      lock to return counters for
+ *
+ * Return: the number of times a lock is held for read, intent and write.
  */
 struct six_lock_count six_lock_counts(struct six_lock *lock)
 {
@@ -843,15 +947,45 @@ struct six_lock_count six_lock_counts(struct six_lock *lock)
 }
 EXPORT_SYMBOL_GPL(six_lock_counts);
 
+/**
+ * six_lock_readers_add - directly manipulate reader count of a lock
+ * @lock:      lock to add/subtract readers for
+ * @nr:                reader count to add/subtract
+ *
+ * When an upper layer is implementing lock reentrency, we may have both read
+ * and intent locks on the same lock.
+ *
+ * When we need to take a write lock, the read locks will cause self-deadlock,
+ * because six locks themselves do not track which read locks are held by the
+ * current thread and which are held by a different thread - it does no
+ * per-thread tracking of held locks.
+ *
+ * The upper layer that is tracking held locks may however, if trylock() has
+ * failed, count up its own read locks, subtract them, take the write lock, and
+ * then re-add them.
+ *
+ * As in any other situation when taking a write lock, @lock must be held for
+ * intent one (or more) times, so @lock will never be left unlocked.
+ */
 void six_lock_readers_add(struct six_lock *lock, int nr)
 {
-       if (lock->readers)
+       if (lock->readers) {
                this_cpu_add(*lock->readers, nr);
-       else /* reader count starts at bit 0 */
+       } else {
+               EBUG_ON((int) (atomic64_read(&lock->state) & SIX_STATE_READ_LOCK) + nr < 0);
+               /* reader count starts at bit 0 */
                atomic64_add(nr, &lock->state);
+       }
 }
 EXPORT_SYMBOL_GPL(six_lock_readers_add);
 
+/**
+ * six_lock_exit - release resources held by a lock prior to freeing
+ * @lock:      lock to exit
+ *
+ * When a lock was initialized in percpu mode (SIX_OLCK_INIT_PCPU), this is
+ * required to free the percpu read counts.
+ */
 void six_lock_exit(struct six_lock *lock)
 {
        WARN_ON(lock->readers && pcpu_read_count(lock));
index 449589f76628bf44bbeca5800a17448e51c73af8..82bf9de7249075dc3e6a2f507d8ef24d2dff4c2f 100644 (file)
 #ifndef _LINUX_SIX_H
 #define _LINUX_SIX_H
 
-/*
- * Shared/intent/exclusive locks: sleepable read/write locks, much like rw
- * semaphores, except with a third intermediate state, intent. Basic operations
- * are:
+/**
+ * DOC: SIX locks overview
  *
- * six_lock_read(&foo->lock);
- * six_unlock_read(&foo->lock);
+ * Shared/intent/exclusive locks: sleepable read/write locks, like rw semaphores
+ * but with an additional state: read/shared, intent, exclusive/write
  *
- * six_lock_intent(&foo->lock);
- * six_unlock_intent(&foo->lock);
+ * The purpose of the intent state is to allow for greater concurrency on tree
+ * structures without deadlocking. In general, a read can't be upgraded to a
+ * write lock without deadlocking, so an operation that updates multiple nodes
+ * will have to take write locks for the full duration of the operation.
  *
- * six_lock_write(&foo->lock);
- * six_unlock_write(&foo->lock);
+ * But by adding an intent state, which is exclusive with other intent locks but
+ * not with readers, we can take intent locks at thte start of the operation,
+ * and then take write locks only for the actual update to each individual
+ * nodes, without deadlocking.
  *
- * Intent locks block other intent locks, but do not block read locks, and you
- * must have an intent lock held before taking a write lock, like so:
+ * Example usage:
+ *   six_lock_read(&foo->lock);
+ *   six_unlock_read(&foo->lock);
  *
- * six_lock_intent(&foo->lock);
- * six_lock_write(&foo->lock);
- * six_unlock_write(&foo->lock);
- * six_unlock_intent(&foo->lock);
+ * An intent lock must be held before taking a write lock:
+ *   six_lock_intent(&foo->lock);
+ *   six_lock_write(&foo->lock);
+ *   six_unlock_write(&foo->lock);
+ *   six_unlock_intent(&foo->lock);
  *
  * Other operations:
- *
  *   six_trylock_read()
  *   six_trylock_intent()
  *   six_trylock_write()
  *
- *   six_lock_downgrade():     convert from intent to read
- *   six_lock_tryupgrade():    attempt to convert from read to intent
- *
- * Locks also embed a sequence number, which is incremented when the lock is
- * locked or unlocked for write. The current sequence number can be grabbed
- * while a lock is held from lock->state.seq; then, if you drop the lock you can
- * use six_relock_(read|intent_write)(lock, seq) to attempt to retake the lock
- * iff it hasn't been locked for write in the meantime.
- *
- * There are also operations that take the lock type as a parameter, where the
- * type is one of SIX_LOCK_read, SIX_LOCK_intent, or SIX_LOCK_write:
- *
- *   six_lock_type(lock, type)
- *   six_unlock_type(lock, type)
- *   six_relock(lock, type, seq)
- *   six_trylock_type(lock, type)
- *   six_trylock_convert(lock, from, to)
- *
- * A lock may be held multiple times by the same thread (for read or intent,
- * not write). However, the six locks code does _not_ implement the actual
- * recursive checks itself though - rather, if your code (e.g. btree iterator
- * code) knows that the current thread already has a lock held, and for the
- * correct type, six_lock_increment() may be used to bump up the counter for
- * that type - the only effect is that one more call to unlock will be required
- * before the lock is unlocked.
+ *   six_lock_downgrade()      convert from intent to read
+ *   six_lock_tryupgrade()     attempt to convert from read to intent, may fail
+ *
+ * There are also interfaces that take the lock type as an enum:
+ *
+ *   six_lock_type(&foo->lock, SIX_LOCK_read);
+ *   six_trylock_convert(&foo->lock, SIX_LOCK_read, SIX_LOCK_intent)
+ *   six_lock_type(&foo->lock, SIX_LOCK_write);
+ *   six_unlock_type(&foo->lock, SIX_LOCK_write);
+ *   six_unlock_type(&foo->lock, SIX_LOCK_intent);
+ *
+ * Lock sequence numbers - unlock(), relock():
+ *
+ *   Locks embed sequences numbers, which are incremented on write lock/unlock.
+ *   This allows locks to be dropped and the retaken iff the state they protect
+ *   hasn't changed; this makes it much easier to avoid holding locks while e.g.
+ *   doing IO or allocating memory.
+ *
+ *   Example usage:
+ *     six_lock_read(&foo->lock);
+ *     u32 seq = six_lock_seq(&foo->lock);
+ *     six_unlock_read(&foo->lock);
+ *
+ *     some_operation_that_may_block();
+ *
+ *     if (six_relock_read(&foo->lock, seq)) { ... }
+ *
+ *   If the relock operation succeeds, it is as if the lock was never unlocked.
+ *
+ * Reentrancy:
+ *
+ *   Six locks are not by themselves reentrent, but have counters for both the
+ *   read and intent states that can be used to provide reentrency by an upper
+ *   layer that tracks held locks. If a lock is known to already be held in the
+ *   read or intent state, six_lock_increment() can be used to bump the "lock
+ *   held in this state" counter, increasing the number of unlock calls that
+ *   will be required to fully unlock it.
+ *
+ *   Example usage:
+ *     six_lock_read(&foo->lock);
+ *     six_lock_increment(&foo->lock, SIX_LOCK_read);
+ *     six_unlock_read(&foo->lock);
+ *     six_unlock_read(&foo->lock);
+ *   foo->lock is now fully unlocked.
+ *
+ *   Since the intent state supercedes read, it's legal to increment the read
+ *   counter when holding an intent lock, but not the reverse.
+ *
+ *   A lock may only be held once for write: six_lock_increment(.., SIX_LOCK_write)
+ *   is not legal.
+ *
+ * should_sleep_fn:
+ *
+ *   There is a six_lock() variant that takes a function pointer that is called
+ *   immediately prior to schedule() when blocking, and may return an error to
+ *   abort.
+ *
+ *   One possible use for this feature is when objects being locked are part of
+ *   a cache and may reused, and lock ordering is based on a property of the
+ *   object that will change when the object is reused - i.e. logical key order.
+ *
+ *   If looking up an object in the cache may race with object reuse, and lock
+ *   ordering is required to prevent deadlock, object reuse may change the
+ *   correct lock order for that object and cause a deadlock. should_sleep_fn
+ *   can be used to check if the object is still the object we want and avoid
+ *   this deadlock.
+ *
+ * Wait list entry interface:
+ *
+ *   There is a six_lock() variant, six_lock_waiter(), that takes a pointer to a
+ *   wait list entry. By embedding six_lock_waiter into another object, and by
+ *   traversing lock waitlists, it is then possible for an upper layer to
+ *   implement full cycle detection for deadlock avoidance.
+ *
+ *   should_sleep_fn should be used for invoking the cycle detector, walking the
+ *   graph of held locks to check for a deadlock. The upper layer must track
+ *   held locks for each thread, and each thread's held locks must be reachable
+ *   from its six_lock_waiter object.
+ *
+ *   six_lock_waiter() will add the wait object to the waitlist re-trying taking
+ *   the lock, and before calling should_sleep_fn, and the wait object will not
+ *   be removed from the waitlist until either the lock has been successfully
+ *   acquired, or we aborted because should_sleep_fn returned an error.
+ *
+ *   Also, six_lock_waiter contains a timestamp, and waiters on a waitlist will
+ *   have timestamps in strictly ascending order - this is so the timestamp can
+ *   be used as a cursor for lock graph traverse.
  */
 
 #include <linux/lockdep.h>
 #include <linux/osq_lock.h>
 #endif
 
-#define SIX_LOCK_SEPARATE_LOCKFNS
-
 enum six_lock_type {
        SIX_LOCK_read,
        SIX_LOCK_intent,
@@ -108,6 +171,11 @@ enum six_lock_init_flags {
 void __six_lock_init(struct six_lock *lock, const char *name,
                     struct lock_class_key *key, enum six_lock_init_flags flags);
 
+/**
+ * six_lock_init - initialize a six lock
+ * @lock:      lock to initialize
+ * @flags:     optional flags, i.e. SIX_LOCK_INIT_PCPU
+ */
 #define six_lock_init(lock, flags)                                     \
 do {                                                                   \
        static struct lock_class_key __key;                             \
@@ -115,73 +183,148 @@ do {                                                                     \
        __six_lock_init((lock), #lock, &__key, flags);                  \
 } while (0)
 
+/**
+ * six_lock_seq - obtain current lock sequence number
+ * @lock:      six_lock to obtain sequence number for
+ *
+ * @lock should be held for read or intent, and not write
+ *
+ * By saving the lock sequence number, we can unlock @lock and then (typically
+ * after some blocking operation) attempt to relock it: the relock will succeed
+ * if the sequence number hasn't changed, meaning no write locks have been taken
+ * and state corresponding to what @lock protects is still valid.
+ */
 static inline u32 six_lock_seq(const struct six_lock *lock)
 {
        return atomic64_read(&lock->state) >> 32;
 }
 
-bool six_trylock_ip_type(struct six_lock *lock, enum six_lock_type type,
-                        unsigned long ip);
+bool six_trylock_ip(struct six_lock *lock, enum six_lock_type type, unsigned long ip);
 
+/**
+ * six_trylock_type - attempt to take a six lock without blocking
+ * @lock:      lock to take
+ * @type:      SIX_LOCK_read, SIX_LOCK_intent, or SIX_LOCK_write
+ *
+ * Return: true on success, false on failure.
+ */
 static inline bool six_trylock_type(struct six_lock *lock, enum six_lock_type type)
 {
-       return six_trylock_ip_type(lock, type, _THIS_IP_);
+       return six_trylock_ip(lock, type, _THIS_IP_);
 }
 
-int six_lock_type_ip_waiter(struct six_lock *lock, enum six_lock_type type,
-                           struct six_lock_waiter *wait,
-                           six_lock_should_sleep_fn should_sleep_fn, void *p,
-                           unsigned long ip);
-
-static inline int six_lock_type_waiter(struct six_lock *lock, enum six_lock_type type,
-                               struct six_lock_waiter *wait,
-                               six_lock_should_sleep_fn should_sleep_fn, void *p)
+int six_lock_ip_waiter(struct six_lock *lock, enum six_lock_type type,
+                      struct six_lock_waiter *wait,
+                      six_lock_should_sleep_fn should_sleep_fn, void *p,
+                      unsigned long ip);
+
+/**
+ * six_lock_waiter - take a lock, with full waitlist interface
+ * @lock:      lock to take
+ * @type:      SIX_LOCK_read, SIX_LOCK_intent, or SIX_LOCK_write
+ * @wait:      pointer to wait object, which will be added to lock's waitlist
+ * @should_sleep_fn: callback run after adding to waitlist, immediately prior
+ *             to scheduling
+ * @p:         passed through to @should_sleep_fn
+ *
+ * This is a convenience wrapper around six_lock_ip_waiter(), see that function
+ * for full documentation.
+ *
+ * Return: 0 on success, or the return code from @should_sleep_fn on failure.
+ */
+static inline int six_lock_waiter(struct six_lock *lock, enum six_lock_type type,
+                                 struct six_lock_waiter *wait,
+                                 six_lock_should_sleep_fn should_sleep_fn, void *p)
 {
-       return six_lock_type_ip_waiter(lock, type, wait, should_sleep_fn, p, _THIS_IP_);
+       return six_lock_ip_waiter(lock, type, wait, should_sleep_fn, p, _THIS_IP_);
 }
 
-static inline int six_lock_ip_type(struct six_lock *lock, enum six_lock_type type,
-                               six_lock_should_sleep_fn should_sleep_fn, void *p,
-                               unsigned long ip)
+/**
+ * six_lock_ip - take a six lock lock
+ * @lock:      lock to take
+ * @type:      SIX_LOCK_read, SIX_LOCK_intent, or SIX_LOCK_write
+ * @should_sleep_fn: callback run after adding to waitlist, immediately prior
+ *             to scheduling
+ * @p:         passed through to @should_sleep_fn
+ * @ip:                ip parameter for lockdep/lockstat, i.e. _THIS_IP_
+ *
+ * Return: 0 on success, or the return code from @should_sleep_fn on failure.
+ */
+static inline int six_lock_ip(struct six_lock *lock, enum six_lock_type type,
+                             six_lock_should_sleep_fn should_sleep_fn, void *p,
+                             unsigned long ip)
 {
        struct six_lock_waiter wait;
 
-       return six_lock_type_ip_waiter(lock, type, &wait, should_sleep_fn, p, ip);
+       return six_lock_ip_waiter(lock, type, &wait, should_sleep_fn, p, ip);
 }
 
+/**
+ * six_lock_type - take a six lock lock
+ * @lock:      lock to take
+ * @type:      SIX_LOCK_read, SIX_LOCK_intent, or SIX_LOCK_write
+ * @should_sleep_fn: callback run after adding to waitlist, immediately prior
+ *             to scheduling
+ * @p:         passed through to @should_sleep_fn
+ *
+ * Return: 0 on success, or the return code from @should_sleep_fn on failure.
+ */
 static inline int six_lock_type(struct six_lock *lock, enum six_lock_type type,
                                six_lock_should_sleep_fn should_sleep_fn, void *p)
 {
        struct six_lock_waiter wait;
 
-       return six_lock_type_ip_waiter(lock, type, &wait, should_sleep_fn, p, _THIS_IP_);
+       return six_lock_ip_waiter(lock, type, &wait, should_sleep_fn, p, _THIS_IP_);
 }
 
-bool six_relock_ip_type(struct six_lock *lock, enum six_lock_type type,
-                       unsigned seq, unsigned long ip);
+bool six_relock_ip(struct six_lock *lock, enum six_lock_type type,
+                  unsigned seq, unsigned long ip);
 
+/**
+ * six_relock_type - attempt to re-take a lock that was held previously
+ * @lock:      lock to take
+ * @type:      SIX_LOCK_read, SIX_LOCK_intent, or SIX_LOCK_write
+ * @seq:       lock sequence number obtained from six_lock_seq() while lock was
+ *             held previously
+ *
+ * Return: true on success, false on failure.
+ */
 static inline bool six_relock_type(struct six_lock *lock, enum six_lock_type type,
                                   unsigned seq)
 {
-       return six_relock_ip_type(lock, type, seq, _THIS_IP_);
+       return six_relock_ip(lock, type, seq, _THIS_IP_);
 }
 
-void six_unlock_ip_type(struct six_lock *lock, enum six_lock_type type, unsigned long ip);
+void six_unlock_ip(struct six_lock *lock, enum six_lock_type type, unsigned long ip);
 
+/**
+ * six_unlock_type - drop a six lock
+ * @lock:      lock to unlock
+ * @type:      SIX_LOCK_read, SIX_LOCK_intent, or SIX_LOCK_write
+ *
+ * When a lock is held multiple times (because six_lock_incement()) was used),
+ * this decrements the 'lock held' counter by one.
+ *
+ * For example:
+ * six_lock_read(&foo->lock);                          read count 1
+ * six_lock_increment(&foo->lock, SIX_LOCK_read);      read count 2
+ * six_lock_unlock(&foo->lock, SIX_LOCK_read);         read count 1
+ * six_lock_unlock(&foo->lock, SIX_LOCK_read);         read count 0
+ */
 static inline void six_unlock_type(struct six_lock *lock, enum six_lock_type type)
 {
-       six_unlock_ip_type(lock, type, _THIS_IP_);
+       six_unlock_ip(lock, type, _THIS_IP_);
 }
 
 #define __SIX_LOCK(type)                                               \
 static inline bool six_trylock_ip_##type(struct six_lock *lock, unsigned long ip)\
 {                                                                      \
-       return six_trylock_ip_type(lock, SIX_LOCK_##type, ip);          \
+       return six_trylock_ip(lock, SIX_LOCK_##type, ip);               \
 }                                                                      \
                                                                        \
 static inline bool six_trylock_##type(struct six_lock *lock)           \
 {                                                                      \
-       return six_trylock_ip_type(lock, SIX_LOCK_##type, _THIS_IP_);   \
+       return six_trylock_ip(lock, SIX_LOCK_##type, _THIS_IP_);        \
 }                                                                      \
                                                                        \
 static inline int six_lock_ip_waiter_##type(struct six_lock *lock,     \
@@ -189,24 +332,24 @@ static inline int six_lock_ip_waiter_##type(struct six_lock *lock,        \
                           six_lock_should_sleep_fn should_sleep_fn, void *p,\
                           unsigned long ip)                            \
 {                                                                      \
-       return six_lock_type_ip_waiter(lock, SIX_LOCK_##type, wait, should_sleep_fn, p, ip);\
+       return six_lock_ip_waiter(lock, SIX_LOCK_##type, wait, should_sleep_fn, p, ip);\
 }                                                                      \
                                                                        \
 static inline int six_lock_ip_##type(struct six_lock *lock,            \
                    six_lock_should_sleep_fn should_sleep_fn, void *p,  \
                    unsigned long ip)                                   \
 {                                                                      \
-       return six_lock_ip_type(lock, SIX_LOCK_##type, should_sleep_fn, p, ip);\
+       return six_lock_ip(lock, SIX_LOCK_##type, should_sleep_fn, p, ip);\
 }                                                                      \
                                                                        \
 static inline bool six_relock_ip_##type(struct six_lock *lock, u32 seq, unsigned long ip)\
 {                                                                      \
-       return six_relock_ip_type(lock, SIX_LOCK_##type, seq, ip);      \
+       return six_relock_ip(lock, SIX_LOCK_##type, seq, ip);           \
 }                                                                      \
                                                                        \
 static inline bool six_relock_##type(struct six_lock *lock, u32 seq)   \
 {                                                                      \
-       return six_relock_ip_type(lock, SIX_LOCK_##type, seq, _THIS_IP_);\
+       return six_relock_ip(lock, SIX_LOCK_##type, seq, _THIS_IP_);    \
 }                                                                      \
                                                                        \
 static inline int six_lock_##type(struct six_lock *lock,               \
@@ -215,21 +358,14 @@ static inline int six_lock_##type(struct six_lock *lock,          \
        return six_lock_ip_##type(lock, fn, p, _THIS_IP_);              \
 }                                                                      \
                                                                        \
-static inline int six_lock_waiter_##type(struct six_lock *lock,                \
-                       struct six_lock_waiter *wait,                   \
-                       six_lock_should_sleep_fn fn, void *p)           \
-{                                                                      \
-       return six_lock_ip_waiter_##type(lock, wait, fn, p, _THIS_IP_); \
-}                                                                      \
-                                                                       \
 static inline void six_unlock_ip_##type(struct six_lock *lock, unsigned long ip)       \
 {                                                                      \
-       six_unlock_ip_type(lock, SIX_LOCK_##type, ip);                  \
+       six_unlock_ip(lock, SIX_LOCK_##type, ip);                       \
 }                                                                      \
                                                                        \
 static inline void six_unlock_##type(struct six_lock *lock)            \
 {                                                                      \
-       six_unlock_ip_type(lock, SIX_LOCK_##type, _THIS_IP_);           \
+       six_unlock_ip(lock, SIX_LOCK_##type, _THIS_IP_);                \
 }
 
 __SIX_LOCK(read)