LICENSES: Add the copyleft-next-0.3.1 license
Subsystems (15)
A message that is not in the archive (sent off-list or not yet received): [email protected]
Luis Chamberlain
maintainerPATCH v7 1/12 LICENSES: Add the copyleft-next-0.3.1 license
Add the full text of the copyleft-next-0.3.1 license to the kernel tree as well as the required tags for reference and tooling. The license text was copied directly from the copyleft-next project's git tree [0].
Discussion of using copyleft-next-0.3.1 on Linux started since June, 2016 [1]. In the end Linus' preference was to have drivers use MODULE_LICENSE("GPL") to make it clear that the GPL applies when it comes to Linux [2]. Additionally, even though copyleft-next-0.3.1 has been found to be to be GPLv2 compatible by three attorneys at SUSE and Redhat [3], to err on the side of caution we simply recommend to always use the "OR" language for this license [4].
Even though it has been a goal of the project to be GPL-v2 compatible to be certain in 2016 I asked for a clarification about what makes copyleft-next GPLv2 compatible and also asked for a summary of benefits. This prompted some small minor changes to make compatibility even further clear and as of copyleft 0.3.1 compatibility should be crystal clear [5].
The summary of why copyleft-next 0.3.1 is compatible with GPLv2 is explained as follows:
Like GPLv2, copyleft-next requires distribution of derivative works ("Derived Works" in copyleft-next 0.3.x) to be under the same license. Ordinarily this would make the two licenses incompatible. However, copyleft-next 0.3.1 says: "If the Derived Work includes material licensed under the GPL, You may instead license the Derived Work under the GPL." "GPL" is defined to include GPLv2.
In practice this means copyleft-next code in Linux may be licensed under the GPL2, however there are additional obvious gains for bringing contributions from Linux outbound where copyleft-next is preferred. A summary of benefits why projects outside of Linux might prefer to use copyleft-next >= 0.3.1 over GPLv2:
o It is much shorter and simpler
o It has an explicit patent license grant, unlike GPLv2
o Its notice preservation conditions are clearer
o More free software/open source licenses are compatible with it (via section 4)
o The source code requirement triggered by binary distribution is much simpler in a procedural sense
o Recipients potentially have a contract claim against distributors who are noncompliant with the source code requirement
o There is a built-in inbound=outbound policy for upstream contributions (cf. Apache License 2.0 section 5)
o There are disincentives to engage in the controversial practice of copyleft/ proprietary dual-licensing
o In 15 years copyleft expires, which can be advantageous for legacy code
o There are explicit disincentives to bringing patent infringement claims accusing the licensed work of infringement (see 10b)
o There is a cure period for licensees who are not compliant with the license (there is no cure opportunity in GPLv2)
o copyleft-next has a 'built-in or-later' provision
The first driver submission to Linux under this dual strategy was lib/test_sysctl.c through commit 9308f2f9e7f05 ("test_sysctl: add dedicated proc sysctl test driver") merged in July 2017. Shortly after that I also added test_kmod through commit d9c6a72d6fa29 ("kmod: add test driver to stress test the module loader") in the same month. These two drivers went in just a few months before the SPDX license practice kicked in. In 2018 Kuno Woudt went through the process to get SPDX identifiers for copyleft-next [6] [7]. Although there are SPDX tags for copyleft-next-0.3.0, we only document use in Linux starting from copyleft-next-0.3.1 which makes GPLv2 compatibility crystal clear.
This patch will let us update the two Linux selftest drivers in subsequent patches with their respective SPDX license identifiers and let us remove repetitive license boiler plate.
[0] https://github.com/copyleft-next/copyleft-next/blob/master/Releases/copyleft-next-0.3.1
[1] https://lore.kernel.org/lkml/[email protected]/
[2] https://lore.kernel.org/lkml/CA+55aFyhxcvD+q7tp+-yrSFDKfR0mOHgyEAe=f_94aKLsOu0Og@mail.gmail.com/
[3] https://lore.kernel.org/lkml/[email protected]/
[4] https://lkml.kernel.org/r/[email protected]
[5] https://lists.fedorahosted.org/archives/list/[email protected]/thread/JTGV56DDADWGKU7ZKTZA4DLXTGTLNJ57/#SQMDIKBRAVDOCT4UVNOOCRGBN2UJIKHZ
[6] https://spdx.org/licenses/copyleft-next-0.3.0.html
[7] https://spdx.org/licenses/copyleft-next-0.3.1.html
--- LICENSES/dual/copyleft-next-0.3.1 | 237 ++++++++++++++++++++++++++++++ 1 file changed, 237 insertions(+) create mode 100644 LICENSES/dual/copyleft-next-0.3.1
LICENSES/dual/copyleft-next-0.3.1 new file +237 -0
@@ -0,0 +1,237 @@ +Valid-License-Identifier: copyleft-next-0.3.1 +SPDX-URL: https://spdx.org/licenses/copyleft-next-0.3.1 +Usage-Guide: + This license can be used in code, it has been found to be GPLv2 compatible + by attorneys at Redhat and SUSE, however to air on the side of caution, + it's best to only use it together with a GPL2 compatible license using "OR". + To use the copyleft-next-0.3.1 license put the following SPDX tag/value + pair into a comment according to the placement guidelines in the + licensing rules documentation: + SPDX-License-Identifier: GPL-2.0 OR copyleft-next-0.3.1 + SPDX-License-Identifier: GPL-2.0-only OR copyleft-next 0.3.1 + SPDX-License-Identifier: GPL-2.0+ OR copyleft-next-0.3.1 + SPDX-License-Identifier: GPL-2.0-or-later OR copyleft-next-0.3.1 +License-Text: + +======================================================================= + + copyleft-next 0.3.1 ("this License") + Release date: 2016-04-29 + +1. License Grants; No Trademark License + + Subject to the terms of this License, I grant You: + + a) A non-exclusive, worldwide, perpetual, royalty-free, irrevocable + copyright license, to reproduce, Distribute, prepare derivative works + of, publicly perform and publicly display My Work. + + b) A non-exclusive, worldwide, perpetual, royalty-free, irrevocable + patent license under Licensed Patents to make, have made, use, sell, + offer for sale, and import Covered Works. + + This License does not grant any rights in My name, trademarks, service + marks, or logos. + +2. Distribution: General Conditions + + You may Distribute Covered Works, provided that You (i) inform + recipients how they can obtain a copy of this License; (ii) satisfy the + applicable conditions of sections 3 through 6; and (iii) preserve all + Legal Notices contained in My Work (to the extent they remain + pertinent). "Legal Notices" means copyright notices, license notices, + license texts, and author attributions, but does not include logos, + other graphical images, trademarks or trademark legends. + +3. Conditions for Distributing Derived Works; Outbound GPL Compatibility + + If You Distribute a Derived Work, You must license the entire Derived + Work as a whole under this License, with prominent notice of such + licensing. This condition may not be avoided through such means as + separate Distribution of portions of the Derived Work. + + If the Derived Work includes material licensed under the GPL, You may + instead license the Derived Work under the GPL. + +4. Condition Against Further Restrictions; Inbound License Compatibility + + When Distributing a Covered Work, You may not impose further + restrictions on the exercise of rights in the Covered Work granted under + this License. This condition is not excused merely because such + restrictions result from Your compliance with conditions or obligations + extrinsic to this License (such as a court order or an agreement with a + third party). + + However, You may Distribute a Covered Work incorporating material + governed by a license that is both OSI-Approved and FSF-Free as of the + release date of this License, provided that compliance with such + other license would not conflict with any conditions stated in other + sections of this License. + +5. Conditions for Distributing Object Code + + You may Distribute an Object Code form of a Covered Work, provided that + you accompany the Object Code with a URL through which the Corresponding + Source is made available, at no charge, by some standard or customary + means of providing network access to source code. + + If you Distribute the Object Code in a physical product or tangible + storage medium ("Product"), the Corresponding Source must be available + through such URL for two years from the date of Your most recent + Distribution of the Object Code in the Product. However, if the Product + itself contains or is accompanied by the Corresponding Source (made + available in a customarily accessible manner), You need not also comply + with the first paragraph of this section. + + Each direct and indirect recipient of the Covered Work from You is an + intended third-party beneficiary of this License solely as to this + section 5, with the right to enforce its terms. + +6. Symmetrical Licensing Condition for Upstream Contributions + + If You Distribute a work to Me specifically for inclusion in or + modification of a Covered Work (a "Patch"), and no explicit licensing + terms apply to the Patch, You license the Patch under this License, to + the extent of Your copyright in the Patch. This condition does not + negate the other conditions of this License, if applicable to the Patch. + +7. Nullification of Copyleft/Proprietary Dual Licensing + + If I offer to license, for a fee, a Covered Work under terms other than + a license that is OSI-Approved or FSF-Free as of the release date of this + License or a numbered version of copyleft-next released by the + Copyleft-Next Project, then the license I grant You under section 1 is no + longer subject to the conditions in sections 3 through 5. + +8. Copyleft Sunset + + The conditions in sections 3 through 5 no longer apply once fifteen + years have elapsed from the date of My first Distribution of My Work + under this License. + +9. Pass-Through + + When You Distribute a Covered Work, the recipient automatically receives + a license to My Work from Me, subject to the terms of this License. + +10. Termination + + Your license grants under section 1 are automatically terminated if You + + a) fail to comply with the conditions of this License, unless You cure + such noncompliance within thirty days after becoming aware of it, or + + b) initiate a patent infringement litigation claim (excluding + declaratory judgment actions, counterclaims, and cross-claims) + alleging that any part of My Work directly or indirectly infringes + any patent. + + Termination of Your license grants extends to all copies of Covered + Works You subsequently obtain. Termination does not terminate the + rights of those who have received copies or rights from You subject to + this License. + + To the extent permission to make copies of a Covered Work is necessary + merely for running it, such permission is not terminable. + +11. Later License Versions + + The Copyleft-Next Project may release new versions of copyleft-next, + designated by a distinguishing version number ("Later Versions"). + Unless I explicitly remove the option of Distributing Covered Works + under Later Versions, You may Distribute Covered Works under any Later + Version. + +** 12. No Warranty ** +** ** +** My Work is provided "as-is", without warranty. You bear the risk ** +** of using it. To the extent permitted by applicable law, each ** +** Distributor of My Work excludes the implied warranties of title, ** +** merchantability, fitness for a particular purpose and ** +** non-infringement. ** + +** 13. Limitation of Liability ** +** ** +** To the extent permitted by applicable law, in no event will any ** +** Distributor of My Work be liable to You for any damages ** +** whatsoever, whether direct, indirect, special, incidental, or ** +** consequential damages, whether arising under contract, tort ** +** (including negligence), or otherwise, even where the Distributor ** +** knew or should have known about the possibility of such damages. ** + +14. Severability + + The invalidity or unenforceability of any provision of this License + does not affect the validity or enforceability of the remainder of + this License. Such provision is to be reformed to the minimum extent + necessary to make it valid and enforceable. + +15. Definitions + + "Copyleft-Next Project" means the project that maintains the source + code repository at <https://github.com/copyleft-next/copyleft-next.git/> + as of the release date of this License. + + "Corresponding Source" of a Covered Work in Object Code form means (i) + the Source Code form of the Covered Work; (ii) all scripts, + instructions and similar information that are reasonably necessary for + a skilled developer to generate such Object Code from the Source Code + provided under (i); and (iii) a list clearly identifying all Separate + Works (other than those provided in compliance with (ii)) that were + specifically used in building and (if applicable) installing the + Covered Work (for example, a specified proprietary compiler including + its version number). Corresponding Source must be machine-readable. + + "Covered Work" means My Work or a Derived Work. + + "Derived Work" means a work of authorship that copies from, modifies, + adapts, is based on, is a derivative work of, transforms, translates or + contains all or part of My Work, such that copyright permission is + required. The following are not Derived Works: (i) Mere Aggregation; + (ii) a mere reproduction of My Work; and (iii) if My Work fails to + explicitly state an expectation otherwise, a work that merely makes + reference to My Work. + + "Distribute" means to distribute, transfer or make a copy available to + someone else, such that copyright permission is required. + + "Distributor" means Me and anyone else who Distributes a Covered Work. + + "FSF-Free" means classified as 'free' by the Free Software Foundation. + + "GPL" means a version of the GNU General Public License or the GNU + Affero General Public License. + + "I"/"Me"/"My" refers to the individual or legal entity that places My + Work under this License. "You"/"Your" refers to the individual or legal + entity exercising rights in My Work under this License. A legal entity + includes each entity that controls, is controlled by, or is under + common control with such legal entity. "Control" means (a) the power to + direct the actions of such legal entity, whether by contract or + otherwise, or (b) ownership of more than fifty percent of the + outstanding shares or beneficial ownership of such legal entity. + + "Licensed Patents" means all patent claims licensable royalty-free by + Me, now or in the future, that are necessarily infringed by making, + using, or selling My Work, and excludes claims that would be infringed + only as a consequence of further modification of My Work. + + "Mere Aggregation" means an aggregation of a Covered Work with a + Separate Work. + + "My Work" means the particular work of authorship I license to You + under this License. + + "Object Code" means any form of a work that is not Source Code. + + "OSI-Approved" means approved as 'Open Source' by the Open Source + Initiative. + + "Separate Work" means a work that is separate from and independent of a + particular Covered Work and is not by its nature an extension or + enhancement of the Covered Work, and/or a runtime library, standard + library or similar component that is used to generate an Object Code + form of a Covered Work. + + "Source Code" means the preferred form of a work for making + modifications to it.
Signature · 1 line
2.30.2
Luis Chamberlain
maintainerPATCH v7 2/12 testing: use the copyleft-next-0.3.1 SPDX tag
In reply to a message not in the archiveTwo selftests drivers exist under the copyleft-next license. These drivers were added prior to SPDX practice taking full swing in the kernel. Now that we have an SPDX tag for copylef-next-0.3.1 documented, embrace it and remove the boiler plate.
--- lib/test_kmod.c | 12 +----------- lib/test_sysctl.c | 12 +----------- tools/testing/selftests/kmod/kmod.sh | 13 +------------ tools/testing/selftests/sysctl/sysctl.sh | 12 +----------- 4 files changed, 4 insertions(+), 45 deletions(-)
lib/test_kmod.c +1 -11
@@ -1,18 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR copyleft-next-0.3.1 /* * kmod stress test driver * * Copyright (C) 2017 Luis R. Rodriguez <[email protected]> -* -* This program is free software; you can redistribute it and/or modify it -* under the terms of the GNU General Public License as published by the Free -* Software Foundation; either version 2 of the License, or at your option any -* later version; or, when distributed separately from the Linux kernel or -* when incorporated into other software packages, subject to the following -* license: -* -* This program is free software; you can redistribute it and/or modify it -* under the terms of copyleft-next (version 0.3.1 or later) as published -* at http://copyleft-next.org/. */ #define pr_fmt (fmt) KBUILD_MODNAME ": " fmt
lib/test_sysctl.c +1 -11
@@ -1,18 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR copyleft-next-0.3.1 /* * proc sysctl test driver * * Copyright (C) 2017 Luis R. Rodriguez <[email protected]> -* -* This program is free software; you can redistribute it and/or modify it -* under the terms of the GNU General Public License as published by the Free -* Software Foundation; either version 2 of the License, or at your option any -* later version; or, when distributed separately from the Linux kernel or -* when incorporated into other software packages, subject to the following -* license: -* -* This program is free software; you can redistribute it and/or modify it -* under the terms of copyleft-next (version 0.3.1 or later) as published -* at http://copyleft-next.org/. */ /*
tools/testing/selftests/kmod/kmod.sh +1 -12
@@ -1,18 +1,7 @@ #!/bin/bash -# +# SPDX-License-Identifier: GPL-2.0-or-later OR copyleft-next-0.3.1 # Copyright (C) 2017 Luis R. Rodriguez <[email protected]> # -# This program is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the Free -# Software Foundation; either version 2 of the License, or at your option any -# later version; or, when distributed separately from the Linux kernel or -# when incorporated into other software packages, subject to the following -# license: -# -# This program is free software; you can redistribute it and/or modify it -# under the terms of copyleft-next (version 0.3.1 or later) as published -# at http://copyleft-next.org/. - # This is a stress test script for kmod, the kernel module loader. It uses # test_kmod which exposes a series of knobs for the API for us so we can # tweak each test in userspace rather than in kernelspace.
tools/testing/selftests/sysctl/sysctl.sh +1 -11
@@ -1,16 +1,6 @@ #!/bin/bash +# SPDX-License-Identifier: GPL-2.0-or-later OR copyleft-next-0.3.1 # Copyright (C) 2017 Luis R. Rodriguez <[email protected]> -# -# This program is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the Free -# Software Foundation; either version 2 of the License, or at your option any -# later version; or, when distributed separately from the Linux kernel or -# when incorporated into other software packages, subject to the following -# license: -# -# This program is free software; you can redistribute it and/or modify it -# under the terms of copyleft-next (version 0.3.1 or later) as published -# at http://copyleft-next.org/. # This performs a series tests against the proc sysctl interface.
Signature · 1 line
2.30.2
Luis Chamberlain
maintainerPATCH v7 3/12 selftests: add tests_sysfs module
In reply to a message not in the archiveThis adds a new selftest module which can be used to test sysfs, which would otherwise require using an existing driver. This lets us muck with a template driver to test breaking things without affecting system behaviour or requiring the dependencies of a real device driver.
A series of 28 tests are added. Support for using two device types are supported:
* misc
* block
Contrary to sysctls, sysfs requires a full write to happen at once, and so we reduce the digit tests to single writes. Two main sysfs knobs are provided for testing reading/storing, one which doesn't inclur any delays and another which can incur programmed delays. What locks are held, if any, are configurable, at module load time, or through dynamic configuration at run time.
Since sysfs is a technically filesystem, but a pseudo one, which requires a kernel user, our test_sysfs module and respective test script embraces fstests format for tests in the kernel ring bufffer. Likewise, a scraper for kernel crashes is provided which matches what fstests does as well.
Two tests are kept disabled as they currently cause a deadlock, and so this provides a mechanism to easily show proof and demo how the deadlock can happen:
Demos the deadlock with a device specific lock./tools/testing/selftests/sysfs/sysfs.sh -t 0027
Demos the deadlock with rtnl_lock()./tools/testing/selftests/sysfs/sysfs.sh -t 0028
Two separate solutions to the deadlock issue have been proposed, and so now its a matter of either documenting this limitation or eventually adopting a generic fix.
This selftests will shortly be expanded upon with more tests which require further kernel changes in order to provide better test coverage.
--- MAINTAINERS | 7 + lib/Kconfig.debug | 12 + lib/Makefile | 1 + lib/test_sysfs.c | 943 ++++++++++++++++++ tools/testing/selftests/sysfs/Makefile | 12 + tools/testing/selftests/sysfs/config | 2 + tools/testing/selftests/sysfs/sysfs.sh | 1208 ++++++++++++++++++++++++ 7 files changed, 2185 insertions(+) create mode 100644 lib/test_sysfs.c create mode 100644 tools/testing/selftests/sysfs/Makefile create mode 100644 tools/testing/selftests/sysfs/config create mode 100755 tools/testing/selftests/sysfs/sysfs.sh
MAINTAINERS +7 -0
@@ -18166,6 +18166,13 @@ L: [email protected] S: Maintained F: drivers/mmc/host/sdhci-pci-dwc-mshc.c +SYSFS TEST DRIVER +M: Luis Chamberlain <[email protected]> +L: [email protected] +S: Maintained +F: lib/test_sysfs.c +F: tools/testing/selftests/sysfs/ + SYSTEM CONFIGURATION (SYSCON) M: Lee Jones <[email protected]> M: Arnd Bergmann <[email protected]>
lib/Kconfig.debug +12 -0
@@ -2343,6 +2343,18 @@ config TEST_SYSCTL If unsure, say N. +config TEST_SYSFS + tristate "sysfs test driver" + depends on SYSFS + depends on NET + depends on BLOCK + help + This builds the "test_sysfs" module. This driver enables to test the + sysfs file system safely without affecting production knobs which + might alter system functionality. + + If unsure, say N. + config BITFIELD_KUNIT tristate "KUnit test bitfield functions at runtime" depends on KUNIT
lib/Makefile +1 -0
@@ -61,6 +61,7 @@ obj-$(CONFIG_TEST_FIRMWARE) += test_firmware.o obj-$(CONFIG_TEST_BITOPS) += test_bitops.o CFLAGS_test_bitops.o += -Werror obj-$(CONFIG_TEST_SYSCTL) += test_sysctl.o +obj-$(CONFIG_TEST_SYSFS) += test_sysfs.o obj-$(CONFIG_TEST_HASH) += test_hash.o test_siphash.o obj-$(CONFIG_TEST_IDA) += test_ida.o obj-$(CONFIG_KASAN_KUNIT_TEST) += test_kasan.o
lib/test_sysfs.c new file +943 -0
943 lines not on this page · raw message
tools/testing/selftests/sysfs/Makefile new file +12 -0
@@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0-only +# Makefile for sysfs selftests. + +# No binaries, but make sure arg-less "make" doesn't trigger "run_tests". +all: + +TEST_PROGS := sysfs.sh + +include ../lib.mk + +# Nothing to clean up. +clean:
tools/testing/selftests/sysfs/config new file +2 -0
@@ -0,0 +1,2 @@ +CONFIG_SYSFS=m +CONFIG_TEST_SYSFS=m
tools/testing/selftests/sysfs/sysfs.sh new file +1208 -0
1,208 lines not on this page · raw message
Signature · 1 line
2.30.2
Re: [PATCH v7 03/12] selftests: add tests_sysfs module
lib/test_sysfs.c line 186
Quoted 5 lines · Luis Chamberlain
lib/test_sysfs.c new file +5
-0+static void test_dev_config_lock (struct sysfs_test_device *test_dev) +{ +struct test_config *config; + + config = &test_dev->config;
Please change the above into a single line (there are multiple occurrences):
struct test_config *config = &test_dev->config;
Thanks,
Bart.
Luis Chamberlain
maintainerPATCH v7 4/12 kernfs: add initial failure injection support
In reply to a message not in the archiveThis adds initial failure injection support to kernfs. We start off with debug knobs which when enabled allow test drivers, such as test_sysfs, to then make use of these to try to force certain difficult races to take place with a high degree of certainty.
This only adds runtime code *iff* the new bool CONFIG_FAIL_KERNFS_KNOBS is enabled in your kernel. If you don't have this enabled this provides no new functional. When CONFIG_FAIL_KERNFS_KNOBS is disabled the new routine kernfs_debug_should_wait() ends up being transformed to if (false), and so the compiler should optimize these out as dead code producing no new effective binary changes.
We start off with enabling failure injections in kernfs by allowing us to alter the way kernfs_fop_write_iter() behaves. We allow for the routine kernfs_fop_write_iter() to wait for a certain condition in the kernel to occur, after which it will sleep a predefined amount of time. This lets kernfs users to time exactly when it want kernfs_fop_write_iter() to complete, allowing for developing race conditions and test for correctness in kernfs.
You'd boot with this enabled on your kernel command line:
fail_kernfs_fop_write_iter=1,100,0,1
The values are <interval,probability,size,times>, we don't care for size, so for now we ignore it. The above ensures a failure will trigger only once.
*How* we allow for this routine to change behaviour is left to knobs we expose under debugfs:
# ls -1 /sys/kernel/debug/kernfs/config_fail_kernfs_fop_write_iter/ wait_after_active wait_after_mutex wait_at_start wait_before_mutex
A debugfs entry also exists to allow us to sleep a configurabler amount of time after the completion:
/sys/kernel/debug/kernfs/sleep_after_wait_ms
These two sets of knobs allow us to construct races and demonstrate how the kernfs active reference should suffice to project against races.
Enabling CONFIG_FAULT_INJECTION_DEBUG_FS enables us to configure the differnt fault injection parametres for the new fail_kernfs_fop_write_iter fault injection at run time:
ls -1 /sys/kernel/debug/kernfs/fail_kernfs_fop_write_iter/ interval
probability
space
task-filter
times
verboseverbose_ratelimit_burst verbose_ratelimit_interval_ms
--- .../fault-injection/fault-injection.rst | 22 +++++ MAINTAINERS | 2 +- fs/kernfs/Makefile | 1 + fs/kernfs/failure-injection.c | 91 +++++++++++++++++++ fs/kernfs/file.c | 13 +++ fs/kernfs/kernfs-internal.h | 72 +++++++++++++++ include/linux/kernfs.h | 5 + lib/Kconfig.debug | 10 ++ 8 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 fs/kernfs/failure-injection.c
Documentation/fault-injection/fault-injection.rst +22 -0
@@ -28,6 +28,28 @@ Available fault injection capabilities injects kernel RPC client and server failures. +- fail_kernfs_fop_write_iter + + Allows for failures to be enabled inside kernfs_fop_write_iter(). Enabling + this does not immediately enable any errors to occur. You must configure + how you want this routine to fail or change behaviour by using the debugfs + knobs for it: + + # ls -1 /sys/kernel/debug/kernfs/config_fail_kernfs_fop_write_iter/ + wait_after_active + wait_after_mutex + wait_at_start + wait_before_mutex + + You can also configure how long to sleep after a wait under + + /sys/kernel/debug/kernfs/sleep_after_wait_ms + + If you enable CONFIG_FAULT_INJECTION_DEBUG_FS the fail_add_disk failure + injection parameters are placed under: + + /sys/kernel/debug/kernfs/fail_kernfs_fop_write_iter/ + - fail_make_request injects disk IO errors on devices permitted by setting
MAINTAINERS +1 -1
@@ -10341,7 +10341,7 @@ M: Greg Kroah-Hartman <[email protected]> M: Tejun Heo <[email protected]> S: Supported T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core.git -F: fs/kernfs/ +F: fs/kernfs/* F: include/linux/kernfs.h KEXEC
fs/kernfs/Makefile +1 -0
@@ -4,3 +4,4 @@ # obj-y := mount.o inode.o dir.o file.o symlink.o +obj-$(CONFIG_FAIL_KERNFS_KNOBS) += failure-injection.o
fs/kernfs/failure-injection.c new file +91 -0
91 lines not on this page · raw message
fs/kernfs/file.c +13 -0
@@ -259,6 +259,9 @@ static ssize_t kernfs_fop_write_iter(struct kiocb *iocb, struct iov_iter *iter)const struct kernfs_ops *ops;char *buf; +if (kernfs_debug_should_wait (kernfs_fop_write_iter, at_start)) +kernfs_debug_wait (); +if (of->atomic_write_len) {if (len > of->atomic_write_len)return -E2BIG ;
@@ -280,17 +283,27 @@ static ssize_t kernfs_fop_write_iter(struct kiocb *iocb, struct iov_iter *iter) } buf[len] ='\0' ;/* guarantee string termination */ +if (kernfs_debug_should_wait (kernfs_fop_write_iter, before_mutex)) +kernfs_debug_wait (); +/* * @of->mutex nests outside active ref and is used both to ensure that * the ops aren't called concurrently for the same open file. */ mutex_lock (&of->mutex); + +if (kernfs_debug_should_wait (kernfs_fop_write_iter, after_mutex)) +kernfs_debug_wait (); +if (!kernfs_get_active (of->kn)) {mutex_unlock (&of->mutex); len = -ENODEV ; goto out_free; } +if (kernfs_debug_should_wait (kernfs_fop_write_iter, after_active)) +kernfs_debug_wait (); + ops =kernfs_ops (of->kn);if (ops->write) len = ops->write (of, buf, len, iocb->ki_pos);
fs/kernfs/kernfs-internal.h +72 -0
82 lines not on this page · raw message
include/linux/kernfs.h +5 -0
@@ -411,6 +411,11 @@ void kernfs_init(void);struct kernfs_node *kernfs_find_and_get_node_by_id (struct kernfs_root *root,u64 id); + +#ifdef CONFIG_FAIL_KERNFS_KNOBS +extern struct completion kernfs_debug_wait_completion; +#endif +#else /* CONFIG_KERNFS */ static inline enum kernfs_node_type kernfs_type (struct kernfs_node *kn)
lib/Kconfig.debug +10 -0
@@ -1902,6 +1902,16 @@ config FAULT_INJECTION_USERCOPY Provides fault-injection capability to inject failures in usercopy functions (copy_from_user(), get_user(), ...). +config FAIL_KERNFS_KNOBS + bool "Fault-injection support in kernfs" + depends on FAULT_INJECTION + help + Provide fault-injection capability for kernfs. This only enables + the error injection functionality. To use it you must configure which + which path you want to trigger on error on using debugfs under + /sys/kernel/debug/kernfs/config_fail_kernfs_fop_write_iter/. By + default all of these are disabled. + config FAIL_MAKE_REQUEST bool "Fault-injection capability for disk IO" depends on FAULT_INJECTION && BLOCK
Signature · 1 line
2.30.2
Luis Chamberlain
maintainerPATCH v7 5/12 test_sysfs: add support to use kernfs failure injection
In reply to a message not in the archiveThis extends test_sysfs with support for using the failure injection wait completion and knobs to force a few race conditions which demonstrates that kernfs active reference protection is sufficient for kobject / device protection at higher layers.
This adds 4 new tests which tries to remove the device attribute store operation in 4 different situations:
1) at the start of kernfs_kernfs_fop_write_iter()
2) before the of->mutex is held in kernfs_kernfs_fop_write_iter()
3) after the of->mutex is held in kernfs_kernfs_fop_write_iter()
4) after the kernfs node active reference is taken
A write fails in call cases except the last one, test number #32. There is a good explanation for this: *once* kernfs_get_active() gets called we have a guarantee that the kernfs entry cannot be removed. If kernfs_get_active() succeeds that entry cannot be removed and so anything trying to remove that entry will have to wait. It is perhaps not obvious but since a sysfs write will trigger eventually a kernfs_get_active() call, and *only* if this succeeds will the sysfs op be called, this and the fact that you cannot remove the kernfs entry while the kenfs entry is active implies that a module that created the respective sysfs / kernfs entry *cannot* possibly be removed during a sysfs operation. And test number 32 provides us with proof of this. If it were not true test #32 should crash.
No null dereferences are reproduced, even though this has been observed in some complex testing cases [0]. If this issue really exists we should have enough tools on the sysfs_test toolbox now to try to reproduce this easily without having to poke around other drivers. It very likley was the case that the issue reported [0] was possibly a side issue after the first bug which was zram specific. This is why it is important to isolate the issue and try to reproduce it in a generic form using the test_sysfs driver.
[0] https://lkml.kernel.org/r/20210623215007.862787-1-mcgrof-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org
--- lib/Kconfig.debug | 3 + lib/test_sysfs.c | 31 +++++ tools/testing/selftests/sysfs/config | 3 + tools/testing/selftests/sysfs/sysfs.sh | 175 +++++++++++++++++++++++++ 4 files changed, 212 insertions(+)
lib/Kconfig.debug +3 -0
@@ -2358,6 +2358,9 @@ config TEST_SYSFS depends on SYSFS depends on NET depends on BLOCK + select FAULT_INJECTION + select FAULT_INJECTION_DEBUG_FS + select FAIL_KERNFS_KNOBS help This builds the "test_sysfs" module. This driver enables to test the sysfs file system safely without affecting production knobs which
lib/test_sysfs.c +31 -0
@@ -38,6 +38,11 @@#include <linux/rtnetlink.h> #include <linux/genhd.h> #include <linux/blkdev.h> +#include <linux/kernfs.h> + +#ifdef CONFIG_FAIL_KERNFS_KNOBS +MODULE_IMPORT_NS (KERNFS_DEBUG_PRIVATE ); +#endif static bool enable_lock;module_param (enable_lock, bool_enable_only,0644 );
@@ -82,6 +87,13 @@ static bool enable_verbose_rmmod;module_param (enable_verbose_rmmod, bool_enable_only,0644 );MODULE_PARM_DESC (enable_verbose_rmmod,"enable verbose print messages on rmmod" ); +#ifdef CONFIG_FAIL_KERNFS_KNOBS +static bool enable_completion_on_rmmod; +module_param (enable_completion_on_rmmod, bool_enable_only,0644 ); +MODULE_PARM_DESC (enable_completion_on_rmmod, +"enable sending a kernfs completion on rmmod" ); +#endif +static int sysfs_test_major;/**
@@ -289,6 +301,12 @@ static ssize_t config_show(struct device *dev,"enable_verbose_writes:\t%s\n" , enable_verbose_writes ? "true" : "false"); +#ifdef CONFIG_FAIL_KERNFS_KNOBS + len +=snprintf (buf+len,PAGE_SIZE - len, +"enable_completion_on_rmmod:\t%s\n" , + enable_completion_on_rmmod ?"true" :"false" ); +#endif +test_dev_config_unlock (test_dev);return len;
@@ -926,10 +944,23 @@ static int __init test_sysfs_init(void) }module_init (test_sysfs_init); +#ifdef CONFIG_FAIL_KERNFS_KNOBS +/* The goal is to race our device removal with a pending kernfs -> store call */ +static void test_sysfs_kernfs_send_completion_rmmod (void ) +{ +if (!enable_completion_on_rmmod) +return ; +complete (&kernfs_debug_wait_completion); +} +#else +static inline void test_sysfs_kernfs_send_completion_rmmod (void ) {} +#endif + static void __exit test_sysfs_exit(void) {if (enable_debugfs)debugfs_remove (debugfs_dir); +test_sysfs_kernfs_send_completion_rmmod ();if (delay_rmmod_ms)msleep (delay_rmmod_ms);unregister_test_dev_sysfs (first_test_dev);
tools/testing/selftests/sysfs/config +3 -0
@@ -1,2 +1,5 @@ CONFIG_SYSFS=m CONFIG_TEST_SYSFS=m +CONFIG_FAULT_INJECTION=y +CONFIG_FAULT_INJECTION_DEBUG_FS=y +CONFIG_FAIL_KERNFS_KNOBS=y
tools/testing/selftests/sysfs/sysfs.sh +175 -0
211 lines not on this page · raw message
Signature · 1 line
2.30.2
Luis Chamberlain
maintainerPATCH v7 6/12 kernel/module: add documentation for try_module_get()
In reply to a message not in the archiveThere is quite a bit of tribal knowledge around proper use of try_module_get() and that it must be used only in a context which can ensure the module won't be gone during the operation. Document this little bit of tribal knowledge.
I'm extending this tribal knowledge with new developments which it seems some folks do not yet believe to be true: we can be sure a module will exist during the lifetime of a sysfs file operation. For proof, refer to test_sysfs test #32:
./tools/testing/selftests/sysfs/sysfs.sh -t 0032
Without this being true, the write would fail or worse, a crash would happen, in this test. It does not.
--- include/linux/module.h | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-)
include/linux/module.h +32 -2
@@ -609,10 +609,40 @@ void symbol_put_addr(void *addr);to handle the error case (which only happens with rmmod --wait). */ extern void __module_get (struct module *module); -/* This is the Right Way to get a module: if it fails, it's being removed, -* so pretend it's not there. */ +/** +* try_module_get() - yields to module removal and bumps refcnt otherwise +* @module: the module we should check for +* +* This can be used to try to bump the reference count of a module, so to +* prevent module removal. The reference count of a module is not allowed +* to be incremented if the module is already being removed. +* +* Care must be taken to ensure the module cannot be removed during the call to +* try_module_get(). This can be done by having another entity other than the +* module itself increment the module reference count, or through some other +* means which guarantees the module could not be removed during an operation. +* An example of this later case is using try_module_get() in a sysfs file +* which the module created. The sysfs store / read file operations are +* gauranteed to exist through the use of kernfs's active reference (see +* kernfs_active()). If a sysfs file operation is being run, the module which +* created it must still exist as the module is in charge of removing the same +* sysfs file being read. Also, a sysfs / kernfs file removal cannot happen +* unless the same file is not active. +* +* One of the real values to try_module_get() is the module_is_live() check +* which ensures this the caller of try_module_get() can yield to userspace +* module removal requests and fail whatever it was about to process. +*/ extern bool try_module_get (struct module *module); +/** +* module_put() - release a reference count to a module +* @module: the module we should release a reference count for +* +* If you successfully bump a reference count to a module with try_module_get(), +* when you are finished you must call module_put() to release that reference +* count. +*/ extern void module_put (struct module *module);#else /*!CONFIG_MODULE_UNLOAD*/
Signature · 1 line
2.30.2
Luis Chamberlain
maintainerPATCH v7 7/12 fs/kernfs/symlink.c: replace S_IRWXUGO with 0777 on kernfs_create_link()
In reply to a message not in the archiveIf one ends up extending this line checkpatch will complain about the use of S_IRWXUGO suggesting it is not preferred and that 0777 should be used instead. Take the tip from checkpatch and do that change before we do our subsequent changes.
This makes no functional changes.
--- fs/kernfs/symlink.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
fs/kernfs/symlink.c +1 -2
@@ -36,8 +36,7 @@ struct kernfs_node *kernfs_create_link(struct kernfs_node *parent, gid = target->iattr->ia_gid; } - kn =kernfs_new_node (parent, name,S_IFLNK |S_IRWXUGO , uid, gid, -KERNFS_LINK ); + kn =kernfs_new_node (parent, name,S_IFLNK |0777 , uid, gid,KERNFS_LINK );if (!kn)return ERR_PTR (-ENOMEM );
Signature · 1 line
2.30.2
Luis Chamberlain
maintainerPATCH v7 8/12 fs/sysfs/dir.c: replace S_IRWXU|S_IRUGO|S_IXUGO with 0755 sysfs_create_dir_ns()
In reply to a message not in the archiveIf one ends up expanding on this line checkpatch will complain that the combination S_IRWXU|S_IRUGO|S_IXUGO should just be replaced with the octal 0755. Do that.
This makes no functional changes.
--- fs/sysfs/dir.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
fs/sysfs/dir.c +1 -2
@@ -56,8 +56,7 @@ int sysfs_create_dir_ns(struct kobject *kobj, const void *ns)kobject_get_ownership (kobj, &uid, &gid); - kn =kernfs_create_dir_ns (parent,kobject_name (kobj), -S_IRWXU |S_IRUGO |S_IXUGO , uid, gid, + kn =kernfs_create_dir_ns (parent,kobject_name (kobj),0755 , uid, gid, kobj, ns);if (IS_ERR (kn)) {if (PTR_ERR (kn) == -EEXIST )
Signature · 1 line
2.30.2
Luis Chamberlain
maintainerPATCH v7 9/12 sysfs: fix deadlock race with module removal
In reply to a message not in the archiveWhen sysfs attributes use a lock also used on module removal we can race to deadlock. This happens when for instance a sysfs file on a driver is used, then at the same time we have module removal call trigger. The module removal call code holds a lock, and then the sysfs file entry waits for the same lock. While holding the lock the module removal tries to remove the sysfs entries, but these cannot be removed yet as one is waiting for a lock. This won't complete as the lock is already held. Likewise module removal cannot complete, and so we deadlock.
This can now be easily reproducible with our sysfs selftest as follows:
./tools/testing/selftests/sysfs/sysfs.sh -t 0027
To fix this we extend the struct kernfs_node with a module reference and use the try_module_get() after kernfs_get_active() is called which protects integrity and the existence of the kernfs node during the operation.
So long as the kernfs node is protected with kernfs_get_active() we know we can rely on its contents. And, as now just documented in the previous patch, we also now know that once kernfs_get_active() is called the module is also guarded to exist and cannot be removed.
If try_module_get() fails we fail the operation on the kernfs node.
We use a try method as a full lock means we'd then make our sysfs attributes busy us out from possible module removal, and so userspace could force denying module removal, a silly form of "DOS" against module removal. A try lock on the module removal ensures we give priority to module removal and interacting with sysfs attributes only comes second. Using a full lock could mean for instance that if you don't stop poking at sysfs files you cannot remove a module.
Races between removal of sysfs files and the module are not possible given sysfs files are created by the same module, and when a sysfs file is being used kernfs prevents removal of the sysfs file. So if module removal is actually happening the removal would have to wait until the sysfs file operation is complete.
This deadlock was first reported with the zram driver, however the live patching folks have acknowledged they have observed this as well with live patching, when a live patch is removed. I was then able to reproduce easily by creating a dedicated selftests.
A sketch of how this can happen follows:
CPU A CPU B
whatever_store()module_unload
mutex_lock(foo)
mutex_lock(foo)
del_gendisk(zram->disk);
device_del()
device_remove_groups()
In this situation whatever_store() is waiting for the mutex foo to become unlocked, but that won't happen until module removal is complete. But module removal won't complete until the sysfs file being poked completes which is waiting for a lock already held.
--- arch/x86/kernel/cpu/resctrl/rdtgroup.c | 4 +- fs/kernfs/dir.c | 41 ++++++++++++++++++-- fs/kernfs/file.c | 7 +++- fs/kernfs/kernfs-internal.h | 1 + fs/kernfs/symlink.c | 3 +- fs/sysfs/dir.c | 4 +- fs/sysfs/file.c | 10 +++-- fs/sysfs/group.c | 1 + include/linux/kernfs.h | 11 ++++-- include/linux/sysfs.h | 52 ++++++++++++++++++++------ kernel/cgroup/cgroup.c | 2 +- 11 files changed, 106 insertions(+), 30 deletions(-)
arch/x86/kernel/cpu/resctrl/rdtgroup.c +2 -2
@@ -207,7 +207,7 @@ static int rdtgroup_add_file(struct kernfs_node *parent_kn, struct rftype *rft)struct kernfs_node *kn;int ret; - kn =__kernfs_create_file (parent_kn, rft->name, rft->mode, + kn =__kernfs_create_file (parent_kn, rft->name, rft->mode,NULL ,GLOBAL_ROOT_UID ,GLOBAL_ROOT_GID ,0 , rft->kf_ops, rft,NULL ,NULL );if (IS_ERR (kn))
@@ -2480,7 +2480,7 @@ static int mon_addfile(struct kernfs_node *parent_kn, const char *name,struct kernfs_node *kn;int ret =0 ; - kn =__kernfs_create_file (parent_kn, name,0444 , + kn =__kernfs_create_file (parent_kn, name,0444 ,NULL ,GLOBAL_ROOT_UID ,GLOBAL_ROOT_GID ,0 , &kf_mondata_ops, priv,NULL ,NULL );if (IS_ERR (kn))
fs/kernfs/dir.c +37 -4
119 lines not on this page · raw message
fs/kernfs/file.c +5 -2
@@ -974,6 +974,7 @@ const struct file_operations kernfs_file_fops = {* @uid: uid of the file * @gid: gid of the file * @size: size of the file +* @owner: if set, the module owner of the file * @ops: kernfs operations for the file * @priv: private data for the file * @ns: optional namespace tag of the file
@@ -983,7 +984,9 @@ const struct file_operations kernfs_file_fops = {*/ struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent, const char *name, - umode_t mode, kuid_t uid, kgid_t gid, + umode_t mode, + struct module *owner, + kuid_t uid, kgid_t gid, loff_t size, const struct kernfs_ops *ops, void *priv, const void *ns,
@@ -996,7 +999,7 @@ struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent, flags =KERNFS_FILE ; kn =kernfs_new_node (parent, name, (mode &S_IALLUGO ) |S_IFREG , - uid, gid, flags); + owner, uid, gid, flags);if (!kn)return ERR_PTR (-ENOMEM );
fs/kernfs/kernfs-internal.h +1 -0
@@ -133,6 +133,7 @@ void kernfs_put_active(struct kernfs_node *kn);int kernfs_add_one (struct kernfs_node *kn);struct kernfs_node *kernfs_new_node (struct kernfs_node *parent,const char *name,umode_t mode, +struct module *owner,kuid_t uid,kgid_t gid,unsigned flags);
fs/kernfs/symlink.c +2 -1
@@ -36,7 +36,8 @@ struct kernfs_node *kernfs_create_link(struct kernfs_node *parent, gid = target->iattr->ia_gid; } - kn =kernfs_new_node (parent, name,S_IFLNK |0777 , uid, gid,KERNFS_LINK ); + kn =kernfs_new_node (parent, name,S_IFLNK |0777 , target->owner, uid, + gid,KERNFS_LINK );if (!kn)return ERR_PTR (-ENOMEM );
fs/sysfs/dir.c +2 -2
@@ -56,8 +56,8 @@ int sysfs_create_dir_ns(struct kobject *kobj, const void *ns)kobject_get_ownership (kobj, &uid, &gid); - kn =kernfs_create_dir_ns (parent,kobject_name (kobj),0755 , uid, gid, - kobj, ns); + kn =kernfs_create_dir_ns (parent,kobject_name (kobj),0755 ,NULL , uid, + gid, kobj, ns);if (IS_ERR (kn)) {if (PTR_ERR (kn) == -EEXIST )sysfs_warn_dup (parent,kobject_name (kobj));
fs/sysfs/file.c +6 -4
@@ -291,8 +291,9 @@ int sysfs_add_file_mode_ns(struct kernfs_node *parent, key = attr->key ?: (struct lock_class_key *)&attr->skey;#endif - kn =__kernfs_create_file (parent, attr->name, mode &0777 , uid, gid, -PAGE_SIZE , ops, (void *)attr, ns, key); + kn =__kernfs_create_file (parent, attr->name, mode &0777 , attr->owner, + uid, gid,PAGE_SIZE , ops, (void *)attr, ns, + key);if (IS_ERR (kn)) {if (PTR_ERR (kn) == -EEXIST )sysfs_warn_dup (parent, attr->name);
@@ -326,8 +327,9 @@ int sysfs_add_bin_file_mode_ns(struct kernfs_node *parent, key = attr->key ?: (struct lock_class_key *)&attr->skey;#endif - kn =__kernfs_create_file (parent, attr->name, mode &0777 , uid, gid, - battr->size, ops, (void *)attr, ns, key); + kn =__kernfs_create_file (parent, attr->name, mode &0777 , attr->owner, + uid, gid, battr->size, ops, (void *)attr, ns, + key);if (IS_ERR (kn)) {if (PTR_ERR (kn) == -EEXIST )sysfs_warn_dup (parent, attr->name);
fs/sysfs/group.c +1 -0
@@ -135,6 +135,7 @@ static int internal_create_group(struct kobject *kobj, int update, }else { kn =kernfs_create_dir_ns (kobj->sd, grp->name,S_IRWXU |S_IRUGO |S_IXUGO , + grp->owner, uid, gid, kobj,NULL );if (IS_ERR (kn)) {if (PTR_ERR (kn) == -EEXIST )
include/linux/kernfs.h +8 -3
@@ -160,6 +160,7 @@ struct kernfs_node {unsigned short flags;umode_t mode; +struct module *owner;struct kernfs_iattrs *iattr; };
@@ -369,12 +370,14 @@ void kernfs_destroy_root(struct kernfs_root *root);struct kernfs_node *kernfs_create_dir_ns (struct kernfs_node *parent,const char *name,umode_t mode, +struct module *owner,kuid_t uid,kgid_t gid,void *priv,const void *ns);struct kernfs_node *kernfs_create_empty_dir (struct kernfs_node *parent,const char *name); struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent, const char *name, umode_t mode, + struct module *owner, kuid_t uid, kgid_t gid, loff_t size, const struct kernfs_ops *ops,
@@ -471,13 +474,15 @@ static inline void kernfs_destroy_root(struct kernfs_root *root) { }static inline struct kernfs_node *kernfs_create_dir_ns (struct kernfs_node *parent,const char *name, -umode_t mode,kuid_t uid,kgid_t gid, +umode_t mode,struct module *owner, +kuid_t uid,kgid_t gid,void *priv,const void *ns) {return ERR_PTR (-ENOSYS ); }static inline struct kernfs_node *__kernfs_create_file (struct kernfs_node *parent,const char *name, -umode_t mode,kuid_t uid,kgid_t gid, +umode_t mode,struct module *owner, +kuid_t uid,kgid_t gid,loff_t size,const struct kernfs_ops *ops,void *priv,const void *ns,struct lock_class_key *key) {return ERR_PTR (-ENOSYS ); }
@@ -564,7 +569,7 @@ static inline struct kernfs_node * kernfs_create_dir(struct kernfs_node *parent, const char *name, umode_t mode, void *priv) { - return kernfs_create_dir_ns(parent, name, mode, + return kernfs_create_dir_ns(parent, name, mode, parent->owner, GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, priv, NULL); }
include/linux/sysfs.h +41 -11
131 lines not on this page · raw message
kernel/cgroup/cgroup.c +1 -1
@@ -3949,7 +3949,7 @@ static int cgroup_add_file(struct cgroup_subsys_state *css, struct cgroup *cgrp, key = &cft->lockdep_key;#endif kn =__kernfs_create_file (cgrp->kn,cgroup_file_name (cgrp, cft, name), -cgroup_file_mode (cft), +cgroup_file_mode (cft),NULL ,GLOBAL_ROOT_UID ,GLOBAL_ROOT_GID ,0 , cft->kf_ops, cft,NULL , key);
Signature · 1 line
2.30.2
Tejun Heo
maintainerRe: [PATCH v7 09/12] sysfs: fix deadlock race with module removal
Hello,
Quoted 9 lines · Luis Chamberlain
If
try_module_get()fails we fail the operation on the kernfs node.We use a try method as a full lock means we'd then make our sysfs attributes busy us out from possible module removal, and so userspace could force denying module removal, a silly form of "DOS" against module removal. A try lock on the module removal ensures we give priority to module removal and interacting with sysfs attributes only comes second. Using a full lock could mean for instance that if you don't stop poking at sysfs files you cannot remove a module.
I find this explanation odd because there's no real equivalent to locking the module (as opposed to try locking) because you can't wait for the removal to finish and then grant the lock, so any operation which increases the reference *has* to be a try method unless the caller already holds a reference to the same module and thus knows that the module is already pinned. The code isn't wrong, so maybe just drop the related paragraphs in the commit message?
fs/kernfs/dir.c struct kernfs_node *kernfs_node_from_dentry(struct dentry *dentry) · line 604
Quoted 6 lines
static struct
kernfs_node*__kernfs_new_node(structkernfs_root*root,fs/kernfs/dir.c +1
-0struct kernfs_node *parent, const char *name, umode_t mode, + struct module *owner, kuid_t uid, kgid_t gid, unsigned flags)
Is there a particular reason why @owner is added between @mode and @uid? Sitting between two fs attributes seems a bit awkward. Maybe it can just be the last one?
Thanks.
Signature · 1 line
tejun
Luis Chamberlain
maintainerRe: [PATCH v7 09/12] sysfs: fix deadlock race with module removal
Quoted 13 lines · Tejun Heo
Hello,
Quoted 9 lines · Luis Chamberlain
If
try_module_get()fails we fail the operation on the kernfs node.We use a try method as a full lock means we'd then make our sysfs attributes busy us out from possible module removal, and so userspace could force denying module removal, a silly form of "DOS" against module removal. A try lock on the module removal ensures we give priority to module removal and interacting with sysfs attributes only comes second. Using a full lock could mean for instance that if you don't stop poking at sysfs files you cannot remove a module.
Quoted 2 lines
I find this explanation odd because there's no real equivalent to locking the module (as opposed to try locking)
Actually there is, __module_get() but I suspect some of these users are probably incorrect and should be be moved to try. The documentation about "rmmod --wait" for __module_get() is also outdated as that option is no longer supported. I'll send an update for that later.
Quoted 5 lines
because you can't wait for the
removal to finish and then grant the lock, so any operation which increases the reference *has* to be a try method unless the caller already holds a reference to the same module and thus knows that the module is already pinned.
Right, the reason I mention the alternative is that we technically don't need to use try in this case since during a kernfs op it is implied the module will be pinned, but we have further motivations to use a try method here: to avoid a possible DOS from module removal by userspace mucking with ops.
Quoted 2 lines
The code isn't wrong, so maybe just drop the related paragraphs in the commit message?
Does it make sense to clarify the above a bit more somehow? Or do think its not needed?
Quoted 10 lines
fs/kernfs/dir.c struct kernfs_node *kernfs_node_from_dentry(struct dentry *dentry) · line 604
Quoted 6 lines
static struct
kernfs_node*__kernfs_new_node(structkernfs_root*root,fs/kernfs/dir.c +1
-0struct kernfs_node *parent, const char *name, umode_t mode, + struct module *owner, kuid_t uid, kgid_t gid, unsigned flags)Is there a particular reason why @owner is added between @mode and @uid? Sitting between two fs attributes seems a bit awkward. Maybe it can just be the last one?
No, I just picked an arbitrary place. Sure I'll move it to the end.
Luis
Tejun Heo
maintainerRe: [PATCH v7 09/12] sysfs: fix deadlock race with module removal
Hello,
Quoted 5 lines · Luis Chamberlain
Quoted 2 lines
I find this explanation odd because there's no real equivalent to locking the module (as opposed to try locking)
Actually there is,
__module_get()but I suspect some of these users are probably incorrect and should be be moved to try. The documentation
__module_get() is just getting an extra ref when the caller already has one (or more). It can't be used to freshly acquire a new reference. There is no equivalence between the relationship between try_module_get() and __module_get() and the one between spin_trylock() and spin_lock().
Quoted 3 lines
Right, the reason I mention the alternative is that we technically don't need to use try in this case since during a kernfs op it is implied the module will be pinned, but we have further motivations to use a try
I'm confused. If the module is already pinned, why are we getting an extra reference? Also, I don't understand how this has that much to do with preventing ddoses. I mean, it does cut down the duration of one operation but the eventual gating is through whoever acquiring the initial reference through try_module_get(), which again is the *only* way to acquire a fresh reference.
Thanks.
Signature · 1 line
tejun
Luis Chamberlain
maintainerRe: [PATCH v7 09/12] sysfs: fix deadlock race with module removal
Quoted 8 lines · Tejun Heo
Quoted 3 lines · Luis Chamberlain
Right, the reason I mention the alternative is that we technically don't need to use try in this case since during a kernfs op it is implied the module will be pinned, but we have further motivations to use a try
I'm confused. If the module is already pinned, why are we getting an extra reference?
Sorry, I meant its pinned implicitly not through an actual refcount. A module exit can happen then in that case, it just that the sysfs removal will have to wait until the active reference for the kernfs ops go down.
Quoted 5 lines
Also, I don't understand how this has that much to do
with preventing ddoses. I mean, it does cut down the duration of one operation but the eventual gating is through whoever acquiring the initial reference throughtry_module_get(), which again is the *only* way to acquire a fresh reference.
True. I can just leave that out from the commit log. But it is perhaps an implicit gain here that using try alows module exit to trump use of an kernfs op. Maybe's that's sufficient to mention.
Luis
Re: [PATCH v7 09/12] sysfs: fix deadlock race with module removal
In reply to Luis ChamberlainQuoted 47 lines · Luis Chamberlain
When sysfs attributes use a lock also used on module removal we can race to deadlock. This happens when for instance a sysfs file on a driver is used, then at the same time we have module removal call trigger. The module removal call code holds a lock, and then the sysfs file entry waits for the same lock. While holding the lock the module removal tries to remove the sysfs entries, but these cannot be removed yet as one is waiting for a lock. This won't complete as the lock is already held. Likewise module removal cannot complete, and so we deadlock.
This can now be easily reproducible with our sysfs selftest as follows:
./tools/testing/selftests/sysfs/sysfs.sh-t 0027To fix this we extend the struct
kernfs_nodewith a module reference and use thetry_module_get()afterkernfs_get_active()is called which protects integrity and the existence of the kernfs node during the operation.So long as the kernfs node is protected with
kernfs_get_active()we know we can rely on its contents. And, as now just documented in the previous patch, we also now know that oncekernfs_get_active()is called the module is also guarded to exist and cannot be removed.If
try_module_get()fails we fail the operation on the kernfs node.We use a try method as a full lock means we'd then make our sysfs attributes busy us out from possible module removal, and so userspace could force denying module removal, a silly form of "DOS" against module removal. A try lock on the module removal ensures we give priority to module removal and interacting with sysfs attributes only comes second. Using a full lock could mean for instance that if you don't stop poking at sysfs files you cannot remove a module.
Races between removal of sysfs files and the module are not possible given sysfs files are created by the same module, and when a sysfs file is being used kernfs prevents removal of the sysfs file. So if module removal is actually happening the removal would have to wait until the sysfs file operation is complete.
This deadlock was first reported with the zram driver, however the live patching folks have acknowledged they have observed this as well with live patching, when a live patch is removed. I was then able to reproduce easily by creating a dedicated selftests.
A sketch of how this can happen follows:
Quoted 8 lines
CPU A CPU B
whatever_store()module_unload
mutex_lock(foo)
mutex_lock(foo)
del_gendisk(zram->disk);
device_del()
device_remove_groups()
This flow seems possible to trigger with:
echo $dev > /sys/bus/$bus/drivers/$driver/unbind
I am missing why module pinning is part of the solution when it's the device_del() path that is racing? Module removal is just a more coarse grained way to trigger unbind => device_del(). Isn't the above a bug in the driver, not missing synchronization in kernfs? Forgive me if the unbind question was asked and answered elsewhere, this is my first time taking a look at this series.
Luis Chamberlain
maintainerRe: [PATCH v7 09/12] sysfs: fix deadlock race with module removal
Quoted 17 lines · Dan Williams
Quoted 7 lines · Luis Chamberlain
This deadlock was first reported with the zram driver, however the live patching folks have acknowledged they have observed this as well with live patching, when a live patch is removed. I was then able to reproduce easily by creating a dedicated selftests.
A sketch of how this can happen follows:
Quoted 8 lines
CPU A CPU B
whatever_store()module_unload
mutex_lock(foo)
mutex_lock(foo)
del_gendisk(zram->disk);
device_del()
device_remove_groups()
Quoted 5 lines
This flow seems possible to trigger with:
echo $dev >
/sys/bus/$bus/drivers/$driver/unbindI am missing why module pinning
The aspect of try_module_get() which comes to value to prevent the deadlock is it ensures kernfs ops do not run once exit is on the way.
Quoted 2 lines
is part of the solution when it's the
device_del()path that is racing?
But its not, the device_del() path will yield until the kernfs op completes. It is fine to wait there.
The deadlock happens if a module exit routine uses a lock which is also used on a sysfs op. If the lock was first held by module exit, and module exit is waiting for the kernfs op to complete, and the kernfs op is waiting to hold the same lock then the exit will wait forever.
Quoted 2 lines
Module removal is just a more coarse grained way to trigger unbind =>
device_del().
Right, but the device_del() path is not sharing a lock with the sysfs op.
Quoted 2 lines
Isn't the above a bug
in the driver, not missing synchronization in kernfs?
We can certainly take the position as an alternative:
"thou shalt not use a lock on exit which is also used on a syfs op"
However that seems counter intuitive, specially if we can resolve the issue easily with a try_module_get().
Luis
Re: [PATCH v7 09/12] sysfs: fix deadlock race with module removal
Quoted 40 lines · Luis Chamberlain
Quoted 17 lines · Dan Williams
Quoted 7 lines · Luis Chamberlain
This deadlock was first reported with the zram driver, however the live patching folks have acknowledged they have observed this as well with live patching, when a live patch is removed. I was then able to reproduce easily by creating a dedicated selftests.
A sketch of how this can happen follows:
Quoted 8 lines
CPU A CPU B
whatever_store()module_unload
mutex_lock(foo)
mutex_lock(foo)
del_gendisk(zram->disk);
device_del()
device_remove_groups()Quoted 5 lines
This flow seems possible to trigger with:
echo $dev >
/sys/bus/$bus/drivers/$driver/unbindI am missing why module pinning
The aspect of
try_module_get()which comes to value to prevent the deadlock is it ensures kernfs ops do not run once exit is on the way.Quoted 2 lines
is part of the solution when it's the
device_del()path that is racing?But its not, the
device_del()path will yield until the kernfs op completes. It is fine to wait there.The deadlock happens if a module exit routine uses a lock which is also used on a sysfs op. If the lock was first held by module exit, and module exit is waiting for the kernfs op to complete, and the kernfs op is waiting to hold the same lock then the exit will wait forever.
Quoted 4 lines
Quoted 2 lines
Module removal is just a more coarse grained way to trigger unbind =>
device_del().Right, but the
device_del()path is not sharing a lock with the sysfs op.
The deadlock in the example comes from holding a lock over device_del() that is also taken in a kernfs op. For example, the code above looks like something that runs from driver.remove(), not exclusively module_exit(). Yes, module_exit() may trigger driver.remove() via driver_unregister(), but there's other ways to trigger driver.remove() that do not involve module_exit().
Quoted 9 lines
Quoted 2 lines
Isn't the above a bug
in the driver, not missing synchronization in kernfs?We can certainly take the position as an alternative:
"thou shalt not use a lock on exit which is also used on a syfs op"
However that seems counter intuitive, specially if we can resolve the issue easily with a
try_module_get().
Again, I don't see how try_module_get() can affect the ABBA failure case of holding a lock over device_del() that is also held inside sysfs op. I agree that the problem is subtle. Does lockdep not complain about this case? If it's going to be avoided in the core it seems try_module_get() does not completely cover the hole that unsuspecting driver writers might fall into.
Luis Chamberlain
maintainerRe: [PATCH v7 09/12] sysfs: fix deadlock race with module removal
Quoted 41 lines · Dan Williams
Quoted 40 lines · Luis Chamberlain
Quoted 17 lines · Dan Williams
Quoted 7 lines · Luis Chamberlain
This deadlock was first reported with the zram driver, however the live patching folks have acknowledged they have observed this as well with live patching, when a live patch is removed. I was then able to reproduce easily by creating a dedicated selftests.
A sketch of how this can happen follows:
Quoted 8 lines
CPU A CPU B
whatever_store()module_unload
mutex_lock(foo)
mutex_lock(foo)
del_gendisk(zram->disk);
device_del()
device_remove_groups()Quoted 5 lines
This flow seems possible to trigger with:
echo $dev >
/sys/bus/$bus/drivers/$driver/unbindI am missing why module pinning
The aspect of
try_module_get()which comes to value to prevent the deadlock is it ensures kernfs ops do not run once exit is on the way.Quoted 2 lines
is part of the solution when it's the
device_del()path that is racing?But its not, the
device_del()path will yield until the kernfs op completes. It is fine to wait there.The deadlock happens if a module exit routine uses a lock which is also used on a sysfs op. If the lock was first held by module exit, and module exit is waiting for the kernfs op to complete, and the kernfs op is waiting to hold the same lock then the exit will wait forever.
Quoted 7 lines
Quoted 4 lines
Quoted 2 lines
Module removal is just a more coarse grained way to trigger unbind =>
device_del().Right, but the
device_del()path is not sharing a lock with the sysfs op.The deadlock in the example comes from holding a lock over
device_del()[...]
No sorry, that is my mistake not making it clear that the mutex held in the example is on module exit. Or any lock for that matter. That is these locks are driver specific.
Quoted 5 lines
Quoted 5 lines
Quoted 2 lines
Isn't the above a bug
in the driver, not missing synchronization in kernfs?We can certainly take the position as an alternative:
Quoted 8 lines
Quoted 4 lines
"thou shalt not use a lock on exit which is also used on a syfs op"
However that seems counter intuitive, specially if we can resolve the issue easily with a
try_module_get().Again, I don't see how
try_module_get()can affect the ABBA failure case of holding a lock overdevice_del()that is also held inside sysfs op.
It is not device_del(), it is on module exit. Sorry for this not being clear before. I'll fix the commit log to make it clearer. The subject at least was clear but I think the example could be clearer.
Luis
Re: [PATCH v7 09/12] sysfs: fix deadlock race with module removal
In reply to Luis ChamberlainQuoted 11 lines · Luis Chamberlain
A sketch of how this can happen follows:
CPU A CPU B
whatever_store()module_unload
mutex_lock(foo)mutex_lock(foo) del_gendisk(zram->disk); device_del() device_remove_groups()
Quoted 4 lines
In this situation
whatever_store()is waiting for the mutex foo to become unlocked, but that won't happen until module removal is complete. But module removal won't complete until the sysfs file being poked completes which is waiting for a lock already held.
If I remember correctly I encountered the deadlock scenario described above for the first time about ten years ago while working on the SCST project. We solved this deadlock by removing the sysfs attributes from the module unload code before grabbing mutex_lock(foo), e.g. by calling sysfs_remove_file(). This works because calling sysfs_remove_file() multiple times in a row is safe. Is that solution good enough for the zram driver?
Thanks,
Bart.
Luis Chamberlain
maintainerRe: [PATCH v7 09/12] sysfs: fix deadlock race with module removal
Quoted 17 lines · Bart Van Assche
Quoted 11 lines · Luis Chamberlain
A sketch of how this can happen follows:
CPU A CPU B
whatever_store()module_unload
mutex_lock(foo)mutex_lock(foo) del_gendisk(zram->disk); device_del() device_remove_groups()Quoted 4 lines
In this situation
whatever_store()is waiting for the mutex foo to become unlocked, but that won't happen until module removal is complete. But module removal won't complete until the sysfs file being poked completes which is waiting for a lock already held.
Quoted 5 lines
If I remember correctly I encountered the deadlock scenario described above for the first time about ten years ago while working on the SCST project. We solved this deadlock by removing the sysfs attributes from the module unload code before grabbing
mutex_lock(foo), e.g. by callingsysfs_remove_file().
Well the sysfs attributes in zram do tons of funky mucking around so unfortunately no. It's not the only driver where this can happen. It is why I decided to work on a generic solution instead.
Quoted 3 lines
This works because calling
sysfs_remove_file()
multiple times in a row is safe. Is that solution good enough for the zram driver?
The sysfs attributes are group attributes part of the block, and so are removed for the driver on a del_gendisk(). So unfortunately no, this would not be a good solution in this case.
Luis
Luis Chamberlain
maintainerPATCH v7 10/12 test_sysfs: enable deadlock tests by default
In reply to a message not in the archiveNow that sysfs has the deadlock race fixed with module removal, enable the deadlock tests module removal tests. They were left disabled by default as otherwise you would deadlock your system
./tools/testing/selftests/sysfs/sysfs.sh -t 0027 Running test: sysfs_test_0027 - run #0
Test for possible rmmod deadlock while writing x ... ok
./tools/testing/selftests/sysfs/sysfs.sh -t 0028
Running test: sysfs_test_0028 - run #0
Test for possible rmmod deadlock using rtnl_lock while writing x ... ok
--- tools/testing/selftests/sysfs/sysfs.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
tools/testing/selftests/sysfs/sysfs.sh +2 -2
@@ -60,8 +60,8 @@ ALL_TESTS="$ALL_TESTS 0023:1:1:test_dev_y:block" ALL_TESTS="$ALL_TESTS 0024:1:1:test_dev_x:block" ALL_TESTS="$ALL_TESTS 0025:1:1:test_dev_y:block" ALL_TESTS="$ALL_TESTS 0026:1:1:test_dev_y:block" -ALL_TESTS="$ALL_TESTS 0027:1:0:test_dev_x:block" # deadlock test -ALL_TESTS="$ALL_TESTS 0028:1:0:test_dev_x:block" # deadlock test with rntl_lock +ALL_TESTS="$ALL_TESTS 0027:1:1:test_dev_x:block" # deadlock test +ALL_TESTS="$ALL_TESTS 0028:1:1:test_dev_x:block" # deadlock test with rntl_lock ALL_TESTS="$ALL_TESTS 0029:1:1:test_dev_x:block" # kernfs race removal of store ALL_TESTS="$ALL_TESTS 0030:1:1:test_dev_x:block" # kernfs race removal before mutex ALL_TESTS="$ALL_TESTS 0031:1:1:test_dev_x:block" # kernfs race removal after mutex
Signature · 1 line
2.30.2
Luis Chamberlain
maintainerPATCH v7 11/12 zram: fix crashes with cpu hotplug multistate
In reply to a message not in the archiveProvide a simple state machine to fix races with driver exit where we remove the CPU multistate callbacks and re-initialization / creation of new per CPU instances which should be managed by these callbacks.
The zram driver makes use of cpu hotplug multistate support, whereby it associates a struct zcomp per CPU. Each struct zcomp represents a compression algorithm in charge of managing compression streams per CPU. Although a compiled zram driver only supports a fixed set of compression algorithms, each zram device gets a struct zcomp allocated per CPU. The "multi" in CPU hotplug multstate refers to these per cpu struct zcomp instances. Each of these will have the CPU hotplug callback called for it on CPU plug / unplug. The kernel's CPU hotplug multistate keeps a linked list of these different structures so that it will iterate over them on CPU transitions.
By default at driver initialization we will create just one zram device (num_devices=1) and a zcomp structure then set for the now default lzo-rle comrpession algorithm. At driver removal we first remove each zram device, and so we destroy the associated struct zcomp per CPU. But since we expose sysfs attributes to create new devices or reset / initialize existing zram devices, we can easily end up re-initializing a struct zcomp for a zram device before the exit routine of the module removes the cpu hotplug callback. When this happens the kernel's CPU hotplug will detect that at least one instance (struct zcomp for us) exists. This can happen in the following situation:
CPU 1 CPU 2
disksize_store(...);
class_unregister(...);idr_for_each(...);zram_debugfs_destroy();
idr_destroy(...);unregister_blkdev(...);cpuhp_remove_multi_state(...);
The warning comes up on cpuhp_remove_multi_state() when it sees that the state for CPUHP_ZCOMP_PREPARE does not have an empty instance linked list. In this case, that a struct zcom still exists, the driver allowed its creation per CPU even though we could have just freed them per CPU though a call on another CPU, and we are then later trying to remove the hotplug callback.
Fix all this by providing a zram initialization boolean protected the shared in the driver zram_index_mutex, which we can use to annotate when sysfs attributes are safe to use or not -- once the driver is properly initialized. When the driver is going down we also are sure to not let userspace muck with attributes which may affect each per cpu struct zcomp.
This also fixes a series of possible memory leaks. The crashes and memory leaks can easily be caused by issuing the zram02.sh script from the LTP project [0] in a loop in two separate windows:
cd testcases/kernel/device-drivers/zram
while true; do PATH=$PATH:$PWD:$PWD/../../../lib/ ./zram02.sh; done
You end up with a splat as follows:
kernel: zram: Removed device: zram0
kernel: zram: Added device: zram0
kernel: zram0: detected capacity change from 0 to 209715200
kernel: Adding 104857596k swap on /dev/zram0. <etc>
kernel: zram0: detected capacitky change from 209715200 to 0
kernel: zram0: detected capacity change from 0 to 209715200
kernel: ------------[ cut here ]------------
kernel: Error: Removing state 63 which has instances left.
kernel: WARNING: CPU: 7 PID: 70457 at \
kernel/cpu.c:2069 __cpuhp_remove_state_cpuslocked+0xf9/0x100 kernel: Modules linked in: zram(E-) zsmalloc(E) <etc> kernel: CPU: 7 PID: 70457 Comm: rmmod Tainted: G \ E 5.12.0-rc1-next-20210304 #3 kernel: Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), \ BIOS 1.14.0-2 04/01/2014
kernel: RIP: 0010:__cpuhp_remove_state_cpuslocked+0xf9/0x100
kernel: Code: <etc>
kernel: RSP: 0018:ffffa800c139be98 EFLAGS: 00010282
kernel: RAX: 0000000000000000 RBX: ffffffff9083db58 RCX: ffff9609f7dd86d8
kernel: RDX: 00000000ffffffd8 RSI: 0000000000000027 RDI: ffff9609f7dd86d0
kernel: RBP: 0000000000000000i R08: 0000000000000000 R09: ffffa800c139bcb8
kernel: R10: ffffa800c139bcb0 R11: ffffffff908bea40 R12: 000000000000003f
kernel: R13: 00000000000009d8 R14: 0000000000000000 R15: 0000000000000000
kernel: FS: 00007f1b075a7540(0000) GS:ffff9609f7dc0000(0000) knlGS:<etc>
kernel: CS: 0010 DS: 0000 ES 0000 CR0: 0000000080050033
kernel: CR2: 00007f1b07610490 CR3: 00000001bd04e000 CR4: 0000000000350ee0
kernel: Call Trace:
kernel: __cpuhp_remove_state+0x2e/0x80
kernel: __do_sys_delete_module+0x190/0x2a0
kernel: do_syscall_64+0x33/0x80
kernel: entry_SYSCALL_64_after_hwframe+0x44/0xae
The "Error: Removing state 63 which has instances left" refers to the zram per CPU struct zcomp instances left.
[0] https://github.com/linux-test-project/ltp.git
--- drivers/block/zram/zram_drv.c | 63 ++++++++++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 8 deletions(-)
drivers/block/zram/zram_drv.c +55 -8
@@ -44,6 +44,8 @@ static DEFINE_MUTEX(zram_index_mutex);static int zram_major;static const char *default_compressor =CONFIG_ZRAM_DEF_COMP ; +static bool zram_up; +/* Module params (documentation at end) */ static unsigned int num_devices =1 ;/*
@@ -1704,6 +1706,7 @@ static void zram_reset_device(struct zram *zram) comp = zram->comp; disksize = zram->disksize; zram->disksize =0 ; + zram->comp =NULL ;set_capacity_and_notify (zram->disk,0 );part_stat_set_all (zram->disk->part0,0 );
@@ -1724,9 +1727,18 @@ static ssize_t disksize_store(struct device *dev,struct zram *zram =dev_to_zram (dev);int err; +mutex_lock (&zram_index_mutex); + +if (!zram_up) { + err = -ENODEV ; + goto out; + } + disksize =memparse (buf,NULL ); -if (!disksize) -return -EINVAL ; +if (!disksize) { + err = -EINVAL ; + goto out; + }down_write (&zram->init_lock);if (init_done (zram)) {
@@ -1754,12 +1766,16 @@ static ssize_t disksize_store(struct device *dev,set_capacity_and_notify (zram->disk, zram->disksize >>SECTOR_SHIFT );up_write (&zram->init_lock); +mutex_unlock (&zram_index_mutex); +return len; out_free_meta:zram_meta_free (zram, disksize); out_unlock:up_write (&zram->init_lock); +out: +mutex_unlock (&zram_index_mutex);return err; }
@@ -1775,8 +1791,17 @@ static ssize_t reset_store(struct device *dev,if (ret)return ret; -if (!do_reset) -return -EINVAL ; +mutex_lock (&zram_index_mutex); + +if (!zram_up) { + len = -ENODEV ; + goto out; + } + +if (!do_reset) { + len = -EINVAL ; + goto out; + } zram =dev_to_zram (dev); bdev = zram->disk->part0;
@@ -1785,7 +1810,8 @@ static ssize_t reset_store(struct device *dev,/* Do not reset an active device or claimed device */ if (bdev->bd_openers || zram->claim) {mutex_unlock (&bdev->bd_disk->open_mutex); -return -EBUSY ; + len = -EBUSY ; + goto out; }/* From now on, anyone can't open /dev/zram[0-9] */
@@ -1800,6 +1826,8 @@ static ssize_t reset_store(struct device *dev, zram->claim = false;mutex_unlock (&bdev->bd_disk->open_mutex); +out: +mutex_unlock (&zram_index_mutex);return len; }
@@ -2010,6 +2038,10 @@ static ssize_t hot_add_show(struct class *class,int ret;mutex_lock (&zram_index_mutex); +if (!zram_up) { +mutex_unlock (&zram_index_mutex); +return -ENODEV ; + } ret =zram_add ();mutex_unlock (&zram_index_mutex);
@@ -2037,6 +2069,11 @@ static ssize_t hot_remove_store(struct class *class,mutex_lock (&zram_index_mutex); +if (!zram_up) { + ret = -ENODEV ; + goto out; + } + zram =idr_find (&zram_index_idr, dev_id);if (zram) { ret =zram_remove (zram);
@@ -2046,6 +2083,7 @@ static ssize_t hot_remove_store(struct class *class, ret = -ENODEV ; } +out:mutex_unlock (&zram_index_mutex);return ret ? ret : count; }
@@ -2072,12 +2110,15 @@ static int zram_remove_cb(int id, void *ptr, void *data)static void destroy_devices (void ) { +mutex_lock (&zram_index_mutex); + zram_up = false;class_unregister (&zram_control_class);idr_for_each (&zram_index_idr, &zram_remove_cb,NULL );zram_debugfs_destroy ();idr_destroy (&zram_index_idr);unregister_blkdev (zram_major,"zram" );cpuhp_remove_multi_state (CPUHP_ZCOMP_PREPARE ); +mutex_unlock (&zram_index_mutex); }static int __init zram_init (void )
@@ -2105,15 +2146,21 @@ static int __init zram_init(void)return -EBUSY ; } +mutex_lock (&zram_index_mutex); +while (num_devices !=0 ) { -mutex_lock (&zram_index_mutex); ret =zram_add (); -mutex_unlock (&zram_index_mutex); -if (ret <0 ) +if (ret <0 ) { +mutex_unlock (&zram_index_mutex); goto out_error; + } num_devices--; } + zram_up = true; + +mutex_unlock (&zram_index_mutex); +return 0 ; out_error:
Signature · 1 line
2.30.2
Luis Chamberlain
maintainerPATCH v7 12/12 zram: use ATTRIBUTE_GROUPS to fix sysfs deadlock module removal
In reply to a message not in the archiveThe ATTRIBUTE_GROUPS is typically used to avoid boiler plate code which is used in many drivers. Embracing ATTRIBUTE_GROUPS was long due on the zram driver, however a recent fix for sysfs allows users of ATTRIBUTE_GROUPS to also associate a module to the group attribute.
In zram's case this also means it allows us to fix a race which triggers a deadlock on the zram driver. This deadlock happens when a sysfs attribute use a lock also used on module removal. This happens when for instance a sysfs file on a driver is used, then at the same time we have module removal call trigger. The module removal call code holds a lock, and then the sysfs file entry waits for the same lock. While holding the lock the module removal tries to remove the sysfs entries, but these cannot be removed yet as one is waiting for a lock. This won't complete as the lock is already held. Likewise module removal cannot complete, and so we deadlock.
Sysfs fixes this when the group attributes have a module associated to it, sysfs will *try* to get a refcount to the module when a shared lock is used, prior to mucking with a sysfs attribute. If this fails we just give up right away.
This deadlock was first reported with the zram driver, a sketch of how this can happen follows:
CPU A CPU B
whatever_store()module_unload
mutex_lock(foo)
mutex_lock(foo)
del_gendisk(zram->disk);
device_del()
device_remove_groups()
In this situation whatever_store() is waiting for the mutex foo to become unlocked, but that won't happen until module removal is complete. But module removal won't complete until the sysfs file being poked completes which is waiting for a lock already held.
This issue can be reproduced easily on the zram driver as follows:
Loop 1 on one terminal:
while true; do modprobe zram; modprobe -r zram;
done
Loop 2 on a second terminal:
while true; do
echo 1024 > /sys/block/zram0/disksize; echo 1 > /sys/block/zram0/reset;
done
Without this patch we end up in a deadlock, and the following stack trace is produced which hints to us what the issue was:
INFO: task bash:888 blocked for more than 120 seconds.
Tainted: G E 5.12.0-rc1-next-20210304+ #4
"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
task:bash state:D stack: 0 pid: 888 ppid: 887 flags:<etc>
Call Trace:
__schedule+0x2e4/0x900
schedule+0x46/0xb0
schedule_preempt_disabled+0xa/0x10
__mutex_lock.constprop.0+0x2c3/0x490
? _kstrtoull+0x35/0xd0
reset_store+0x6c/0x160 [zram]
kernfs_fop_write_iter+0x124/0x1b0
new_sync_write+0x11c/0x1b0
vfs_write+0x1c2/0x260
ksys_write+0x5f/0xe0
do_syscall_64+0x33/0x80
entry_SYSCALL_64_after_hwframe+0x44/0xae
RIP: 0033:0x7f34f2c3df33
RSP: 002b:00007ffe751df6e8 EFLAGS: 00000246 ORIG_RAX: 0000000000000001
RAX: ffffffffffffffda RBX: 0000000000000002 RCX: 00007f34f2c3df33
RDX: 0000000000000002 RSI: 0000561ccb06ec10 RDI: 0000000000000001
RBP: 0000561ccb06ec10 R08: 000000000000000a R09: 0000000000000001
R10: 0000561ccb157590 R11: 0000000000000246 R12: 0000000000000002
R13: 00007f34f2d0e6a0 R14: 0000000000000002 R15: 00007f34f2d0e8a0
INFO: task modprobe:1104 can't die for more than 120 seconds.
task:modprobe state:D stack: 0 pid: 1104 ppid: 916 flags:<etc>
Call Trace:
__schedule+0x2e4/0x900
schedule+0x46/0xb0
__kernfs_remove.part.0+0x228/0x2b0
? finish_wait+0x80/0x80
kernfs_remove_by_name_ns+0x50/0x90
remove_files+0x2b/0x60
sysfs_remove_group+0x38/0x80
sysfs_remove_groups+0x29/0x40
device_remove_attrs+0x4a/0x80
device_del+0x183/0x3e0
? mutex_lock+0xe/0x30
del_gendisk+0x27a/0x2d0
zram_remove+0x8a/0xb0 [zram]
? hot_remove_store+0xf0/0xf0 [zram]
zram_remove_cb+0xd/0x10 [zram]
idr_for_each+0x5e/0xd0
destroy_devices+0x39/0x6f [zram]
__do_sys_delete_module+0x190/0x2a0
do_syscall_64+0x33/0x80
entry_SYSCALL_64_after_hwframe+0x44/0xae
RIP: 0033:0x7f32adf727d7
RSP: 002b:00007ffc08bb38a8 EFLAGS: 00000206 ORIG_RAX: 00000000000000b0
RAX: ffffffffffffffda RBX: 000055eea23cbb10 RCX: 00007f32adf727d7
RDX: 0000000000000000 RSI: 0000000000000800 RDI: 000055eea23cbb78
RBP: 000055eea23cbb10 R08: 0000000000000000 R09: 0000000000000000
R10: 00007f32adfe5ac0 R11: 0000000000000206 R12: 000055eea23cbb78
R13: 0000000000000000 R14: 0000000000000000 R15: 000055eea23cbc20[0] https://lkml.kernel.org/r/[email protected]
--- drivers/block/zram/zram_drv.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-)
drivers/block/zram/zram_drv.c +2 -9
@@ -1902,14 +1902,7 @@ static struct attribute *zram_disk_attrs[] = { NULL, }; -static const struct attribute_group zram_disk_attr_group = { - .attrs = zram_disk_attrs, -}; - -static const struct attribute_group *zram_disk_attr_groups[] = { - &zram_disk_attr_group, -NULL , -}; +ATTRIBUTE_GROUPS (zram_disk);/* * Allocate and initialize new zram device. the function returns
@@ -1981,7 +1974,7 @@ static int zram_add(void)blk_queue_max_write_zeroes_sectors (zram->disk->queue,UINT_MAX );blk_queue_flag_set (QUEUE_FLAG_STABLE_WRITES , zram->disk->queue); -device_add_disk (NULL , zram->disk, zram_disk_attr_groups); +device_add_disk (NULL , zram->disk, zram_disk_groups);strlcpy (zram->compressor, default_compressor,sizeof (zram->compressor));
Signature · 1 line
2.30.2
Tejun Heo
maintainerRe: [PATCH v7 00/12 (RESEND)] syfs: generic deadlock fix with module removal
In reply to a message not in the archiveQuoted 5 lines · Luis Chamberlain
In this v7 I've decided it is best to merge all the effort together into one patch set because communication was being lost when I split the patches up. This was not helping in any way to either fix the zram issues or come to consensus on a generic solution. The patches are also merged now because they are all related now.
Building up all the testing framewoork is really great. I have no opinions about the license related stuff but all other changes generally look good to me.
Thanks.
Signature · 1 line
tejun
Luis Chamberlain
maintainerRe: [PATCH v7 00/12 (RESEND)] syfs: generic deadlock fix with module removal
Quoted 10 lines · Tejun Heo
Quoted 5 lines · Luis Chamberlain
In this v7 I've decided it is best to merge all the effort together into one patch set because communication was being lost when I split the patches up. This was not helping in any way to either fix the zram issues or come to consensus on a generic solution. The patches are also merged now because they are all related now.
Building up all the testing framewoork is really great. I have no opinions about the license related stuff but all other changes generally look good to me.
OK I am going to send a v8 shortly with the changes from folks addressed. I'm going to trim the Cc list considerbly for that v8 as the list is already quite large and I think it may appear as spam to some lists, I'll drop the copyleft-next folks as hopefully the license stuff is out of the way.
Luis