Re: [PATCH 3/6] block: add a hard-readonly flag to struct gendisk

Ming Lei
2020-12-08T10:24:08Z

On Mon, Dec 07, 2020 at 02:19:15PM +0100, Christoph Hellwig wrote:
> Commit 20bd1d026aac ("scsi: sd: Keep disk read-only when re-reading
> partition") addressed a long-standing problem with user read-only
> policy being overridden as a result of a device-initiated revalidate.
> The commit has since been reverted due to a regression that left some
> USB devices read-only indefinitely.
>
> To fix the underlying problems with revalidate we need to keep track
> of hardware state and user policy separately.
>
> The gendisk has been updated to reflect the current hardware state set
> by the device driver. This is done to allow returning the device to
> the hardware state once the user clears the BLKROSET flag.
>
> The resulting semantics are as follows:
>
> - If BLKROSET sets a given partition read-only, that partition will
> remain read-only even if the underlying storage stack initiates a
> revalidate. However, the BLKRRPART ioctl will cause the partition
> table to be dropped and any user policy on partitions will be lost.
>
> - If BLKROSET has not been set, both the whole disk device and any
> partitions will reflect the current write-protect state of the
> underlying device.
>
> Based on a patch from Martin K. Petersen <martin.petersen@oracle.com>.
>
> Reported-by: Oleksii Kurochko <olkuroch@cisco.com>
> Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=201221
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> Reviewed-by: Hannes Reinecke <hare@suse.de>
> ---
> block/blk-core.c | 2 +-
> block/genhd.c | 33 +++++++++++++++++++--------------
> block/partitions/core.c | 3 +--
> include/linux/genhd.h | 6 ++++--
> 4 files changed, 25 insertions(+), 19 deletions(-)
>
> diff --git a/block/blk-core.c b/block/blk-core.c
> index ad041e903b0a8f..ecd68415c6acad 100644
> --- a/block/blk-core.c
> +++ b/block/blk-core.c
> @@ -693,7 +693,7 @@ static inline bool should_fail_request(struct block_device *part,
>
> static inline bool bio_check_ro(struct bio *bio)
> {
> - if (op_is_write(bio_op(bio)) && bio->bi_bdev->bd_read_only) {
> + if (op_is_write(bio_op(bio)) && bdev_read_only(bio->bi_bdev))
> char b[BDEVNAME_SIZE];
>
> if (op_is_flush(bio->bi_opf) && !bio_sectors(bio))
> diff --git a/block/genhd.c b/block/genhd.c
> index c87013879b8650..878f94727aaa96 100644
> --- a/block/genhd.c
> +++ b/block/genhd.c
> @@ -1425,27 +1425,32 @@ static void set_disk_ro_uevent(struct gendisk *gd, int ro)
> kobject_uevent_env(&disk_to_dev(gd)->kobj, KOBJ_CHANGE, envp);
> }
>
> -void set_disk_ro(struct gendisk *disk, int flag)
> +/**
> + * set_disk_ro - set a gendisk read-only
> + * @disk: gendisk to operate on
> + * @ready_only: %true to set the disk read-only, %false set the disk read/write
> + *
> + * This function is used to indicate whether a given disk device should have its
> + * read-only flag set. set_disk_ro() is typically used by device drivers to
> + * indicate whether the underlying physical device is write-protected.
> + */
> +void set_disk_ro(struct gendisk *disk, bool read_only)
> {
> - struct disk_part_iter piter;
> - struct block_device *part;
> -
> - if (disk->part0->bd_read_only != flag) {
> - set_disk_ro_uevent(disk, flag);
> - disk->part0->bd_read_only = flag;
> + if (read_only) {
> + if (test_and_set_bit(GD_READ_ONLY, &disk->state))
> + return;
> + } else {
> + if (!test_and_clear_bit(GD_READ_ONLY, &disk->state))
> + return;
> }
> -
> - disk_part_iter_init(&piter, disk, DISK_PITER_INCL_EMPTY);
> - while ((part = disk_part_iter_next(&piter)))
> - part->bd_read_only = flag;
> - disk_part_iter_exit(&piter);
> + set_disk_ro_uevent(disk, read_only);
> }
> -
> EXPORT_SYMBOL(set_disk_ro);
>
> int bdev_read_only(struct block_device *bdev)
> {
> - return bdev->bd_read_only;
> + return bdev->bd_read_only ||
> + test_bit(GD_READ_ONLY, &bdev->bd_disk->state);
> }
> EXPORT_SYMBOL(bdev_read_only);

Maybe one inline version can be added for fast path(bio_check_ro()), and the approach
is good:

Reviewed-by: Ming Lei <ming.lei@redhat.com>

--
Ming