€•ŠKŒ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/RCU/rcuref”Œ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/RCU/rcuref”Œmodname”NŒ classname”NŒ refexplicit”ˆuh1hhh ubh)”}”(hhh]”hŒItalian”…””}”hhFsbah}”(h]”h ]”h"]”h$]”h&]”Œ refdomain”h)Œreftype”h+Œ reftarget”Œ/translations/it_IT/RCU/rcuref”Œmodname”NŒ classname”NŒ refexplicit”ˆuh1hhh ubh)”}”(hhh]”hŒJapanese”…””}”hhZsbah}”(h]”h ]”h"]”h$]”h&]”Œ refdomain”h)Œreftype”h+Œ reftarget”Œ/translations/ja_JP/RCU/rcuref”Œmodname”NŒ classname”NŒ refexplicit”ˆuh1hhh ubh)”}”(hhh]”hŒKorean”…””}”hhnsbah}”(h]”h ]”h"]”h$]”h&]”Œ refdomain”h)Œreftype”h+Œ reftarget”Œ/translations/ko_KR/RCU/rcuref”Œ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/RCU/rcuref”Œmodname”NŒ classname”NŒ refexplicit”ˆuh1hhh ubeh}”(h]”h ]”h"]”h$]”h&]”Œcurrent_language”ŒEnglish”uh1h hhŒ _document”hŒsource”NŒline”NubhŒcomment”“”)”}”(hŒ SPDX-License-Identifier: GPL-2.0”h]”hŒ SPDX-License-Identifier: GPL-2.0”…””}”hh£sbah}”(h]”h ]”h"]”h$]”h&]”Œ xml:space”Œpreserve”uh1h¡hhhžhhŸŒ8/var/lib/git/docbuild/linux/Documentation/RCU/rcuref.rst”h KubhŒsection”“”)”}”(hhh]”(hŒtitle”“”)”}”(hŒDReference-count design for elements of lists/arrays protected by RCU”h]”hŒDReference-count design for elements of lists/arrays protected by RCU”…””}”(hh»hžhhŸNh Nubah}”(h]”h ]”h"]”h$]”h&]”uh1h¹hh¶hžhhŸh³h KubhŒ paragraph”“”)”}”(hXPlease note that the percpu-ref feature is likely your first stop if you need to combine reference counts and RCU. Please see include/linux/percpu-refcount.h for more information. However, in those unusual cases where percpu-ref would consume too much memory, please read on.”h]”hXPlease note that the percpu-ref feature is likely your first stop if you need to combine reference counts and RCU. Please see include/linux/percpu-refcount.h for more information. However, in those unusual cases where percpu-ref would consume too much memory, please read on.”…””}”(hhËhžhhŸNh Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hÉhŸh³h Khh¶hžhubhŒ transition”“”)”}”(hŒH------------------------------------------------------------------------”h]”h}”(h]”h ]”h"]”h$]”h&]”uh1hÙhŸh³h Khh¶hžhubhÊ)”}”(hŒ…Reference counting on elements of lists which are protected by traditional reader/writer spinlocks or semaphores are straightforward:”h]”hŒ…Reference counting on elements of lists which are protected by traditional reader/writer spinlocks or semaphores are straightforward:”…””}”(hhåhžhhŸNh Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hÉhŸh³h Khh¶hžhubhÊ)”}”(hŒCODE LISTING A::”h]”hŒCODE LISTING A:”…””}”(hhóhžhhŸNh Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hÉhŸh³h Khh¶hžhubhŒ literal_block”“”)”}”(hXÉ1. 2. add() search_and_reference() { { alloc_object read_lock(&list_lock); ... search_for_element atomic_set(&el->rc, 1); atomic_inc(&el->rc); write_lock(&list_lock); ... add_element read_unlock(&list_lock); ... ... write_unlock(&list_lock); } } 3. 4. release_referenced() delete() { { ... write_lock(&list_lock); if(atomic_dec_and_test(&el->rc)) ... kfree(el); ... remove_element } write_unlock(&list_lock); ... if (atomic_dec_and_test(&el->rc)) kfree(el); ... }”h]”hXÉ1. 2. add() search_and_reference() { { alloc_object read_lock(&list_lock); ... search_for_element atomic_set(&el->rc, 1); atomic_inc(&el->rc); write_lock(&list_lock); ... add_element read_unlock(&list_lock); ... ... write_unlock(&list_lock); } } 3. 4. release_referenced() delete() { { ... write_lock(&list_lock); if(atomic_dec_and_test(&el->rc)) ... kfree(el); ... remove_element } write_unlock(&list_lock); ... if (atomic_dec_and_test(&el->rc)) kfree(el); ... }”…””}”hjsbah}”(h]”h ]”h"]”h$]”h&]”h±h²uh1jhŸh³h Khh¶hžhubhÊ)”}”(hX€If this list/array is made lock free using RCU as in changing the write_lock() in add() and delete() to spin_lock() and changing read_lock() in search_and_reference() to rcu_read_lock(), the atomic_inc() in search_and_reference() could potentially hold reference to an element which has already been deleted from the list/array. Use atomic_inc_not_zero() in this scenario as follows:”h]”hX€If this list/array is made lock free using RCU as in changing the write_lock() in add() and delete() to spin_lock() and changing read_lock() in search_and_reference() to rcu_read_lock(), the atomic_inc() in search_and_reference() could potentially hold reference to an element which has already been deleted from the list/array. Use atomic_inc_not_zero() in this scenario as follows:”…””}”(hjhžhhŸNh Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hÉhŸh³h K/hh¶hžhubhÊ)”}”(hŒCODE LISTING B::”h]”hŒCODE LISTING B:”…””}”(hjhžhhŸNh Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hÉhŸh³h K6hh¶hžhubj)”}”(hXF1. 2. add() search_and_reference() { { alloc_object rcu_read_lock(); ... search_for_element atomic_set(&el->rc, 1); if (!atomic_inc_not_zero(&el->rc)) { spin_lock(&list_lock); rcu_read_unlock(); return FAIL; add_element } ... ... spin_unlock(&list_lock); rcu_read_unlock(); } } 3. 4. release_referenced() delete() { { ... spin_lock(&list_lock); if (atomic_dec_and_test(&el->rc)) ... call_rcu(&el->head, el_free); remove_element ... spin_unlock(&list_lock); } ... if (atomic_dec_and_test(&el->rc)) call_rcu(&el->head, el_free); ... }”h]”hXF1. 2. add() search_and_reference() { { alloc_object rcu_read_lock(); ... search_for_element atomic_set(&el->rc, 1); if (!atomic_inc_not_zero(&el->rc)) { spin_lock(&list_lock); rcu_read_unlock(); return FAIL; add_element } ... ... spin_unlock(&list_lock); rcu_read_unlock(); } } 3. 4. release_referenced() delete() { { ... spin_lock(&list_lock); if (atomic_dec_and_test(&el->rc)) ... call_rcu(&el->head, el_free); remove_element ... spin_unlock(&list_lock); } ... if (atomic_dec_and_test(&el->rc)) call_rcu(&el->head, el_free); ... }”…””}”hj-sbah}”(h]”h ]”h"]”h$]”h&]”h±h²uh1jhŸh³h K8hh¶hžhubhÊ)”}”(hŒìSometimes, a reference to the element needs to be obtained in the update (write) stream. In such cases, atomic_inc_not_zero() might be overkill, since we hold the update-side spinlock. One might instead use atomic_inc() in such cases.”h]”hŒìSometimes, a reference to the element needs to be obtained in the update (write) stream. In such cases, atomic_inc_not_zero() might be overkill, since we hold the update-side spinlock. One might instead use atomic_inc() in such cases.”…””}”(hj;hžhhŸNh Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hÉhŸh³h KQhh¶hžhubhÊ)”}”(hŒ¶It is not always convenient to deal with "FAIL" in the search_and_reference() code path. In such cases, the atomic_dec_and_test() may be moved from delete() to el_free() as follows:”h]”hŒºIt is not always convenient to deal with “FAIL†in the search_and_reference() code path. In such cases, the atomic_dec_and_test() may be moved from delete() to el_free() as follows:”…””}”(hjIhžhhŸNh Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hÉhŸh³h KVhh¶hžhubhÊ)”}”(hŒCODE LISTING C::”h]”hŒCODE LISTING C:”…””}”(hjWhžhhŸNh Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hÉhŸh³h K[hh¶hžhubj)”}”(hXx1. 2. add() search_and_reference() { { alloc_object rcu_read_lock(); ... search_for_element atomic_set(&el->rc, 1); atomic_inc(&el->rc); spin_lock(&list_lock); ... add_element rcu_read_unlock(); ... } spin_unlock(&list_lock); 4. } delete() 3. { release_referenced() spin_lock(&list_lock); { ... ... remove_element if (atomic_dec_and_test(&el->rc)) spin_unlock(&list_lock); kfree(el); ... ... call_rcu(&el->head, el_free); } ... 5. } void el_free(struct rcu_head *rhp) { release_referenced(); }”h]”hXx1. 2. add() search_and_reference() { { alloc_object rcu_read_lock(); ... search_for_element atomic_set(&el->rc, 1); atomic_inc(&el->rc); spin_lock(&list_lock); ... add_element rcu_read_unlock(); ... } spin_unlock(&list_lock); 4. } delete() 3. { release_referenced() spin_lock(&list_lock); { ... ... remove_element if (atomic_dec_and_test(&el->rc)) spin_unlock(&list_lock); kfree(el); ... ... call_rcu(&el->head, el_free); } ... 5. } void el_free(struct rcu_head *rhp) { release_referenced(); }”…””}”hjesbah}”(h]”h ]”h"]”h$]”h&]”h±h²uh1jhŸh³h K]hh¶hžhubhÊ)”}”(hX0The key point is that the initial reference added by add() is not removed until after a grace period has elapsed following removal. This means that search_and_reference() cannot find this element, which means that the value of el->rc cannot increase. Thus, once it reaches zero, there are no readers that can or ever will be able to reference the element. The element can therefore safely be freed. This in turn guarantees that if any reader finds the element, that reader may safely acquire a reference without checking the value of the reference counter.”h]”hX0The key point is that the initial reference added by add() is not removed until after a grace period has elapsed following removal. This means that search_and_reference() cannot find this element, which means that the value of el->rc cannot increase. Thus, once it reaches zero, there are no readers that can or ever will be able to reference the element. The element can therefore safely be freed. This in turn guarantees that if any reader finds the element, that reader may safely acquire a reference without checking the value of the reference counter.”…””}”(hjshžhhŸNh Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hÉhŸh³h Kwhh¶hžhubhÊ)”}”(hX¤A clear advantage of the RCU-based pattern in listing C over the one in listing B is that any call to search_and_reference() that locates a given object will succeed in obtaining a reference to that object, even given a concurrent invocation of delete() for that same object. Similarly, a clear advantage of both listings B and C over listing A is that a call to delete() is not delayed even if there are an arbitrarily large number of calls to search_and_reference() searching for the same object that delete() was invoked on. Instead, all that is delayed is the eventual invocation of kfree(), which is usually not a problem on modern computer systems, even the small ones.”h]”hX¤A clear advantage of the RCU-based pattern in listing C over the one in listing B is that any call to search_and_reference() that locates a given object will succeed in obtaining a reference to that object, even given a concurrent invocation of delete() for that same object. Similarly, a clear advantage of both listings B and C over listing A is that a call to delete() is not delayed even if there are an arbitrarily large number of calls to search_and_reference() searching for the same object that delete() was invoked on. Instead, all that is delayed is the eventual invocation of kfree(), which is usually not a problem on modern computer systems, even the small ones.”…””}”(hjhžhhŸNh Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hÉhŸh³h K€hh¶hžhubhÊ)”}”(hŒŒIn cases where delete() can sleep, synchronize_rcu() can be called from delete(), so that el_free() can be subsumed into delete as follows::”h]”hŒ‹In cases where delete() can sleep, synchronize_rcu() can be called from delete(), so that el_free() can be subsumed into delete as follows:”…””}”(hjhžhhŸNh Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hÉhŸh³h K‹hh¶hžhubj)”}”(hŒÂ4. delete() { spin_lock(&list_lock); ... remove_element spin_unlock(&list_lock); ... synchronize_rcu(); if (atomic_dec_and_test(&el->rc)) kfree(el); ... }”h]”hŒÂ4. delete() { spin_lock(&list_lock); ... remove_element spin_unlock(&list_lock); ... synchronize_rcu(); if (atomic_dec_and_test(&el->rc)) kfree(el); ... }”…””}”hjsbah}”(h]”h ]”h"]”h$]”h&]”h±h²uh1jhŸh³h KŽhh¶hžhubhÊ)”}”(hŒ§As additional examples in the kernel, the pattern in listing C is used by reference counting of struct pid, while the pattern in listing B is used by struct posix_acl.”h]”hŒ§As additional examples in the kernel, the pattern in listing C is used by reference counting of struct pid, while the pattern in listing B is used by struct posix_acl.”…””}”(hj«hžhhŸNh Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hÉhŸh³h Kœhh¶hžhubeh}”(h]”ŒDreference-count-design-for-elements-of-lists-arrays-protected-by-rcu”ah ]”h"]”ŒDreference-count design for elements of lists/arrays protected by rcu”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”}”j¾j»sŒ nametypes”}”j¾‰sh}”j»h¶sŒ 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.