RE: [PATCH V3 for-6.10/block] loop: Fix a race between loop detach and loop open

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi ,

> -----Original Message-----
> From: Christoph Hellwig <hch@xxxxxx>
> Sent: Friday, May 31, 2024 5:27 PM
> To: Gulam Mohamed <gulam.mohamed@xxxxxxxxxx>
> Cc: linux-block@xxxxxxxxxxxxxxx; linux-kernel@xxxxxxxxxxxxxxx;
> yukuai1@xxxxxxxxxxxxxxx; hch@xxxxxx; axboe@xxxxxxxxx
> Subject: Re: [PATCH V3 for-6.10/block] loop: Fix a race between loop detach
> and loop open
> 
> On Wed, May 29, 2024 at 08:02:40PM +0000, Gulam Mohamed wrote:
> > Test case involves the following two scripts:
> >
> > script1.sh:
> >
> > while [ 1 ];
> > do
> >         losetup -P -f /home/opt/looptest/test10.img
> >         blkid /dev/loop0p1
> > done
> >
> > script2.sh:
> >
> > while [ 1 ];
> > do
> >         losetup -d /dev/loop0
> > done
> 
> When just running these in the background, I just get spam of losetup errors.
> Maybe you can turn this into a proper blktests test case with a sensible
> timeout?
> 
Yes, the blktests test case for this is already in its final stage of the review

> A for the fix: I suspect it is bette to simply always defer the actual work of
> disconnecting the backing device, as that avoid the race entirely, and
> simplifies the code in nbd by removing special cases.  Something like this:
I agree. This looks to be a good solution. Will implement this and send V4 for review

Thanks & Regards,
Gulam Mohamed.
> 
> 
> diff --git a/drivers/block/loop.c b/drivers/block/loop.c index
> 93780f41646b75..c2238c1e2ad68d 100644
> --- a/drivers/block/loop.c
> +++ b/drivers/block/loop.c
> @@ -1131,7 +1131,7 @@ static int loop_configure(struct loop_device *lo,
> blk_mode_t mode,
>  	return error;
>  }
> 
> -static void __loop_clr_fd(struct loop_device *lo, bool release)
> +static void __loop_clr_fd(struct loop_device *lo)
>  {
>  	struct file *filp;
>  	gfp_t gfp = lo->old_gfp_mask;
> @@ -1139,14 +1139,6 @@ static void __loop_clr_fd(struct loop_device *lo,
> bool release)
>  	if (test_bit(QUEUE_FLAG_WC, &lo->lo_queue->queue_flags))
>  		blk_queue_write_cache(lo->lo_queue, false, false);
> 
> -	/*
> -	 * Freeze the request queue when unbinding on a live file descriptor
> and
> -	 * thus an open device.  When called from ->release we are
> guaranteed
> -	 * that there is no I/O in progress already.
> -	 */
> -	if (!release)
> -		blk_mq_freeze_queue(lo->lo_queue);
> -
>  	spin_lock_irq(&lo->lo_lock);
>  	filp = lo->lo_backing_file;
>  	lo->lo_backing_file = NULL;
> @@ -1164,8 +1156,6 @@ static void __loop_clr_fd(struct loop_device *lo,
> bool release)
>  	mapping_set_gfp_mask(filp->f_mapping, gfp);
>  	/* This is safe: open() is still holding a reference. */
>  	module_put(THIS_MODULE);
> -	if (!release)
> -		blk_mq_unfreeze_queue(lo->lo_queue);
> 
>  	disk_force_media_change(lo->lo_disk);
> 
> @@ -1180,11 +1170,7 @@ static void __loop_clr_fd(struct loop_device *lo,
> bool release)
>  		 * must be at least one and it can only become zero when the
>  		 * current holder is released.
>  		 */
> -		if (!release)
> -			mutex_lock(&lo->lo_disk->open_mutex);
>  		err = bdev_disk_changed(lo->lo_disk, false);
> -		if (!release)
> -			mutex_unlock(&lo->lo_disk->open_mutex);
>  		if (err)
>  			pr_warn("%s: partition scan of loop%d failed
> (rc=%d)\n",
>  				__func__, lo->lo_number, err);
> @@ -1232,25 +1218,16 @@ static int loop_clr_fd(struct loop_device *lo)
>  		loop_global_unlock(lo, true);
>  		return -ENXIO;
>  	}
> +
>  	/*
> -	 * If we've explicitly asked to tear down the loop device,
> -	 * and it has an elevated reference count, set it for auto-teardown
> when
> -	 * the last reference goes away. This stops $!~#$@ udev from
> -	 * preventing teardown because it decided that it needs to run blkid
> on
> -	 * the loopback device whenever they appear. xfstests is notorious for
> -	 * failing tests because blkid via udev races with a losetup
> -	 * <dev>/do something like mkfs/losetup -d <dev> causing the losetup
> -d
> -	 * command to fail with EBUSY.
> +	 * Mark the device for removing the backing device on last close.
> +	 * If we are the only opener, also switch the state to roundown here
> to
> +	 * prevent new openers from coming in.
>  	 */
> -	if (disk_openers(lo->lo_disk) > 1) {
> -		lo->lo_flags |= LO_FLAGS_AUTOCLEAR;
> -		loop_global_unlock(lo, true);
> -		return 0;
> -	}
> -	lo->lo_state = Lo_rundown;
> +	lo->lo_flags |= LO_FLAGS_AUTOCLEAR;
> +	if (disk_openers(lo->lo_disk) == 1)
> +		lo->lo_state = Lo_rundown;
>  	loop_global_unlock(lo, true);
> -
> -	__loop_clr_fd(lo, false);
>  	return 0;
>  }
> 
> @@ -1720,22 +1697,24 @@ static int lo_compat_ioctl(struct block_device
> *bdev, blk_mode_t mode,  static void lo_release(struct gendisk *disk)  {
>  	struct loop_device *lo = disk->private_data;
> +	bool need_clear;
> 
>  	if (disk_openers(disk) > 0)
>  		return;
> 
> +	/*
> +	 * Clear the backing device information if this is the last close of
> +	 * a device that's been marked for auto clear, or on which
> LOOP_CLR_FD
> +	 * has been called.
> +	 */
>  	mutex_lock(&lo->lo_mutex);
> -	if (lo->lo_state == Lo_bound && (lo->lo_flags &
> LO_FLAGS_AUTOCLEAR)) {
> +	if (lo->lo_state == Lo_bound && (lo->lo_flags &
> LO_FLAGS_AUTOCLEAR))
>  		lo->lo_state = Lo_rundown;
> -		mutex_unlock(&lo->lo_mutex);
> -		/*
> -		 * In autoclear mode, stop the loop thread
> -		 * and remove configuration after last close.
> -		 */
> -		__loop_clr_fd(lo, true);
> -		return;
> -	}
> +	need_clear = (lo->lo_state == Lo_rundown);
>  	mutex_unlock(&lo->lo_mutex);
> +
> +	if (need_clear)
> +		__loop_clr_fd(lo);
>  }
> 
>  static void lo_free_disk(struct gendisk *disk)





[Index of Archives]     [Linux RAID]     [Linux SCSI]     [Linux ATA RAID]     [IDE]     [Linux Wireless]     [Linux Kernel]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Device Mapper]

  Powered by Linux