• Mauricio Faria de Oliveira's avatar
    loop: do not enforce max_loop hard limit by (new) default · bb5faa99
    Mauricio Faria de Oliveira authored
    Problem:
    
    The max_loop parameter is used for 2 different purposes:
    
    1) initial number of loop devices to pre-create on init
    2) maximum number of loop devices to add on access/open()
    
    Historically, its default value (zero) caused 1) to create non-zero
    number of devices (CONFIG_BLK_DEV_LOOP_MIN_COUNT), and no hard limit on
    2) to add devices with autoloading.
    
    However, the default value changed in commit 85c50197 ("loop: Fix
    the max_loop commandline argument treatment when it is set to 0") to
    CONFIG_BLK_DEV_LOOP_MIN_COUNT, for max_loop=0 not to pre-create devices.
    
    That does improve 1), but unfortunately it breaks 2), as the default
    behavior changed from no-limit to hard-limit.
    
    Example:
    
    For example, this userspace code broke for N >= CONFIG, if the user
    relied on the default value 0 for max_loop:
    
        mknod("/dev/loopN");
        open("/dev/loopN");  // now fails with ENXIO
    
    Though affected users may "fix" it with (loop.)max_loop=0, this means to
    require a kernel parameter change on stable kernel update (that commit
    Fixes: an old commit in stable).
    
    Solution:
    
    The original semantics for the default value in 2) can be applied if the
    parameter is not set (ie, default behavior).
    
    This still keeps the intended function in 1) and 2) if set, and that
    commit's intended improvement in 1) if max_loop=0.
    
    Before 85c50197:
      - default:     1) CONFIG devices   2) no limit
      - max_loop=0:  1) CONFIG devices   2) no limit
      - max_loop=X:  1) X devices        2) X limit
    
    After 85c50197:
      - default:     1) CONFIG devices   2) CONFIG limit (*)
      - max_loop=0:  1) 0 devices (*)    2) no limit
      - max_loop=X:  1) X devices        2) X limit
    
    This commit:
      - default:     1) CONFIG devices   2) no limit (*)
      - max_loop=0:  1) 0 devices        2) no limit
      - max_loop=X:  1) X devices        2) X limit
    
    Future:
    
    The issue/regression from that commit only affects code under the
    CONFIG_BLOCK_LEGACY_AUTOLOAD deprecation guard, thus the fix too is
    contained under it.
    
    Once that deprecated functionality/code is removed, the purpose 2) of
    max_loop (hard limit) is no longer in use, so the module parameter
    description can be changed then.
    
    Tests:
    
    Linux 6.4-rc7
    CONFIG_BLK_DEV_LOOP_MIN_COUNT=8
    CONFIG_BLOCK_LEGACY_AUTOLOAD=y
    
    - default (original)
    
    	# ls -1 /dev/loop*
    	/dev/loop-control
    	/dev/loop0
    	...
    	/dev/loop7
    
    	# ./test-loop
    	open: /dev/loop8: No such device or address
    
    - default (patched)
    
    	# ls -1 /dev/loop*
    	/dev/loop-control
    	/dev/loop0
    	...
    	/dev/loop7
    
    	# ./test-loop
    	#
    
    - max_loop=0 (original & patched):
    
    	# ls -1 /dev/loop*
    	/dev/loop-control
    
    	# ./test-loop
    	#
    
    - max_loop=8 (original & patched):
    
    	# ls -1 /dev/loop*
    	/dev/loop-control
    	/dev/loop0
    	...
    	/dev/loop7
    
    	# ./test-loop
    	open: /dev/loop8: No such device or address
    
    - max_loop=0 (patched; CONFIG_BLOCK_LEGACY_AUTOLOAD is not set)
    
    	# ls -1 /dev/loop*
    	/dev/loop-control
    
    	# ./test-loop
    	open: /dev/loop8: No such device or address
    
    Fixes: 85c50197 ("loop: Fix the max_loop commandline argument treatment when it is set to 0")
    Signed-off-by: default avatarMauricio Faria de Oliveira <mfo@canonical.com>
    Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
    Link: https://lore.kernel.org/r/20230720143033.841001-3-mfo@canonical.comSigned-off-by: default avatarJens Axboe <axboe@kernel.dk>
    bb5faa99
loop.c 58 KB