€•ÏFŒ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/core-api/cleanup”Œ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/core-api/cleanup”Œmodname”NŒ classname”NŒ refexplicit”ˆuh1hhh ubh)”}”(hhh]”hŒItalian”…””}”hhFsbah}”(h]”h ]”h"]”h$]”h&]”Œ refdomain”h)Œreftype”h+Œ reftarget”Œ$/translations/it_IT/core-api/cleanup”Œmodname”NŒ classname”NŒ refexplicit”ˆuh1hhh ubh)”}”(hhh]”hŒJapanese”…””}”hhZsbah}”(h]”h ]”h"]”h$]”h&]”Œ refdomain”h)Œreftype”h+Œ reftarget”Œ$/translations/ja_JP/core-api/cleanup”Œmodname”NŒ classname”NŒ refexplicit”ˆuh1hhh ubh)”}”(hhh]”hŒKorean”…””}”hhnsbah}”(h]”h ]”h"]”h$]”h&]”Œ refdomain”h)Œreftype”h+Œ reftarget”Œ$/translations/ko_KR/core-api/cleanup”Œ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/core-api/cleanup”Œ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/core-api/cleanup”Œ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³Œ>/var/lib/git/docbuild/linux/Documentation/core-api/cleanup.rst”h´KubhŒsection”“”)”}”(hhh]”(hŒtitle”“”)”}”(hŒScope-based Cleanup Helpers”h]”hŒScope-based Cleanup Helpers”…””}”(hhÏh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hÍhhÊh²hh³hÇh´KubhŒ paragraph”“”)”}”(hXxThe "goto error" pattern is notorious for introducing subtle resource leaks. It is tedious and error prone to add new resource acquisition constraints into code paths that already have several unwind conditions. The "cleanup" helpers enable the compiler to help with this tedium and can aid in maintaining LIFO (last in first out) unwind ordering to avoid unintentional leaks.”h]”hX€The “goto error†pattern is notorious for introducing subtle resource leaks. It is tedious and error prone to add new resource acquisition constraints into code paths that already have several unwind conditions. The “cleanup†helpers enable the compiler to help with this tedium and can aid in maintaining LIFO (last in first out) unwind ordering to avoid unintentional leaks.”…””}”(hhßh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hÝh³ŒW/var/lib/git/docbuild/linux/Documentation/core-api/cleanup:7: ./include/linux/cleanup.h”h´K hhÊh²hubhÞ)”}”(hX!As drivers make up the majority of the kernel code base, here is an example of using these helpers to clean up PCI drivers. The target of the cleanups are occasions where a goto is used to unwind a device reference (pci_dev_put()), or unlock the device (pci_dev_unlock()) before returning.”h]”hX!As drivers make up the majority of the kernel code base, here is an example of using these helpers to clean up PCI drivers. The target of the cleanups are occasions where a goto is used to unwind a device reference (pci_dev_put()), or unlock the device (pci_dev_unlock()) before returning.”…””}”(hhîh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hÝh³ŒW/var/lib/git/docbuild/linux/Documentation/core-api/cleanup:7: ./include/linux/cleanup.h”h´KhhÊh²hubhÞ)”}”(hŒ|The DEFINE_FREE() macro can arrange for PCI device references to be dropped when the associated variable goes out of scope::”h]”hŒ{The DEFINE_FREE() macro can arrange for PCI device references to be dropped when the associated variable goes out of scope:”…””}”(hhýh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hÝh³ŒW/var/lib/git/docbuild/linux/Documentation/core-api/cleanup:7: ./include/linux/cleanup.h”h´KhhÊh²hubhŒ literal_block”“”)”}”(hŒ DEFINE_FREE(pci_dev_put, struct pci_dev *, if (_T) pci_dev_put(_T)) ... struct pci_dev *dev __free(pci_dev_put) = pci_get_slot(parent, PCI_DEVFN(0, 0));”h]”hŒ DEFINE_FREE(pci_dev_put, struct pci_dev *, if (_T) pci_dev_put(_T)) ... struct pci_dev *dev __free(pci_dev_put) = pci_get_slot(parent, PCI_DEVFN(0, 0));”…””}”hjsbah}”(h]”h ]”h"]”h$]”h&]”hÅhÆuh1j h³ŒW/var/lib/git/docbuild/linux/Documentation/core-api/cleanup:7: ./include/linux/cleanup.h”h´KhhÊh²hubhÞ)”}”(hŒüThe above will automatically call pci_dev_put() if **dev** is non-NULL when **dev** goes out of scope (automatic variable scope). If a function wants to invoke pci_dev_put() on error, but return **dev** (i.e. without freeing it) on success, it can do::”h]”(hŒ3The above will automatically call pci_dev_put() if ”…””}”(hjh²hh³Nh´NubhŒstrong”“”)”}”(hŒ**dev**”h]”hŒdev”…””}”(hj'h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1j%hjubhŒ is non-NULL when ”…””}”(hjh²hh³Nh´Nubj&)”}”(hŒ**dev**”h]”hŒdev”…””}”(hj9h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1j%hjubhŒp goes out of scope (automatic variable scope). If a function wants to invoke pci_dev_put() on error, but return ”…””}”(hjh²hh³Nh´Nubj&)”}”(hŒ**dev**”h]”hŒdev”…””}”(hjKh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1j%hjubhŒ1 (i.e. without freeing it) on success, it can do:”…””}”(hjh²hh³Nh´Nubeh}”(h]”h ]”h"]”h$]”h&]”uh1hÝh³ŒW/var/lib/git/docbuild/linux/Documentation/core-api/cleanup:7: ./include/linux/cleanup.h”h´KhhÊh²hubj )”}”(hŒreturn no_free_ptr(dev);”h]”hŒreturn no_free_ptr(dev);”…””}”hjdsbah}”(h]”h ]”h"]”h$]”h&]”hÅhÆuh1j h³ŒW/var/lib/git/docbuild/linux/Documentation/core-api/cleanup:7: ./include/linux/cleanup.h”h´K$hhÊh²hubhÞ)”}”(hŒ...or::”h]”hŒ...or:”…””}”(hjsh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hÝh³ŒW/var/lib/git/docbuild/linux/Documentation/core-api/cleanup:7: ./include/linux/cleanup.h”h´K&hhÊh²hubj )”}”(hŒreturn_ptr(dev);”h]”hŒreturn_ptr(dev);”…””}”hj‚sbah}”(h]”h ]”h"]”h$]”h&]”hÅhÆuh1j h³ŒW/var/lib/git/docbuild/linux/Documentation/core-api/cleanup:7: ./include/linux/cleanup.h”h´K(hhÊh²hubhÞ)”}”(hŒyThe DEFINE_GUARD() macro can arrange for the PCI device lock to be dropped when the scope where guard() is invoked ends::”h]”hŒxThe DEFINE_GUARD() macro can arrange for the PCI device lock to be dropped when the scope where guard() is invoked ends:”…””}”(hj‘h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hÝh³ŒW/var/lib/git/docbuild/linux/Documentation/core-api/cleanup:7: ./include/linux/cleanup.h”h´K*hhÊh²hubj )”}”(hŒfDEFINE_GUARD(pci_dev, struct pci_dev *, pci_dev_lock(_T), pci_dev_unlock(_T)) ... guard(pci_dev)(dev);”h]”hŒfDEFINE_GUARD(pci_dev, struct pci_dev *, pci_dev_lock(_T), pci_dev_unlock(_T)) ... guard(pci_dev)(dev);”…””}”hj sbah}”(h]”h ]”h"]”h$]”h&]”hÅhÆuh1j h³ŒW/var/lib/git/docbuild/linux/Documentation/core-api/cleanup:7: ./include/linux/cleanup.h”h´K-hhÊh²hubhÞ)”}”(hŒ‰The lifetime of the lock obtained by the guard() helper follows the scope of automatic variable declaration. Take the following example::”h]”hŒˆThe lifetime of the lock obtained by the guard() helper follows the scope of automatic variable declaration. Take the following example:”…””}”(hj¯h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hÝh³ŒW/var/lib/git/docbuild/linux/Documentation/core-api/cleanup:7: ./include/linux/cleanup.h”h´K1hhÊh²hubj )”}”(hŒÄfunc(...) { if (...) { ... guard(pci_dev)(dev); // pci_dev_lock() invoked here ... } // <- implied pci_dev_unlock() triggered here }”h]”hŒÄfunc(...) { if (...) { ... guard(pci_dev)(dev); // pci_dev_lock() invoked here ... } // <- implied pci_dev_unlock() triggered here }”…””}”hj¾sbah}”(h]”h ]”h"]”h$]”h&]”hÅhÆuh1j h³ŒW/var/lib/git/docbuild/linux/Documentation/core-api/cleanup:7: ./include/linux/cleanup.h”h´K4hhÊh²hubhÞ)”}”(hŒ^Observe the lock is held for the remainder of the "if ()" block not the remainder of "func()".”h]”hŒfObserve the lock is held for the remainder of the “if ()†block not the remainder of “func()â€.”…””}”(hjÍh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hÝh³ŒW/var/lib/git/docbuild/linux/Documentation/core-api/cleanup:7: ./include/linux/cleanup.h”h´K=hhÊh²hubhÞ)”}”(hŒsThe ACQUIRE() macro can be used in all places that guard() can be used and additionally support conditional locks::”h]”hŒrThe ACQUIRE() macro can be used in all places that guard() can be used and additionally support conditional locks:”…””}”(hjÜh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hÝh³ŒW/var/lib/git/docbuild/linux/Documentation/core-api/cleanup:7: ./include/linux/cleanup.h”h´K@hhÊh²hubj )”}”(hŒ¬DEFINE_GUARD_COND(pci_dev, _try, pci_dev_trylock(_T)) ... ACQUIRE(pci_dev_try, lock)(dev); rc = ACQUIRE_ERR(pci_dev_try, &lock); if (rc) return rc; // @lock is held”h]”hŒ¬DEFINE_GUARD_COND(pci_dev, _try, pci_dev_trylock(_T)) ... ACQUIRE(pci_dev_try, lock)(dev); rc = ACQUIRE_ERR(pci_dev_try, &lock); if (rc) return rc; // @lock is held”…””}”hjësbah}”(h]”h ]”h"]”h$]”h&]”hÅhÆuh1j h³ŒW/var/lib/git/docbuild/linux/Documentation/core-api/cleanup:7: ./include/linux/cleanup.h”h´KChhÊh²hubhÞ)”}”(hŒ®Now, when a function uses both __free() and guard()/ACQUIRE(), or multiple instances of __free(), the LIFO order of variable definition order matters. GCC documentation says:”h]”hŒ®Now, when a function uses both __free() and guard()/ACQUIRE(), or multiple instances of __free(), the LIFO order of variable definition order matters. GCC documentation says:”…””}”(hjúh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hÝh³ŒW/var/lib/git/docbuild/linux/Documentation/core-api/cleanup:7: ./include/linux/cleanup.h”h´KKhhÊh²hubhÞ)”}”(hŒÄ"When multiple variables in the same scope have cleanup attributes, at exit from the scope their associated cleanup functions are run in reverse order of definition (last defined, first cleanup)."”h]”hŒÈ“When multiple variables in the same scope have cleanup attributes, at exit from the scope their associated cleanup functions are run in reverse order of definition (last defined, first cleanup).—…””}”(hj h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hÝh³ŒW/var/lib/git/docbuild/linux/Documentation/core-api/cleanup:7: ./include/linux/cleanup.h”h´KOhhÊh²hubhÞ)”}”(hŒ¿When the unwind order matters it requires that variables be defined mid-function scope rather than at the top of the file. Take the following example and notice the bug highlighted by "!!"::”h]”hŒÂWhen the unwind order matters it requires that variables be defined mid-function scope rather than at the top of the file. Take the following example and notice the bug highlighted by “!!â€:”…””}”(hjh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hÝh³ŒW/var/lib/git/docbuild/linux/Documentation/core-api/cleanup:7: ./include/linux/cleanup.h”h´KShhÊh²hubj )”}”(hX»LIST_HEAD(list); DEFINE_MUTEX(lock); struct object { struct list_head node; }; static struct object *alloc_add(void) { struct object *obj; lockdep_assert_held(&lock); obj = kzalloc(sizeof(*obj), GFP_KERNEL); if (obj) { LIST_HEAD_INIT(&obj->node); list_add(obj->node, &list): } return obj; } static void remove_free(struct object *obj) { lockdep_assert_held(&lock); list_del(&obj->node); kfree(obj); } DEFINE_FREE(remove_free, struct object *, if (_T) remove_free(_T)) static int init(void) { struct object *obj __free(remove_free) = NULL; int err; guard(mutex)(&lock); obj = alloc_add(); if (!obj) return -ENOMEM; err = other_init(obj); if (err) return err; // remove_free() called without the lock!! no_free_ptr(obj); return 0; }”h]”hX»LIST_HEAD(list); DEFINE_MUTEX(lock); struct object { struct list_head node; }; static struct object *alloc_add(void) { struct object *obj; lockdep_assert_held(&lock); obj = kzalloc(sizeof(*obj), GFP_KERNEL); if (obj) { LIST_HEAD_INIT(&obj->node); list_add(obj->node, &list): } return obj; } static void remove_free(struct object *obj) { lockdep_assert_held(&lock); list_del(&obj->node); kfree(obj); } DEFINE_FREE(remove_free, struct object *, if (_T) remove_free(_T)) static int init(void) { struct object *obj __free(remove_free) = NULL; int err; guard(mutex)(&lock); obj = alloc_add(); if (!obj) return -ENOMEM; err = other_init(obj); if (err) return err; // remove_free() called without the lock!! no_free_ptr(obj); return 0; }”…””}”hj'sbah}”(h]”h ]”h"]”h$]”h&]”hÅhÆuh1j h³ŒW/var/lib/git/docbuild/linux/Documentation/core-api/cleanup:7: ./include/linux/cleanup.h”h´KWhhÊh²hubhÞ)”}”(hŒdThat bug is fixed by changing init() to call guard() and define + initialize **obj** in this order::”h]”(hŒMThat bug is fixed by changing init() to call guard() and define + initialize ”…””}”(hj6h²hh³Nh´Nubj&)”}”(hŒ**obj**”h]”hŒobj”…””}”(hj>h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1j%hj6ubhŒ in this order:”…””}”(hj6h²hh³Nh´Nubeh}”(h]”h ]”h"]”h$]”h&]”uh1hÝh³ŒW/var/lib/git/docbuild/linux/Documentation/core-api/cleanup:7: ./include/linux/cleanup.h”h´K†hhÊh²hubj )”}”(hŒJguard(mutex)(&lock); struct object *obj __free(remove_free) = alloc_add();”h]”hŒJguard(mutex)(&lock); struct object *obj __free(remove_free) = alloc_add();”…””}”hjWsbah}”(h]”h ]”h"]”h$]”h&]”hÅhÆuh1j h³ŒW/var/lib/git/docbuild/linux/Documentation/core-api/cleanup:7: ./include/linux/cleanup.h”h´K‰hhÊh²hubhÞ)”}”(hX+Given that the "__free(...) = NULL" pattern for variables defined at the top of the function poses this potential interdependency problem the recommendation is to always define and assign variables in one statement and not group variable definitions at the top of the function when __free() is used.”h]”hX/Given that the “__free(...) = NULL†pattern for variables defined at the top of the function poses this potential interdependency problem the recommendation is to always define and assign variables in one statement and not group variable definitions at the top of the function when __free() is used.”…””}”(hjfh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hÝh³ŒW/var/lib/git/docbuild/linux/Documentation/core-api/cleanup:7: ./include/linux/cleanup.h”h´KŒhhÊh²hubhÞ)”}”(hX[Lastly, given that the benefit of cleanup helpers is removal of "goto", and that the "goto" statement can jump between scopes, the expectation is that usage of "goto" and cleanup helpers is never mixed in the same function. I.e. for a given routine, convert all resources that need a "goto" cleanup to scope-based cleanup, or convert none of them.”h]”hXkLastly, given that the benefit of cleanup helpers is removal of “gotoâ€, and that the “goto†statement can jump between scopes, the expectation is that usage of “goto†and cleanup helpers is never mixed in the same function. I.e. for a given routine, convert all resources that need a “goto†cleanup to scope-based cleanup, or convert none of them.”…””}”(hjuh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hÝh³ŒW/var/lib/git/docbuild/linux/Documentation/core-api/cleanup:7: ./include/linux/cleanup.h”h´K’hhÊh²hubeh}”(h]”Œscope-based-cleanup-helpers”ah ]”h"]”Œscope-based cleanup helpers”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.