提交 a7448f9d 编写于 作者: M Martin K. Petersen 提交者: Zheng Zengkai

scsi: sd: block: Fix regressions in read-only block device handling

hulk inclusion
category: bugfix
bugzilla: 46833 https://gitee.com/openeuler/kernel/issues/I4DCSN

-----------------------------------------------

Fix:
https://gitee.com/src-openeuler/util-linux/issues/I28N07

Origin patch:
https://patchwork.kernel.org/project/linux-scsi/patch/20190227041941.
1568-1-martin.petersen@oracle.com/

If partition table changed online, will record partition read-only flag.

Some devices come online in write protected state and switch to
read-write once they are ready to process I/O requests. These devices
broke with commit 20bd1d02 ("scsi: sd: Keep disk read-only when
re-reading partition") because we had no way to distinguish between a
user decision to set a block_device read-only and the actual hardware
device being write-protected.

Because partitions are dropped and recreated on revalidate we are
unable to persist any user-provided policy in hd_struct. Introduce a
bitmap in struct gendisk to track the user configuration. This bitmap
is updated when BLKROSET is called on a given disk or partition.

A helper function, get_user_ro(), is provided to determine whether the
ioctl has forced read-only state for a given block device. This helper
is used by set_disk_ro() and add_partition() to ensure that both
existing and newly created partitions will get the correct state.

 - If BLKROSET sets a whole disk device read-only, all partitions will
   now end up in a read-only state.

 - If BLKROSET sets a given partition read-only, that partition will
   remain read-only post revalidate.

 - Otherwise both the whole disk device and any partitions will
   reflect the write protect state of the underlying device.

Since nobody knows what "policy" means, rename the field to
"read_only" for clarity.

Cc: Jeremy Cline <jeremy@jcline.org>
Cc: Oleksii Kurochko <olkuroch@cisco.com>
Cc: stable@vger.kernel.org # v4.16+
Reported-by: NOleksii Kurochko <olkuroch@cisco.com>
Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=201221
Fixes: 20bd1d02 ("scsi: sd: Keep disk read-only when re-reading partition")
Signed-off-by: NMartin K. Petersen <martin.petersen@oracle.com>
Signed-off-by: NYe Bin <yebin10@huawei.com>
Reviewed-by: NJason Yan <yanaijie@huawei.com>
Signed-off-by: NChen Jun <chenjun102@huawei.com>
Signed-off-by: NZheng Zengkai <zhengzengkai@huawei.com>
上级 4654c5e0
......@@ -696,7 +696,7 @@ static inline bool bio_check_ro(struct bio *bio, struct hd_struct *part)
{
const int op = bio_op(bio);
if (part->policy && op_is_write(op)) {
if (part->read_only && op_is_write(op)) {
char b[BDEVNAME_SIZE];
if (op_is_flush(bio->bi_opf) && !bio_sectors(bio))
......
......@@ -1867,26 +1867,40 @@ static void set_disk_ro_uevent(struct gendisk *gd, int ro)
kobject_uevent_env(&disk_to_dev(gd)->kobj, KOBJ_CHANGE, envp);
}
void set_device_ro(struct block_device *bdev, int flag)
void set_device_ro(struct block_device *bdev, bool state)
{
bdev->bd_part->policy = flag;
bdev->bd_part->read_only = state;
}
EXPORT_SYMBOL(set_device_ro);
void set_disk_ro(struct gendisk *disk, int flag)
bool get_user_ro(struct gendisk *disk, unsigned int partno)
{
/* Is the user read-only bit set for the whole disk device? */
if (test_bit(0, disk->user_ro_bitmap))
return true;
/* Is the user read-only bit set for this particular partition? */
if (test_bit(partno, disk->user_ro_bitmap))
return true;
return false;
}
void set_disk_ro(struct gendisk *disk, bool state)
{
struct disk_part_iter piter;
struct hd_struct *part;
if (disk->part0.policy != flag) {
set_disk_ro_uevent(disk, flag);
disk->part0.policy = flag;
}
if (disk->part0.read_only != state)
set_disk_ro_uevent(disk, state);
disk_part_iter_init(&piter, disk, DISK_PITER_INCL_EMPTY);
disk_part_iter_init(&piter, disk, DISK_PITER_INCL_EMPTY_PART0);
while ((part = disk_part_iter_next(&piter)))
part->policy = flag;
if (get_user_ro(disk, part->partno))
part->read_only = true;
else
part->read_only = state;
disk_part_iter_exit(&piter);
}
......@@ -1896,7 +1910,7 @@ int bdev_read_only(struct block_device *bdev)
{
if (!bdev)
return 0;
return bdev->bd_part->policy;
return bdev->bd_part->read_only;
}
EXPORT_SYMBOL(bdev_read_only);
......
......@@ -405,6 +405,10 @@ static int blkdev_roset(struct block_device *bdev, fmode_t mode,
return ret;
if (get_user(n, (int __user *)arg))
return -EFAULT;
if (n)
set_bit(bdev->bd_partno, bdev->bd_disk->user_ro_bitmap);
else
clear_bit(bdev->bd_partno, bdev->bd_disk->user_ro_bitmap);
set_device_ro(bdev, n);
return 0;
}
......
......@@ -192,7 +192,7 @@ static ssize_t part_ro_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct hd_struct *p = dev_to_part(dev);
return sprintf(buf, "%d\n", p->policy ? 1 : 0);
return sprintf(buf, "%d\n", p->read_only ? 1 : 0);
}
static ssize_t part_alignment_offset_show(struct device *dev,
......@@ -414,7 +414,7 @@ static struct hd_struct *add_partition(struct gendisk *disk, int partno,
p->start_sect = start;
p->nr_sects = len;
p->partno = partno;
p->policy = get_disk_ro(disk);
p->read_only = get_disk_ro(disk) | test_bit(partno, disk->user_ro_bitmap);
if (info) {
struct partition_meta_info *pinfo;
......
......@@ -67,7 +67,8 @@ struct hd_struct {
struct device __dev;
struct kobject *holder_dir;
int policy, partno;
bool read_only;
int partno;
struct partition_meta_info *info;
#ifdef CONFIG_FAIL_MAKE_REQUEST
int make_it_fail;
......@@ -185,6 +186,7 @@ struct gendisk {
*/
struct disk_part_tbl __rcu *part_tbl;
struct hd_struct part0;
DECLARE_BITMAP(user_ro_bitmap, DISK_MAX_PARTS);
const struct block_device_operations *fops;
struct request_queue *queue;
......@@ -304,12 +306,13 @@ extern void del_gendisk(struct gendisk *gp);
extern struct gendisk *get_gendisk(dev_t dev, int *partno);
extern struct block_device *bdget_disk(struct gendisk *disk, int partno);
extern void set_device_ro(struct block_device *bdev, int flag);
extern void set_disk_ro(struct gendisk *disk, int flag);
extern void set_device_ro(struct block_device *bdev, bool state);
extern void set_disk_ro(struct gendisk *disk, bool state);
extern bool get_user_ro(struct gendisk *disk, unsigned int partno);
static inline int get_disk_ro(struct gendisk *disk)
{
return disk->part0.policy;
return disk->part0.read_only;
}
extern void disk_block_events(struct gendisk *disk);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册