€•¥ÎŒsphinx.addnodes”Œdocument”“”)”}”(Œ rawsource”Œ”Œchildren”]”(Œ translations”Œ LanguagesNode”“”)”}”(hhh]”(hŒ pending_xref”“”)”}”(hhh]”Œdocutils.nodes”ŒText”“”ŒChinese (Simplified)”…””}”Œparent”hsbaŒ attributes”}”(Œids”]”Œclasses”]”Œnames”]”Œdupnames”]”Œbackrefs”]”Œ refdomain”Œstd”Œreftype”Œdoc”Œ reftarget”Œ+/translations/zh_CN/locking/ww-mutex-design”Œmodname”NŒ classname”NŒ refexplicit”ˆuŒtagname”hhh ubh)”}”(hhh]”hŒChinese (Traditional)”…””}”hh2sbah}”(h]”h ]”h"]”h$]”h&]”Œ refdomain”h)Œreftype”h+Œ reftarget”Œ+/translations/zh_TW/locking/ww-mutex-design”Œmodname”NŒ classname”NŒ refexplicit”ˆuh1hhh ubh)”}”(hhh]”hŒItalian”…””}”hhFsbah}”(h]”h ]”h"]”h$]”h&]”Œ refdomain”h)Œreftype”h+Œ reftarget”Œ+/translations/it_IT/locking/ww-mutex-design”Œmodname”NŒ classname”NŒ refexplicit”ˆuh1hhh ubh)”}”(hhh]”hŒJapanese”…””}”hhZsbah}”(h]”h ]”h"]”h$]”h&]”Œ refdomain”h)Œreftype”h+Œ reftarget”Œ+/translations/ja_JP/locking/ww-mutex-design”Œmodname”NŒ classname”NŒ refexplicit”ˆuh1hhh ubh)”}”(hhh]”hŒKorean”…””}”hhnsbah}”(h]”h ]”h"]”h$]”h&]”Œ refdomain”h)Œreftype”h+Œ reftarget”Œ+/translations/ko_KR/locking/ww-mutex-design”Œmodname”NŒ classname”NŒ refexplicit”ˆuh1hhh ubh)”}”(hhh]”hŒPortuguese (Brazilian)”…””}”hh‚sbah}”(h]”h ]”h"]”h$]”h&]”Œ refdomain”h)Œreftype”h+Œ reftarget”Œ+/translations/pt_BR/locking/ww-mutex-design”Œmodname”NŒ classname”NŒ refexplicit”ˆuh1hhh ubh)”}”(hhh]”hŒSpanish”…””}”hh–sbah}”(h]”h ]”h"]”h$]”h&]”Œ refdomain”h)Œreftype”h+Œ reftarget”Œ+/translations/sp_SP/locking/ww-mutex-design”Œmodname”NŒ classname”NŒ refexplicit”ˆuh1hhh ubeh}”(h]”h ]”h"]”h$]”h&]”Œcurrent_language”ŒEnglish”uh1h hhŒ _document”hŒsource”NŒline”NubhŒsection”“”)”}”(hhh]”(hŒtitle”“”)”}”(hŒ&Wound/Wait Deadlock-Proof Mutex Design”h]”hŒ&Wound/Wait Deadlock-Proof Mutex Design”…””}”(hh¼h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hºhh·h²hh³ŒE/var/lib/git/docbuild/linux/Documentation/locking/ww-mutex-design.rst”h´KubhŒ paragraph”“”)”}”(hŒLPlease read mutex-design.rst first, as it applies to wait/wound mutexes too.”h]”hŒLPlease read mutex-design.rst first, as it applies to wait/wound mutexes too.”…””}”(hhÍh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´Khh·h²hubh¶)”}”(hhh]”(h»)”}”(hŒMotivation for WW-Mutexes”h]”hŒMotivation for WW-Mutexes”…””}”(hhÞh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hºhhÛh²hh³hÊh´KubhÌ)”}”(hXOGPU's do operations that commonly involve many buffers. Those buffers can be shared across contexts/processes, exist in different memory domains (for example VRAM vs system memory), and so on. And with PRIME / dmabuf, they can even be shared across devices. So there are a handful of situations where the driver needs to wait for buffers to become ready. If you think about this in terms of waiting on a buffer mutex for it to become available, this presents a problem because there is no way to guarantee that buffers appear in a execbuf/batch in the same order in all contexts. That is directly under control of userspace, and a result of the sequence of GL calls that an application makes. Which results in the potential for deadlock. The problem gets more complex when you consider that the kernel may need to migrate the buffer(s) into VRAM before the GPU operates on the buffer(s), which may in turn require evicting some other buffers (and you don't want to evict other buffers which are already queued up to the GPU), but for a simplified understanding of the problem you can ignore this.”h]”hXSGPU’s do operations that commonly involve many buffers. Those buffers can be shared across contexts/processes, exist in different memory domains (for example VRAM vs system memory), and so on. And with PRIME / dmabuf, they can even be shared across devices. So there are a handful of situations where the driver needs to wait for buffers to become ready. If you think about this in terms of waiting on a buffer mutex for it to become available, this presents a problem because there is no way to guarantee that buffers appear in a execbuf/batch in the same order in all contexts. That is directly under control of userspace, and a result of the sequence of GL calls that an application makes. Which results in the potential for deadlock. The problem gets more complex when you consider that the kernel may need to migrate the buffer(s) into VRAM before the GPU operates on the buffer(s), which may in turn require evicting some other buffers (and you don’t want to evict other buffers which are already queued up to the GPU), but for a simplified understanding of the problem you can ignore this.”…””}”(hhìh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´K hhÛh²hubhÌ)”}”(hX The algorithm that the TTM graphics subsystem came up with for dealing with this problem is quite simple. For each group of buffers (execbuf) that need to be locked, the caller would be assigned a unique reservation id/ticket, from a global counter. In case of deadlock while locking all the buffers associated with a execbuf, the one with the lowest reservation ticket (i.e. the oldest task) wins, and the one with the higher reservation id (i.e. the younger task) unlocks all of the buffers that it has already locked, and then tries again.”h]”hX The algorithm that the TTM graphics subsystem came up with for dealing with this problem is quite simple. For each group of buffers (execbuf) that need to be locked, the caller would be assigned a unique reservation id/ticket, from a global counter. In case of deadlock while locking all the buffers associated with a execbuf, the one with the lowest reservation ticket (i.e. the oldest task) wins, and the one with the higher reservation id (i.e. the younger task) unlocks all of the buffers that it has already locked, and then tries again.”…””}”(hhúh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´KhhÛh²hubhÌ)”}”(hX=In the RDBMS literature, a reservation ticket is associated with a transaction. and the deadlock handling approach is called Wait-Die. The name is based on the actions of a locking thread when it encounters an already locked mutex. If the transaction holding the lock is younger, the locking transaction waits. If the transaction holding the lock is older, the locking transaction backs off and dies. Hence Wait-Die. There is also another algorithm called Wound-Wait: If the transaction holding the lock is younger, the locking transaction wounds the transaction holding the lock, requesting it to die. If the transaction holding the lock is older, it waits for the other transaction. Hence Wound-Wait. The two algorithms are both fair in that a transaction will eventually succeed. However, the Wound-Wait algorithm is typically stated to generate fewer backoffs compared to Wait-Die, but is, on the other hand, associated with more work than Wait-Die when recovering from a backoff. Wound-Wait is also a preemptive algorithm in that transactions are wounded by other transactions, and that requires a reliable way to pick up the wounded condition and preempt the running transaction. Note that this is not the same as process preemption. A Wound-Wait transaction is considered preempted when it dies (returning -EDEADLK) following a wound.”h]”hX=In the RDBMS literature, a reservation ticket is associated with a transaction. and the deadlock handling approach is called Wait-Die. The name is based on the actions of a locking thread when it encounters an already locked mutex. If the transaction holding the lock is younger, the locking transaction waits. If the transaction holding the lock is older, the locking transaction backs off and dies. Hence Wait-Die. There is also another algorithm called Wound-Wait: If the transaction holding the lock is younger, the locking transaction wounds the transaction holding the lock, requesting it to die. If the transaction holding the lock is older, it waits for the other transaction. Hence Wound-Wait. The two algorithms are both fair in that a transaction will eventually succeed. However, the Wound-Wait algorithm is typically stated to generate fewer backoffs compared to Wait-Die, but is, on the other hand, associated with more work than Wait-Die when recovering from a backoff. Wound-Wait is also a preemptive algorithm in that transactions are wounded by other transactions, and that requires a reliable way to pick up the wounded condition and preempt the running transaction. Note that this is not the same as process preemption. A Wound-Wait transaction is considered preempted when it dies (returning -EDEADLK) following a wound.”…””}”(hjh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´K$hhÛh²hubeh}”(h]”Œmotivation-for-ww-mutexes”ah ]”h"]”Œmotivation for ww-mutexes”ah$]”h&]”uh1hµhh·h²hh³hÊh´Kubh¶)”}”(hhh]”(h»)”}”(hŒConcepts”h]”hŒConcepts”…””}”(hj!h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hºhjh²hh³hÊh´K:ubhÌ)”}”(hŒiCompared to normal mutexes two additional concepts/objects show up in the lock interface for w/w mutexes:”h]”hŒiCompared to normal mutexes two additional concepts/objects show up in the lock interface for w/w mutexes:”…””}”(hj/h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´Kbuffers that's not allowed to be reordered. This is useful if a list of required objects is already tracked somewhere. Furthermore the lock helper can use propagate the -EALREADY return code back to the caller as a signal that an object is twice on the list. This is useful if the list is constructed from userspace input and the ABI requires userspace to not have duplicate entries (e.g. for a gpu commandbuffer submission ioctl)::”h]”hXÔMethod 1, using a list in execbuf->buffers that’s not allowed to be reordered. This is useful if a list of required objects is already tracked somewhere. Furthermore the lock helper can use propagate the -EALREADY return code back to the caller as a signal that an object is twice on the list. This is useful if the list is constructed from userspace input and the ABI requires userspace to not have duplicate entries (e.g. for a gpu commandbuffer submission ioctl):”…””}”(hjih²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´K†hj*h²hubjX)”}”(hX`int lock_objs(struct list_head *list, struct ww_acquire_ctx *ctx) { struct obj *res_obj = NULL; struct obj_entry *contended_entry = NULL; struct obj_entry *entry; ww_acquire_init(ctx, &ww_class); retry: list_for_each_entry (entry, list, head) { if (entry->obj == res_obj) { res_obj = NULL; continue; } ret = ww_mutex_lock(&entry->obj->lock, ctx); if (ret < 0) { contended_entry = entry; goto err; } } ww_acquire_done(ctx); return 0; err: list_for_each_entry_continue_reverse (entry, list, head) ww_mutex_unlock(&entry->obj->lock); if (res_obj) ww_mutex_unlock(&res_obj->lock); if (ret == -EDEADLK) { /* we lost out in a seqno race, lock and retry.. */ ww_mutex_lock_slow(&contended_entry->obj->lock, ctx); res_obj = contended_entry->obj; goto retry; } ww_acquire_fini(ctx); return ret; }”h]”hX`int lock_objs(struct list_head *list, struct ww_acquire_ctx *ctx) { struct obj *res_obj = NULL; struct obj_entry *contended_entry = NULL; struct obj_entry *entry; ww_acquire_init(ctx, &ww_class); retry: list_for_each_entry (entry, list, head) { if (entry->obj == res_obj) { res_obj = NULL; continue; } ret = ww_mutex_lock(&entry->obj->lock, ctx); if (ret < 0) { contended_entry = entry; goto err; } } ww_acquire_done(ctx); return 0; err: list_for_each_entry_continue_reverse (entry, list, head) ww_mutex_unlock(&entry->obj->lock); if (res_obj) ww_mutex_unlock(&res_obj->lock); if (ret == -EDEADLK) { /* we lost out in a seqno race, lock and retry.. */ ww_mutex_lock_slow(&contended_entry->obj->lock, ctx); res_obj = contended_entry->obj; goto retry; } ww_acquire_fini(ctx); return ret; }”…””}”hjwsbah}”(h]”h ]”h"]”h$]”h&]”jgjhuh1jWh³hÊh´Khj*h²hubhÌ)”}”(hŒÏMethod 2, using a list in execbuf->buffers that can be reordered. Same semantics of duplicate entry detection using -EALREADY as method 1 above. But the list-reordering allows for a bit more idiomatic code::”h]”hŒÎMethod 2, using a list in execbuf->buffers that can be reordered. Same semantics of duplicate entry detection using -EALREADY as method 1 above. But the list-reordering allows for a bit more idiomatic code:”…””}”(hj…h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´K·hj*h²hubjX)”}”(hXint lock_objs(struct list_head *list, struct ww_acquire_ctx *ctx) { struct obj_entry *entry, *entry2; ww_acquire_init(ctx, &ww_class); list_for_each_entry (entry, list, head) { ret = ww_mutex_lock(&entry->obj->lock, ctx); if (ret < 0) { entry2 = entry; list_for_each_entry_continue_reverse (entry2, list, head) ww_mutex_unlock(&entry2->obj->lock); if (ret != -EDEADLK) { ww_acquire_fini(ctx); return ret; } /* we lost out in a seqno race, lock and retry.. */ ww_mutex_lock_slow(&entry->obj->lock, ctx); /* * Move buf to head of the list, this will point * buf->next to the first unlocked entry, * restarting the for loop. */ list_del(&entry->head); list_add(&entry->head, list); } } ww_acquire_done(ctx); return 0; }”h]”hXint lock_objs(struct list_head *list, struct ww_acquire_ctx *ctx) { struct obj_entry *entry, *entry2; ww_acquire_init(ctx, &ww_class); list_for_each_entry (entry, list, head) { ret = ww_mutex_lock(&entry->obj->lock, ctx); if (ret < 0) { entry2 = entry; list_for_each_entry_continue_reverse (entry2, list, head) ww_mutex_unlock(&entry2->obj->lock); if (ret != -EDEADLK) { ww_acquire_fini(ctx); return ret; } /* we lost out in a seqno race, lock and retry.. */ ww_mutex_lock_slow(&entry->obj->lock, ctx); /* * Move buf to head of the list, this will point * buf->next to the first unlocked entry, * restarting the for loop. */ list_del(&entry->head); list_add(&entry->head, list); } } ww_acquire_done(ctx); return 0; }”…””}”hj“sbah}”(h]”h ]”h"]”h$]”h&]”jgjhuh1jWh³hÊh´K»hj*h²hubhÌ)”}”(hŒ9Unlocking works the same way for both methods #1 and #2::”h]”hŒ8Unlocking works the same way for both methods #1 and #2:”…””}”(hj¡h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´Kßhj*h²hubjX)”}”(hŒåvoid unlock_objs(struct list_head *list, struct ww_acquire_ctx *ctx) { struct obj_entry *entry; list_for_each_entry (entry, list, head) ww_mutex_unlock(&entry->obj->lock); ww_acquire_fini(ctx); }”h]”hŒåvoid unlock_objs(struct list_head *list, struct ww_acquire_ctx *ctx) { struct obj_entry *entry; list_for_each_entry (entry, list, head) ww_mutex_unlock(&entry->obj->lock); ww_acquire_fini(ctx); }”…””}”hj¯sbah}”(h]”h ]”h"]”h$]”h&]”jgjhuh1jWh³hÊh´Káhj*h²hubhÌ)”}”(hX+Method 3 is useful if the list of objects is constructed ad-hoc and not upfront, e.g. when adjusting edges in a graph where each node has its own ww_mutex lock, and edges can only be changed when holding the locks of all involved nodes. w/w mutexes are a natural fit for such a case for two reasons:”h]”hX+Method 3 is useful if the list of objects is constructed ad-hoc and not upfront, e.g. when adjusting edges in a graph where each node has its own ww_mutex lock, and edges can only be changed when holding the locks of all involved nodes. w/w mutexes are a natural fit for such a case for two reasons:”…””}”(hj½h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´Këhj*h²hubjh)”}”(hhh]”(jm)”}”(hŒÊThey can handle lock-acquisition in any order which allows us to start walking a graph from a starting point and then iteratively discovering new edges and locking down the nodes those edges connect to.”h]”hÌ)”}”(hŒÊThey can handle lock-acquisition in any order which allows us to start walking a graph from a starting point and then iteratively discovering new edges and locking down the nodes those edges connect to.”h]”hŒÊThey can handle lock-acquisition in any order which allows us to start walking a graph from a starting point and then iteratively discovering new edges and locking down the nodes those edges connect to.”…””}”(hjÒh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´KðhjÎubah}”(h]”h ]”h"]”h$]”h&]”uh1jlhjËh²hh³hÊh´Nubjm)”}”(hŒþDue to the -EALREADY return code signalling that a given objects is already held there's no need for additional book-keeping to break cycles in the graph or keep track off which looks are already held (when using more than one node as a starting point). ”h]”hÌ)”}”(hŒýDue to the -EALREADY return code signalling that a given objects is already held there's no need for additional book-keeping to break cycles in the graph or keep track off which looks are already held (when using more than one node as a starting point).”h]”hŒÿDue to the -EALREADY return code signalling that a given objects is already held there’s no need for additional book-keeping to break cycles in the graph or keep track off which looks are already held (when using more than one node as a starting point).”…””}”(hjêh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´Kóhjæubah}”(h]”h ]”h"]”h$]”h&]”uh1jlhjËh²hh³hÊh´Nubeh}”(h]”h ]”h"]”h$]”h&]”jßjàuh1jgh³hÊh´Kðhj*h²hubhÌ)”}”(hŒMNote that this approach differs in two important ways from the above methods:”h]”hŒMNote that this approach differs in two important ways from the above methods:”…””}”(hjh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´Køhj*h²hubjh)”}”(hhh]”(jm)”}”(hXSince the list of objects is dynamically constructed (and might very well be different when retrying due to hitting the -EDEADLK die condition) there's no need to keep any object on a persistent list when it's not locked. We can therefore move the list_head into the object itself.”h]”hÌ)”}”(hXSince the list of objects is dynamically constructed (and might very well be different when retrying due to hitting the -EDEADLK die condition) there's no need to keep any object on a persistent list when it's not locked. We can therefore move the list_head into the object itself.”h]”hXSince the list of objects is dynamically constructed (and might very well be different when retrying due to hitting the -EDEADLK die condition) there’s no need to keep any object on a persistent list when it’s not locked. We can therefore move the list_head into the object itself.”…””}”(hjh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´Kúhjubah}”(h]”h ]”h"]”h$]”h&]”uh1jlhjh²hh³hÊh´Nubjm)”}”(hŒvOn the other hand the dynamic object list construction also means that the -EALREADY return code can't be propagated. ”h]”hÌ)”}”(hŒuOn the other hand the dynamic object list construction also means that the -EALREADY return code can't be propagated.”h]”hŒwOn the other hand the dynamic object list construction also means that the -EALREADY return code can’t be propagated.”…””}”(hj1h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´Kþhj-ubah}”(h]”h ]”h"]”h$]”h&]”uh1jlhjh²hh³hÊh´Nubeh}”(h]”h ]”h"]”h$]”h&]”jßjàuh1jgh³hÊh´Kúhj*h²hubhÌ)”}”(hXýNote also that methods #1 and #2 and method #3 can be combined, e.g. to first lock a list of starting nodes (passed in from userspace) using one of the above methods. And then lock any additional objects affected by the operations using method #3 below. The backoff/retry procedure will be a bit more involved, since when the dynamic locking step hits -EDEADLK we also need to unlock all the objects acquired with the fixed list. But the w/w mutex debug checks will catch any interface misuse for these cases.”h]”hXýNote also that methods #1 and #2 and method #3 can be combined, e.g. to first lock a list of starting nodes (passed in from userspace) using one of the above methods. And then lock any additional objects affected by the operations using method #3 below. The backoff/retry procedure will be a bit more involved, since when the dynamic locking step hits -EDEADLK we also need to unlock all the objects acquired with the fixed list. But the w/w mutex debug checks will catch any interface misuse for these cases.”…””}”(hjKh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´Mhj*h²hubhÌ)”}”(hŒÙAlso, method 3 can't fail the lock acquisition step since it doesn't return -EALREADY. Of course this would be different when using the _interruptible variants, but that's outside of the scope of these examples here::”h]”hŒÞAlso, method 3 can’t fail the lock acquisition step since it doesn’t return -EALREADY. Of course this would be different when using the _interruptible variants, but that’s outside of the scope of these examples here:”…””}”(hjYh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´M hj*h²hubjX)”}”(hXstruct obj { struct ww_mutex ww_mutex; struct list_head locked_list; }; static DEFINE_WW_CLASS(ww_class); void __unlock_objs(struct list_head *list) { struct obj *entry, *temp; list_for_each_entry_safe (entry, temp, list, locked_list) { /* need to do that before unlocking, since only the current lock holder is allowed to use object */ list_del(&entry->locked_list); ww_mutex_unlock(entry->ww_mutex) } } void lock_objs(struct list_head *list, struct ww_acquire_ctx *ctx) { struct obj *obj; ww_acquire_init(ctx, &ww_class); retry: /* re-init loop start state */ loop { /* magic code which walks over a graph and decides which objects * to lock */ ret = ww_mutex_lock(obj->ww_mutex, ctx); if (ret == -EALREADY) { /* we have that one already, get to the next object */ continue; } if (ret == -EDEADLK) { __unlock_objs(list); ww_mutex_lock_slow(obj, ctx); list_add(&entry->locked_list, list); goto retry; } /* locked a new object, add it to the list */ list_add_tail(&entry->locked_list, list); } ww_acquire_done(ctx); return 0; } void unlock_objs(struct list_head *list, struct ww_acquire_ctx *ctx) { __unlock_objs(list); ww_acquire_fini(ctx); }”h]”hXstruct obj { struct ww_mutex ww_mutex; struct list_head locked_list; }; static DEFINE_WW_CLASS(ww_class); void __unlock_objs(struct list_head *list) { struct obj *entry, *temp; list_for_each_entry_safe (entry, temp, list, locked_list) { /* need to do that before unlocking, since only the current lock holder is allowed to use object */ list_del(&entry->locked_list); ww_mutex_unlock(entry->ww_mutex) } } void lock_objs(struct list_head *list, struct ww_acquire_ctx *ctx) { struct obj *obj; ww_acquire_init(ctx, &ww_class); retry: /* re-init loop start state */ loop { /* magic code which walks over a graph and decides which objects * to lock */ ret = ww_mutex_lock(obj->ww_mutex, ctx); if (ret == -EALREADY) { /* we have that one already, get to the next object */ continue; } if (ret == -EDEADLK) { __unlock_objs(list); ww_mutex_lock_slow(obj, ctx); list_add(&entry->locked_list, list); goto retry; } /* locked a new object, add it to the list */ list_add_tail(&entry->locked_list, list); } ww_acquire_done(ctx); return 0; } void unlock_objs(struct list_head *list, struct ww_acquire_ctx *ctx) { __unlock_objs(list); ww_acquire_fini(ctx); }”…””}”hjgsbah}”(h]”h ]”h"]”h$]”h&]”jgjhuh1jWh³hÊh´M hj*h²hubhÌ)”}”(hX Method 4: Only lock one single objects. In that case deadlock detection and prevention is obviously overkill, since with grabbing just one lock you can't produce a deadlock within just one class. To simplify this case the w/w mutex api can be used with a NULL context.”h]”hXMethod 4: Only lock one single objects. In that case deadlock detection and prevention is obviously overkill, since with grabbing just one lock you can’t produce a deadlock within just one class. To simplify this case the w/w mutex api can be used with a NULL context.”…””}”(hjuh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´MGhj*h²hubeh}”(h]”Œusage”ah ]”h"]”Œusage”ah$]”h&]”uh1hµhh·h²hh³hÊh´Koubh¶)”}”(hhh]”(h»)”}”(hŒImplementation Details”h]”hŒImplementation Details”…””}”(hjŽh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hºhj‹h²hh³hÊh´MMubh¶)”}”(hhh]”(h»)”}”(hŒDesign:”h]”hŒDesign:”…””}”(hjŸh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hºhjœh²hh³hÊh´MPubhŒ block_quote”“”)”}”(hX™ww_mutex currently encapsulates a struct mutex, this means no extra overhead for normal mutex locks, which are far more common. As such there is only a small increase in code size if wait/wound mutexes are not used. We maintain the following invariants for the wait list: (1) Waiters with an acquire context are sorted by stamp order; waiters without an acquire context are interspersed in FIFO order. (2) For Wait-Die, among waiters with contexts, only the first one can have other locks acquired already (ctx->acquired > 0). Note that this waiter may come after other waiters without contexts in the list. The Wound-Wait preemption is implemented with a lazy-preemption scheme: The wounded status of the transaction is checked only when there is contention for a new lock and hence a true chance of deadlock. In that situation, if the transaction is wounded, it backs off, clears the wounded status and retries. A great benefit of implementing preemption in this way is that the wounded transaction can identify a contending lock to wait for before restarting the transaction. Just blindly restarting the transaction would likely make the transaction end up in a situation where it would have to back off again. In general, not much contention is expected. The locks are typically used to serialize access to resources for devices, and optimization focus should therefore be directed towards the uncontended cases. ”h]”(hÌ)”}”(hŒ×ww_mutex currently encapsulates a struct mutex, this means no extra overhead for normal mutex locks, which are far more common. As such there is only a small increase in code size if wait/wound mutexes are not used.”h]”hŒ×ww_mutex currently encapsulates a struct mutex, this means no extra overhead for normal mutex locks, which are far more common. As such there is only a small increase in code size if wait/wound mutexes are not used.”…””}”(hj³h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´MRhj¯ubhÌ)”}”(hŒ7We maintain the following invariants for the wait list:”h]”hŒ7We maintain the following invariants for the wait list:”…””}”(hjÁh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´MVhj¯ubhŒenumerated_list”“”)”}”(hhh]”(jm)”}”(hŒ}Waiters with an acquire context are sorted by stamp order; waiters without an acquire context are interspersed in FIFO order.”h]”hÌ)”}”(hŒ}Waiters with an acquire context are sorted by stamp order; waiters without an acquire context are interspersed in FIFO order.”h]”hŒ}Waiters with an acquire context are sorted by stamp order; waiters without an acquire context are interspersed in FIFO order.”…””}”(hjØh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´MXhjÔubah}”(h]”h ]”h"]”h$]”h&]”uh1jlhjÑubjm)”}”(hŒÊFor Wait-Die, among waiters with contexts, only the first one can have other locks acquired already (ctx->acquired > 0). Note that this waiter may come after other waiters without contexts in the list. ”h]”hÌ)”}”(hŒÉFor Wait-Die, among waiters with contexts, only the first one can have other locks acquired already (ctx->acquired > 0). Note that this waiter may come after other waiters without contexts in the list.”h]”hŒÉFor Wait-Die, among waiters with contexts, only the first one can have other locks acquired already (ctx->acquired > 0). Note that this waiter may come after other waiters without contexts in the list.”…””}”(hjðh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´MZhjìubah}”(h]”h ]”h"]”h$]”h&]”uh1jlhjÑubeh}”(h]”h ]”h"]”h$]”h&]”Œenumtype”Œarabic”Œprefix”Œ(”Œsuffix”Œ)”uh1jÏhj¯ubhÌ)”}”(hX]The Wound-Wait preemption is implemented with a lazy-preemption scheme: The wounded status of the transaction is checked only when there is contention for a new lock and hence a true chance of deadlock. In that situation, if the transaction is wounded, it backs off, clears the wounded status and retries. A great benefit of implementing preemption in this way is that the wounded transaction can identify a contending lock to wait for before restarting the transaction. Just blindly restarting the transaction would likely make the transaction end up in a situation where it would have to back off again.”h]”hX]The Wound-Wait preemption is implemented with a lazy-preemption scheme: The wounded status of the transaction is checked only when there is contention for a new lock and hence a true chance of deadlock. In that situation, if the transaction is wounded, it backs off, clears the wounded status and retries. A great benefit of implementing preemption in this way is that the wounded transaction can identify a contending lock to wait for before restarting the transaction. Just blindly restarting the transaction would likely make the transaction end up in a situation where it would have to back off again.”…””}”(hjh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´M^hj¯ubhÌ)”}”(hŒÊIn general, not much contention is expected. The locks are typically used to serialize access to resources for devices, and optimization focus should therefore be directed towards the uncontended cases.”h]”hŒÊIn general, not much contention is expected. The locks are typically used to serialize access to resources for devices, and optimization focus should therefore be directed towards the uncontended cases.”…””}”(hjh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´Mhhj¯ubeh}”(h]”h ]”h"]”h$]”h&]”uh1j­h³hÊh´MRhjœh²hubeh}”(h]”Œdesign”ah ]”h"]”Œdesign:”ah$]”h&]”uh1hµhj‹h²hh³hÊh´MPubh¶)”}”(hhh]”(h»)”}”(hŒLockdep:”h]”hŒLockdep:”…””}”(hj=h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hºhj:h²hh³hÊh´Mmubj®)”}”(hXSpecial care has been taken to warn for as many cases of api abuse as possible. Some common api abuses will be caught with CONFIG_DEBUG_MUTEXES, but CONFIG_PROVE_LOCKING is recommended. Some of the errors which will be warned about: - Forgetting to call ww_acquire_fini or ww_acquire_init. - Attempting to lock more mutexes after ww_acquire_done. - Attempting to lock the wrong mutex after -EDEADLK and unlocking all mutexes. - Attempting to lock the right mutex after -EDEADLK, before unlocking all mutexes. - Calling ww_mutex_lock_slow before -EDEADLK was returned. - Unlocking mutexes with the wrong unlock function. - Calling one of the ww_acquire_* twice on the same context. - Using a different ww_class for the mutex than for the ww_acquire_ctx. - Normal lockdep errors that can result in deadlocks. Some of the lockdep errors that can result in deadlocks: - Calling ww_acquire_init to initialize a second ww_acquire_ctx before having called ww_acquire_fini on the first. - 'normal' deadlocks that can occur. ”h]”(hÌ)”}”(hŒ¹Special care has been taken to warn for as many cases of api abuse as possible. Some common api abuses will be caught with CONFIG_DEBUG_MUTEXES, but CONFIG_PROVE_LOCKING is recommended.”h]”hŒ¹Special care has been taken to warn for as many cases of api abuse as possible. Some common api abuses will be caught with CONFIG_DEBUG_MUTEXES, but CONFIG_PROVE_LOCKING is recommended.”…””}”(hjOh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´MohjKubhŒdefinition_list”“”)”}”(hhh]”(hŒdefinition_list_item”“”)”}”(hXsSome of the errors which will be warned about: - Forgetting to call ww_acquire_fini or ww_acquire_init. - Attempting to lock more mutexes after ww_acquire_done. - Attempting to lock the wrong mutex after -EDEADLK and unlocking all mutexes. - Attempting to lock the right mutex after -EDEADLK, before unlocking all mutexes. - Calling ww_mutex_lock_slow before -EDEADLK was returned. - Unlocking mutexes with the wrong unlock function. - Calling one of the ww_acquire_* twice on the same context. - Using a different ww_class for the mutex than for the ww_acquire_ctx. - Normal lockdep errors that can result in deadlocks. ”h]”(hŒterm”“”)”}”(hŒ.Some of the errors which will be warned about:”h]”hŒ.Some of the errors which will be warned about:”…””}”(hjjh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhh³hÊh´M€hjdubhŒ definition”“”)”}”(hhh]”jh)”}”(hhh]”(jm)”}”(hŒ6Forgetting to call ww_acquire_fini or ww_acquire_init.”h]”hÌ)”}”(hj‚h]”hŒ6Forgetting to call ww_acquire_fini or ww_acquire_init.”…””}”(hj„h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´Mthj€ubah}”(h]”h ]”h"]”h$]”h&]”uh1jlhj}ubjm)”}”(hŒ6Attempting to lock more mutexes after ww_acquire_done.”h]”hÌ)”}”(hj™h]”hŒ6Attempting to lock more mutexes after ww_acquire_done.”…””}”(hj›h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´Muhj—ubah}”(h]”h ]”h"]”h$]”h&]”uh1jlhj}ubjm)”}”(hŒLAttempting to lock the wrong mutex after -EDEADLK and unlocking all mutexes.”h]”hÌ)”}”(hŒLAttempting to lock the wrong mutex after -EDEADLK and unlocking all mutexes.”h]”hŒLAttempting to lock the wrong mutex after -EDEADLK and unlocking all mutexes.”…””}”(hj²h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´Mvhj®ubah}”(h]”h ]”h"]”h$]”h&]”uh1jlhj}ubjm)”}”(hŒQAttempting to lock the right mutex after -EDEADLK, before unlocking all mutexes. ”h]”hÌ)”}”(hŒPAttempting to lock the right mutex after -EDEADLK, before unlocking all mutexes.”h]”hŒPAttempting to lock the right mutex after -EDEADLK, before unlocking all mutexes.”…””}”(hjÊh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´MxhjÆubah}”(h]”h ]”h"]”h$]”h&]”uh1jlhj}ubjm)”}”(hŒ9Calling ww_mutex_lock_slow before -EDEADLK was returned. ”h]”hÌ)”}”(hŒ8Calling ww_mutex_lock_slow before -EDEADLK was returned.”h]”hŒ8Calling ww_mutex_lock_slow before -EDEADLK was returned.”…””}”(hjâh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´M{hjÞubah}”(h]”h ]”h"]”h$]”h&]”uh1jlhj}ubjm)”}”(hŒ1Unlocking mutexes with the wrong unlock function.”h]”hÌ)”}”(hjøh]”hŒ1Unlocking mutexes with the wrong unlock function.”…””}”(hjúh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´M}hjöubah}”(h]”h ]”h"]”h$]”h&]”uh1jlhj}ubjm)”}”(hŒ:Calling one of the ww_acquire_* twice on the same context.”h]”hÌ)”}”(hjh]”hŒ:Calling one of the ww_acquire_* twice on the same context.”…””}”(hjh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´M~hj ubah}”(h]”h ]”h"]”h$]”h&]”uh1jlhj}ubjm)”}”(hŒEUsing a different ww_class for the mutex than for the ww_acquire_ctx.”h]”hÌ)”}”(hj&h]”hŒEUsing a different ww_class for the mutex than for the ww_acquire_ctx.”…””}”(hj(h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´Mhj$ubah}”(h]”h ]”h"]”h$]”h&]”uh1jlhj}ubjm)”}”(hŒ4Normal lockdep errors that can result in deadlocks. ”h]”hÌ)”}”(hŒ3Normal lockdep errors that can result in deadlocks.”h]”hŒ3Normal lockdep errors that can result in deadlocks.”…””}”(hj?h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´M€hj;ubah}”(h]”h ]”h"]”h$]”h&]”uh1jlhj}ubeh}”(h]”h ]”h"]”h$]”h&]”jßjàuh1jgh³hÊh´Mthjzubah}”(h]”h ]”h"]”h$]”h&]”uh1jxhjdubeh}”(h]”h ]”h"]”h$]”h&]”uh1jbh³hÊh´M€hj_ubjc)”}”(hŒÓSome of the lockdep errors that can result in deadlocks: - Calling ww_acquire_init to initialize a second ww_acquire_ctx before having called ww_acquire_fini on the first. - 'normal' deadlocks that can occur. ”h]”(ji)”}”(hŒ8Some of the lockdep errors that can result in deadlocks:”h]”hŒ8Some of the lockdep errors that can result in deadlocks:”…””}”(hjih²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhh³hÊh´M…hjeubjy)”}”(hhh]”jh)”}”(hhh]”(jm)”}”(hŒpCalling ww_acquire_init to initialize a second ww_acquire_ctx before having called ww_acquire_fini on the first.”h]”hÌ)”}”(hŒpCalling ww_acquire_init to initialize a second ww_acquire_ctx before having called ww_acquire_fini on the first.”h]”hŒpCalling ww_acquire_init to initialize a second ww_acquire_ctx before having called ww_acquire_fini on the first.”…””}”(hjh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´Mƒhj}ubah}”(h]”h ]”h"]”h$]”h&]”uh1jlhjzubjm)”}”(hŒ#'normal' deadlocks that can occur. ”h]”hÌ)”}”(hŒ"'normal' deadlocks that can occur.”h]”hŒ&‘normal’ deadlocks that can occur.”…””}”(hj™h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´M…hj•ubah}”(h]”h ]”h"]”h$]”h&]”uh1jlhjzubeh}”(h]”h ]”h"]”h$]”h&]”jßjàuh1jgh³hÊh´Mƒhjwubah}”(h]”h ]”h"]”h$]”h&]”uh1jxhjeubeh}”(h]”h ]”h"]”h$]”h&]”uh1jbh³hÊh´M…hj_ubeh}”(h]”h ]”h"]”h$]”h&]”uh1j]hjKubeh}”(h]”h ]”h"]”h$]”h&]”uh1j­h³hÊh´Mohj:h²hubj^)”}”(hhh]”jc)”}”(hŒ\FIXME: Update this section once we have the TASK_DEADLOCK task state flag magic implemented.”h]”(ji)”}”(hŒFIXME:”h]”hŒFIXME:”…””}”(hjÒh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhh³hÊh´MˆhjÎubjy)”}”(hhh]”hÌ)”}”(hŒUUpdate this section once we have the TASK_DEADLOCK task state flag magic implemented.”h]”hŒUUpdate this section once we have the TASK_DEADLOCK task state flag magic implemented.”…””}”(hjãh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´Mˆhjàubah}”(h]”h ]”h"]”h$]”h&]”uh1jxhjÎubeh}”(h]”h ]”h"]”h$]”h&]”uh1jbh³hÊh´MˆhjËubah}”(h]”h ]”h"]”h$]”h&]”uh1j]hj:h²hh³hÊh´Nubeh}”(h]”Œlockdep”ah ]”h"]”Œlockdep:”ah$]”h&]”uh1hµhj‹h²hh³hÊh´Mmubeh}”(h]”Œimplementation-details”ah ]”h"]”Œimplementation details”ah$]”h&]”uh1hµhh·h²hh³hÊh´MMubeh}”(h]”Œ&wound-wait-deadlock-proof-mutex-design”ah ]”h"]”Œ&wound/wait deadlock-proof mutex design”ah$]”h&]”uh1hµhhh²hh³hÊh´Kubeh}”(h]”h ]”h"]”h$]”h&]”Œsource”hÊuh1hŒcurrent_source”NŒ current_line”NŒsettings”Œdocutils.frontend”ŒValues”“”)”}”(hºNŒ generator”NŒ datestamp”NŒ source_link”NŒ source_url”NŒ toc_backlinks”Œentry”Œfootnote_backlinks”KŒ sectnum_xform”KŒstrip_comments”NŒstrip_elements_with_classes”NŒ strip_classes”NŒ report_level”KŒ halt_level”KŒexit_status_level”KŒdebug”NŒwarning_stream”NŒ traceback”ˆŒinput_encoding”Œ utf-8-sig”Œinput_encoding_error_handler”Œstrict”Œoutput_encoding”Œutf-8”Œoutput_encoding_error_handler”j>Œerror_encoding”Œutf-8”Œerror_encoding_error_handler”Œbackslashreplace”Œ language_code”Œen”Œrecord_dependencies”NŒconfig”NŒ id_prefix”hŒauto_id_prefix”Œid”Œ dump_settings”NŒdump_internals”NŒdump_transforms”NŒdump_pseudo_xml”NŒexpose_internals”NŒstrict_visitor”NŒ_disable_config”NŒ_source”hÊŒ _destination”NŒ _config_files”]”Œ7/var/lib/git/docbuild/linux/Documentation/docutils.conf”aŒfile_insertion_enabled”ˆŒ raw_enabled”KŒline_length_limit”M'Œpep_references”NŒ pep_base_url”Œhttps://peps.python.org/”Œpep_file_url_template”Œpep-%04d”Œrfc_references”NŒ rfc_base_url”Œ&https://datatracker.ietf.org/doc/html/”Œ tab_width”KŒtrim_footnote_reference_space”‰Œsyntax_highlight”Œlong”Œ smart_quotes”ˆŒsmartquotes_locales”]”Œcharacter_level_inline_markup”‰Œdoctitle_xform”‰Œ docinfo_xform”KŒsectsubtitle_xform”‰Œ image_loading”Œlink”Œembed_stylesheet”‰Œcloak_email_addresses”ˆŒsection_self_link”‰Œenv”NubŒreporter”NŒindirect_targets”]”Œsubstitution_defs”}”Œsubstitution_names”}”Œrefnames”}”Œrefids”}”Œnameids”}”(jjjjj'j$jˆj…jj j7j4jjuŒ nametypes”}”(j‰j‰j'‰jˆ‰j‰j7‰j‰uh}”(jh·jhÛj$jj…j*j j‹j4jœjj:uŒ footnote_refs”}”Œ citation_refs”}”Œ autofootnotes”]”Œautofootnote_refs”]”Œsymbol_footnotes”]”Œsymbol_footnote_refs”]”Œ footnotes”]”Œ citations”]”Œautofootnote_start”KŒsymbol_footnote_start”KŒ id_counter”Œ collections”ŒCounter”“”}”…”R”Œparse_messages”]”Œtransform_messages”]”Œ transformer”NŒ include_log”]”Œ decoration”Nh²hub.