Commit c99f66e4 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Jens Axboe
Browse files

block: fix queue freeze vs limits lock order in sysfs store methods



queue_attr_store() always freezes a device queue before calling the
attribute store operation. For attributes that control queue limits, the
store operation will also lock the queue limits with a call to
queue_limits_start_update(). However, some drivers (e.g. SCSI sd) may
need to issue commands to a device to obtain limit values from the
hardware with the queue limits locked. This creates a potential ABBA
deadlock situation if a user attempts to modify a limit (thus freezing
the device queue) while the device driver starts a revalidation of the
device queue limits.

Avoid such deadlock by not freezing the queue before calling the
->store_limit() method in struct queue_sysfs_entry and instead use the
queue_limits_commit_update_frozen helper to freeze the queue after taking
the limits lock.

This also removes taking the sysfs lock for the store_limit method as
it doesn't protect anything here, but creates even more nesting.
Hopefully it will go away from the actual sysfs methods entirely soon.

(commit log adapted from a similar patch from  Damien Le Moal)

Fixes: ff956a3b ("block: use queue_limits_commit_update in queue_discard_max_store")
Fixes: 0327ca9d ("block: use queue_limits_commit_update in queue_max_sectors_store")
Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Reviewed-by: default avatarMing Lei <ming.lei@redhat.com>
Reviewed-by: default avatarDamien Le Moal <dlemoal@kernel.org>
Reviewed-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
Reviewed-by: default avatarNilay Shroff <nilay@linux.ibm.com>
Reviewed-by: default avatarJohannes Thumshirn <johannes.thumshirn@wdc.com>
Link: https://lore.kernel.org/r/20250110054726.1499538-7-hch@lst.de


Signed-off-by: default avatarJens Axboe <axboe@kernel.dk>
parent a1623064
Loading
Loading
Loading
Loading
+10 −8
Original line number Diff line number Diff line
@@ -694,22 +694,24 @@ queue_attr_store(struct kobject *kobj, struct attribute *attr,
	if (entry->load_module)
		entry->load_module(disk, page, length);

	mutex_lock(&q->sysfs_lock);
	blk_mq_freeze_queue(q);
	if (entry->store_limit) {
		struct queue_limits lim = queue_limits_start_update(q);

		res = entry->store_limit(disk, page, length, &lim);
		if (res < 0) {
			queue_limits_cancel_update(q);
		} else {
			res = queue_limits_commit_update(q, &lim);
			if (!res)
				res = length;
			return res;
		}
	} else {
		res = entry->store(disk, page, length);

		res = queue_limits_commit_update_frozen(q, &lim);
		if (res)
			return res;
		return length;
	}

	mutex_lock(&q->sysfs_lock);
	blk_mq_freeze_queue(q);
	res = entry->store(disk, page, length);
	blk_mq_unfreeze_queue(q);
	mutex_unlock(&q->sysfs_lock);
	return res;