FAILED: patch "[PATCH] loop: make autoclear operation asynchronous" failed to apply to 5.15-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 5.15-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 322c4293ecc58110227b49d7e47ae37b9b03566f Mon Sep 17 00:00:00 2001
From: Tetsuo Handa <[email protected]>
Date: Mon, 13 Dec 2021 21:55:27 +0900
Subject: [PATCH] loop: make autoclear operation asynchronous
syzbot is reporting circular locking problem at __loop_clr_fd() [1], for commit 87579e9b7d8dc36e ("loop: use worker per cgroup instead of kworker") is calling destroy_workqueue() with disk->open_mutex held.
This circular dependency cannot be broken unless we call __loop_clr_fd() without holding disk->open_mutex. Therefore, defer __loop_clr_fd() from lo_release() to a WQ context.
drivers/block/loop.c +36 -29
@@ -1082,7 +1082,7 @@ static int loop_configure(struct loop_device *lo, fmode_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;
@@ -1144,8 +1144,6 @@ static void __loop_clr_fd(struct loop_device *lo, bool release)/* let user-space know about this change */ kobject_uevent (&disk_to_dev (lo->lo_disk)->kobj,KOBJ_CHANGE );mapping_set_gfp_mask (filp->f_mapping, gfp); -/* This is safe: open() is still holding a reference. */ -module_put (THIS_MODULE );blk_mq_unfreeze_queue (lo->lo_queue);disk_force_media_change (lo->lo_disk,DISK_EVENT_MEDIA_CHANGE );
@@ -1153,44 +1151,52 @@ static void __loop_clr_fd(struct loop_device *lo, bool release)if (lo->lo_flags &LO_FLAGS_PARTSCAN ) {int err; -/* -* open_mutex has been held already in release path, so don't -* acquire it if this function is called in such case. -* -* If the reread partition isn't from release path, lo_refcnt -* 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); +mutex_lock (&lo->lo_disk->open_mutex); err =bdev_disk_changed (lo->lo_disk, false); -if (!release) -mutex_unlock (&lo->lo_disk->open_mutex); +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);/* Device is gone, no point in returning error */ } -/* -* lo->lo_state is set to Lo_unbound here after above partscan has -* finished. There cannot be anybody else entering __loop_clr_fd() as -* Lo_rundown state protects us from all the other places trying to -* change the 'lo' device. -*/ lo->lo_flags =0 ;if (!part_shift) lo->lo_disk->flags |=GENHD_FL_NO_PART ; + +fput (filp); +} + +static void loop_rundown_completed (struct loop_device *lo) +{mutex_lock (&lo->lo_mutex); lo->lo_state = Lo_unbound;mutex_unlock (&lo->lo_mutex); +module_put (THIS_MODULE ); +} -/* -* Need not hold lo_mutex to fput backing file. Calling fput holding -* lo_mutex triggers a circular lock dependency possibility warning as -* fput can take open_mutex which is usually taken before lo_mutex. -*/ -fput (filp); +static void loop_rundown_workfn (struct work_struct *work) +{ + struct loop_device *lo = container_of(work, struct loop_device, + rundown_work); +struct block_device *bdev = lo->lo_device; +struct gendisk *disk = lo->lo_disk; + +__loop_clr_fd (lo); +kobject_put (&bdev->bd_device.kobj); +module_put (disk->fops->owner); +loop_rundown_completed (lo); +} + +static void loop_schedule_rundown (struct loop_device *lo) +{ +struct block_device *bdev = lo->lo_device; +struct gendisk *disk = lo->lo_disk; + +__module_get (disk->fops->owner); +kobject_get (&bdev->bd_device.kobj); +INIT_WORK (&lo->rundown_work, loop_rundown_workfn); +queue_work (system_long_wq, &lo->rundown_work); }static int loop_clr_fd (struct loop_device *lo)
@@ -1222,7 +1228,8 @@ static int loop_clr_fd(struct loop_device *lo) lo->lo_state = Lo_rundown;mutex_unlock (&lo->lo_mutex); -__loop_clr_fd (lo, false); +__loop_clr_fd (lo); +loop_rundown_completed (lo);return 0 ; }
@@ -1747,7 +1754,7 @@ static void lo_release(struct gendisk *disk, fmode_t mode)* In autoclear mode, stop the loop thread * and remove configuration after last close. */ -__loop_clr_fd (lo, true); +loop_schedule_rundown (lo);return ; }else if (lo->lo_state == Lo_bound) {/*
drivers/block/loop.h +1 -0
@@ -56,6 +56,7 @@ struct loop_device {struct gendisk *lo_disk;struct mutex lo_mutex;bool idr_visible; +struct work_struct rundown_work; };struct loop_cmd {