Skip to contentSkip to conversation

hot scsi disk resize

PATCH Alex Tomas 5 participants 9 messages Mar 3, 2003 – Mar 17, 2003

Subsystems (2)
lore
Share & tools
lore

Alex Tomas

author
#0 lore raw

Hello!

Here is patch to implement hot scsi resize function. Modern storage boxes support virtualization feature and may change logical volume size online. I think it would be great if linux supports such abilities. Look at this example:

root@zefir:# dmesg | tail
scsi0:A:1:0: Tagged Queuing enabled.  Depth 32
SCSI device sda: 2097152 512-byte hdwr sectors (1074 MB)
SCSI device sda: drive cache: write through
 sda: unknown partition table
Attached scsi disk sda at scsi0, channel 0, id 1, lun 0
root@zefir:# mount -treiserfs /dev/sda /mnt
root@zefir:# df -Th
Filesystem    Type    Size  Used Avail Use% Mounted on
/dev/hda1     ext3    1.9G  1.8G  138M  93% /
/dev/sda  reiserfs    1.0G   33M  991M   4% /mnt
root@zefir:# echo 'scsi rescan 0 0 1 0' >/proc/scsi/scsi 
root@zefir:# dmesg|tail -n1
SCSI device sda: 20971520 512-byte hdwr sectors (10737 MB)
root@zefir:# resize_reiserfs /dev/sda 

<-------------resize_reiserfs, 2002------------->
reiserfsprogs 3.6.4

root@zefir:# df -Th
Filesystem    Type    Size  Used Avail Use% Mounted on
/dev/hda1     ext3    1.9G  1.8G  138M  93% /
/dev/sda  reiserfs     10G   33M  9.9G   1% /mnt

I think it's really useful for for high-availability systems. MS Windows has supported online disk resize since 2000. I Would be happy to hear any comments and suggestions.

drivers/scsi/hosts.h +1 -0
diff -uNr linux/drivers/scsi/hosts.h edited/drivers/scsi/hosts.h
@@ -558,6 +558,7 @@
     void (*detach)(Scsi_Device *);
     int (*init_command)(Scsi_Cmnd *);     /* Used by new queueing code. 
                                            Selects command for blkdevs */
+    void (*rescan)(Scsi_Device *);
     struct device_driver scsi_driverfs_driver;
 };
 
drivers/scsi/scsi.c +15 -0
diff -uNr linux/drivers/scsi/scsi.c edited/drivers/scsi/scsi.c
@@ -1510,6 +1510,21 @@
 	up_read(&scsi_devicelist_mutex);
 }
 
+void scsi_rescan_device(struct scsi_device *sdev)
+{
+	struct Scsi_Device_Template *sdt;
+
+	down_read(&scsi_devicelist_mutex);
+	list_for_each_entry(sdt, &scsi_devicelist, list) {
+		if (!try_module_get(sdt->module))
+			continue;
+		if (*sdt->rescan)
+			(*sdt->rescan)(sdev);
+		module_put(sdt->module);
+	}
+	up_read(&scsi_devicelist_mutex);
+}
+
 int scsi_device_get(struct scsi_device *sdev)
 {
 	if (!try_module_get(sdev->host->hostt->module))
drivers/scsi/scsi_proc.c +12 -0
diff -uNr linux/drivers/scsi/scsi_proc.c edited/drivers/scsi/scsi_proc.c
@@ -551,6 +551,18 @@
 		lun = simple_strtoul(p + 1, &p, 0);
 
 		err = scsi_remove_single_device(host, channel, id, lun);
+		if (err >= 0)
+			err = length;
+	} else if (!strncmp("rescan", buffer + 5, 6)) {
+		p = buffer + 12;
+
+		host = simple_strtoul(p, &p, 0);
+		channel = simple_strtoul(p + 1, &p, 0);
+		id = simple_strtoul(p + 1, &p, 0);
+		lun = simple_strtoul(p + 1, &p, 0);
+		err = scsi_rescan_single_device(host, channel, id, lun);
+		if (err >= 0)
+			err = length;
 	}
 out:
 	
drivers/scsi/scsi_scan.c +18 -0
diff -uNr linux/drivers/scsi/scsi_scan.c edited/drivers/scsi/scsi_scan.c
@@ -1796,6 +1796,24 @@
 	return error;
 }
 
+int scsi_rescan_single_device(uint host, uint channel, uint id, uint lun)
+{
+	struct scsi_device *sdev;
+	struct Scsi_Host *shost;
+
+	shost = scsi_host_hn_get(host);
+	if (!shost)
+		return -ENODEV;
+	sdev = scsi_find_device(shost, channel, id, lun);
+	if (!sdev)
+		goto out;
+
+	scsi_rescan_device(sdev);	
+out:
+	scsi_host_put(shost);
+	return 0;
+}
+
 /**
  * scsi_scan_target - scan a target id, possibly including all LUNs on the
  *     target.
drivers/scsi/sd.c +36 -1
diff -uNr linux/drivers/scsi/sd.c edited/drivers/scsi/sd.c
@@ -93,10 +93,12 @@
 
 static int sd_attach(struct scsi_device *);
 static void sd_detach(struct scsi_device *);
+static void sd_rescan(struct scsi_device *);
 static int sd_init_command(struct scsi_cmnd *);
 static int sd_synchronize_cache(struct scsi_disk *, int);
 static int sd_notifier(struct notifier_block *, unsigned long, void *);
-
+static void sd_read_capacity(struct scsi_disk *sdkp, char *diskname,
+		 struct scsi_request *SRpnt, unsigned char *buffer);
 static struct notifier_block sd_notifier_block = {sd_notifier, NULL, 0}; 
 
 static struct Scsi_Device_Template sd_template = {
@@ -106,6 +108,7 @@
 	.scsi_type	= TYPE_DISK,
 	.attach		= sd_attach,
 	.detach		= sd_detach,
+	.rescan		= sd_rescan,
 	.init_command	= sd_init_command,
 	.scsi_driverfs_driver = {
 		.name   = "sd",
@@ -627,6 +630,38 @@
 not_present:
 	set_media_not_present(sdkp);
 	return 1;
+}
+
+static void sd_rescan(struct scsi_device * sdp)
+{
+	unsigned char *buffer;
+	struct scsi_disk *sdkp = sd_find_by_sdev(sdp);
+	struct gendisk *gd;
+	struct scsi_request *SRpnt;
+
+	if (!sdkp || sdp->online == FALSE || !sdkp->media_present)
+		return;
+		
+	gd = sdkp->disk;
+	
+	SCSI_LOG_HLQUEUE(3, printk("sd_rescan: disk=%s\n", gd->disk_name));
+	
+	SRpnt = scsi_allocate_request(sdp);
+	if (!SRpnt) {
+		printk(KERN_WARNING "(sd_rescan:) Request allocation "
+		       "failure.\n");
+		return;
+	}
+
+	if (sdkp->device->host->unchecked_isa_dma)
+		buffer = kmalloc(512, GFP_DMA);
+	else
+		buffer = kmalloc(512, GFP_KERNEL);
+
+    	sd_read_capacity(sdkp, gd->disk_name, SRpnt, buffer);
+	set_capacity(gd, sdkp->capacity);	
+	scsi_release_request(SRpnt);
+	kfree(buffer);
 }
 
 static int sd_revalidate_disk(struct gendisk *disk)
fs/block_dev.c +2 -0
diff -uNr linux/fs/block_dev.c edited/fs/block_dev.c
@@ -623,6 +623,8 @@
 			up(&whole->bd_sem);
 		}
 	} else {
+		if (!part)
+			bd_set_size(bdev,(loff_t)get_capacity(disk)<<9);
 		put_disk(disk);
 		module_put(owner);
 		if (bdev->bd_contains == bdev) {
#1 lore raw

drivers/scsi/scsi_proc.c line 565

Quoted 12 lines · Alex Tomas
drivers/scsi/scsi_proc.c +12 -0
+		if (err >= 0)
+			err = length;
+	} else if (!strncmp("rescan", buffer + 5, 6)) {
+		p = buffer + 12;
+
+		host = simple_strtoul(p, &p, 0);
+		channel = simple_strtoul(p + 1, &p, 0);
+		id = simple_strtoul(p + 1, &p, 0);
+		lun = simple_strtoul(p + 1, &p, 0);
+		err = scsi_rescan_single_device(host, channel, id, lun);
+		if (err >= 0)
+			err = length;

Please don't add more procfs option. A resize attribute for the sysfs node of the scsi_device is the better choice in my opinion.

drivers/scsi/sd.c line 664

Quoted 8 lines
drivers/scsi/sd.c +8 -0
+		buffer = kmalloc(512, GFP_DMA);
+	else
+		buffer = kmalloc(512, GFP_KERNEL);
+
+    	sd_read_capacity(sdkp, gd->disk_name, SRpnt, buffer);
+	set_capacity(gd, sdkp->capacity);	
+	scsi_release_request(SRpnt);
+	kfree(buffer);

Don't you need some kind of serialization here?

fs/block_dev.c line 627

Quoted 8 lines
fs/block_dev.c +2 -0
@@ -623,6 +623,8 @@
 			up(&whole->bd_sem);
 		}
 	} else {
+		if (!part)
+			bd_set_size(bdev,(loff_t)get_capacity(disk)<<9);

This needs more explanation.

#2 lore raw

Hi!

Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line

Christoph Hellwig (CH) writes:

Quoted 6 lines · Alex Tomas
Quoted 6 lines

+ if (err >= 0) + err = length; + } else if (!strncmp("rescan", buffer + 5, 6)) { + p = buffer + 12; + + host = simple_strtoul(p, &p, 0); + channel = simple_strtoul(p + 1, &p, 0); + id = simple_strtoul(p + 1, &p, 0); + lun = simple_strtoul(p + 1, &p, 0); + err = scsi_rescan_single_device(host, channel, id, lun); + if (err >= 0) + err = length;

CH> Please don't add more procfs option. A resize attribute for the CH> sysfs node of the scsi_device is the better choice in my opinion.

I like your idea.

Quoted 4 lines
Quoted 4 lines

+ buffer = kmalloc(512, GFP_DMA); + else + buffer = kmalloc(512, GFP_KERNEL); + + sd_read_capacity(sdkp, gd->disk_name, SRpnt, buffer); + set_capacity(gd, sdkp->capacity); + scsi_release_request(SRpnt); + kfree(buffer);

CH> Don't you need some kind of serialization here?

Well. I think all changes sd_read_capacity() and set_capacity() make need not to be protected. Am I right?

Quoted 4 lines
Quoted 4 lines

--- linux/fs/block_dev.c Mon Jan 20 02:23:49 2003 +++ edited/fs/block_dev.c Mon Mar 3 18:49:47 2003 @@ -623,6 +623,8 @@ up(&whole->bd_sem); } } else { + if (!part) + bd_set_size(bdev,(loff_t)get_capacity(disk)<<9);

CH> This needs more explanation.

This is required because open("/dev/sd*",) should see new size of disk being used. James Bottomley suggested to introduce new ioctl, but I hope we may implmenent the feature w/o touching userspace tools.

with best regards, Alex

#3 lore raw
In reply to Christoph Hellwig

Hi!

Here is new version of the patch. All procfs-related stuff has been removed. One may rescan device size writing something to /sysfs/.../<scsi device>/rescan:

root@zefir:~# echo 1 >/sysfs/bus/scsi/devices/0\:0\:1\:0/rescan 
root@zefir:~# dmesg
scsi0:A:1:0: Tagged Queuing enabled.  Depth 64
scsi: host 0 channel 0 id 1 lun16384 has a LUN larger than allowed by the host adapter
SCSI device sda: 2097152 512-byte hdwr sectors (1074 MB)
SCSI device sda: drive cache: write through
 sda: unknown partition table
Attached scsi disk sda at scsi0, channel 0, id 1, lun 0
SCSI device sda: 125829120 512-byte hdwr sectors (64425 MB)
root@zefir:~# 
drivers/scsi/hosts.h +1 -0
diff -uNr linux/drivers/scsi/hosts.h edited/drivers/scsi/hosts.h
@@ -558,6 +558,7 @@
     void (*detach)(Scsi_Device *);
     int (*init_command)(Scsi_Cmnd *);     /* Used by new queueing code. 
                                            Selects command for blkdevs */
+    void (*rescan)(Scsi_Device *);
     struct device_driver scsi_driverfs_driver;
 };
 
drivers/scsi/scsi.c +15 -0
diff -uNr linux/drivers/scsi/scsi.c edited/drivers/scsi/scsi.c
@@ -1510,6 +1510,21 @@
 	up_read(&scsi_devicelist_mutex);
 }
 
+void scsi_rescan_device(struct scsi_device *sdev)
+{
+	struct Scsi_Device_Template *sdt;
+
+	down_read(&scsi_devicelist_mutex);
+	list_for_each_entry(sdt, &scsi_devicelist, list) {
+		if (!try_module_get(sdt->module))
+			continue;
+		if (*sdt->rescan)
+			(*sdt->rescan)(sdev);
+		module_put(sdt->module);
+	}
+	up_read(&scsi_devicelist_mutex);
+}
+
 int scsi_device_get(struct scsi_device *sdev)
 {
 	if (!try_module_get(sdev->host->hostt->module))
drivers/scsi/scsi_sysfs.c +20 -0
diff -uNr linux/drivers/scsi/scsi_sysfs.c edited/drivers/scsi/scsi_sysfs.c
@@ -219,6 +219,25 @@
 sdev_rd_attr (rev, "%.4s\n");
 sdev_rw_attr_bit (online);
 
+static ssize_t
+show_rescan_field (struct device *dev, char *buf)
+{
+	return 0; 
+}
+
+static ssize_t
+store_rescan_field (struct device *dev, const char *buf, size_t count) 
+{
+	int ret = ENODEV;
+	struct scsi_device *sdev;
+	sdev = to_scsi_device(dev);
+	if (sdev)
+		ret = scsi_rescan_device(sdev);
+	return ret;
+}
+
+static DEVICE_ATTR(rescan, S_IRUGO | S_IWUSR, show_rescan_field, store_rescan_field)
+
 static struct device_attribute * const sdev_attrs[] = {
 	&dev_attr_device_blocked,
 	&dev_attr_queue_depth,
@@ -229,6 +248,7 @@
 	&dev_attr_model,
 	&dev_attr_rev,
 	&dev_attr_online,
+	&dev_attr_rescan,
 };
 
 /**
drivers/scsi/sd.c +36 -1
diff -uNr linux/drivers/scsi/sd.c edited/drivers/scsi/sd.c
@@ -93,10 +93,12 @@
 
 static int sd_attach(struct scsi_device *);
 static void sd_detach(struct scsi_device *);
+static void sd_rescan(struct scsi_device *);
 static int sd_init_command(struct scsi_cmnd *);
 static int sd_synchronize_cache(struct scsi_disk *, int);
 static int sd_notifier(struct notifier_block *, unsigned long, void *);
-
+static void sd_read_capacity(struct scsi_disk *sdkp, char *diskname,
+		 struct scsi_request *SRpnt, unsigned char *buffer);
 static struct notifier_block sd_notifier_block = {sd_notifier, NULL, 0}; 
 
 static struct Scsi_Device_Template sd_template = {
@@ -106,6 +108,7 @@
 	.scsi_type	= TYPE_DISK,
 	.attach		= sd_attach,
 	.detach		= sd_detach,
+	.rescan		= sd_rescan,
 	.init_command	= sd_init_command,
 	.scsi_driverfs_driver = {
 		.name   = "sd",
@@ -627,6 +630,38 @@
 not_present:
 	set_media_not_present(sdkp);
 	return 1;
+}
+
+static void sd_rescan(struct scsi_device * sdp)
+{
+	unsigned char *buffer;
+	struct scsi_disk *sdkp = sd_find_by_sdev(sdp);
+	struct gendisk *gd;
+	struct scsi_request *SRpnt;
+
+	if (!sdkp || sdp->online == FALSE || !sdkp->media_present)
+		return;
+		
+	gd = sdkp->disk;
+	
+	SCSI_LOG_HLQUEUE(3, printk("sd_rescan: disk=%s\n", gd->disk_name));
+	
+	SRpnt = scsi_allocate_request(sdp);
+	if (!SRpnt) {
+		printk(KERN_WARNING "(sd_rescan:) Request allocation "
+		       "failure.\n");
+		return;
+	}
+
+	if (sdkp->device->host->unchecked_isa_dma)
+		buffer = kmalloc(512, GFP_DMA);
+	else
+		buffer = kmalloc(512, GFP_KERNEL);
+
+    	sd_read_capacity(sdkp, gd->disk_name, SRpnt, buffer);
+	set_capacity(gd, sdkp->capacity);	
+	scsi_release_request(SRpnt);
+	kfree(buffer);
 }
 
 static int sd_revalidate_disk(struct gendisk *disk)
fs/block_dev.c +3 -1
diff -uNr linux/fs/block_dev.c edited/fs/block_dev.c
@@ -546,7 +546,7 @@
 	return res;
 }
 
-static void bd_set_size(struct block_device *bdev, loff_t size)
+void bd_set_size(struct block_device *bdev, loff_t size)
 {
 	unsigned bsize = bdev_hardsect_size(bdev);
 	bdev->bd_inode->i_size = size;
@@ -623,6 +623,8 @@
 			up(&whole->bd_sem);
 		}
 	} else {
+		if (!part)
+			bd_set_size(bdev,(loff_t)get_capacity(disk)<<9);
 		put_disk(disk);
 		module_put(owner);
 		if (bdev->bd_contains == bdev) {
#4 lore raw
Quoted 2 lines · On Tue, 2003-03-04 at 01:00, [email protected] wrote:

Here is new version of the patch. All procfs-related stuff has been removed. One may rescan device size writing something to /sysfs/.../<scsi device>/rescan:

I applied all of this, except this piece:

Quoted 6 lines

diff -uNr linux/fs/block_dev.c edited/fs/block_dev.c

fs/block_dev.c line 630

Quoted 8 lines
fs/block_dev.c +2 -0
@@ -623,6 +623,8 @@
 			up(&whole->bd_sem);
 		}
 	} else {
+		if (!part)
+			bd_set_size(bdev,(loff_t)get_capacity(disk)<<9);
 		put_disk(disk);
 		module_put(owner);
 		if (bdev->bd_contains == bdev) {

Because adding this in will break the initial ramdisk. Thus, although the SCSI layer does resize, the block layer may not.

I've copied Al Viro because he's more familiar with the block layer device code than I and may have been planning to add this feature. Al, can you tell us what needs to happen to allow for hot resizing a SCSI disc?

Thanks,

James

#5 lore raw
Quoted 8 lines · James Bottomley
Quoted 8 lines
fs/block_dev.c +2 -0
@@ -623,6 +623,8 @@
 			up(&whole->bd_sem);
 		}
 	} else {
+		if (!part)
+			bd_set_size(bdev,(loff_t)get_capacity(disk)<<9);
Quoted 8 lines

fs/block_dev.c line 630

Quoted 5 lines
fs/block_dev.c +0 -0
 		put_disk(disk);
 		module_put(owner);
 		if (bdev->bd_contains == bdev) {

Because adding this in will break the initial ramdisk. Thus, although the SCSI layer does resize, the block layer may not.

What about moving the size setting into ->open?

#6 lore raw
In reply to Alex Tomas
Quoted 27 lines · Alex Tomas

Hello!

Here is patch to implement hot scsi resize function. Modern storage boxes support virtualization feature and may change logical volume size online. I think it would be great if linux supports such abilities. Look at this example:

root@zefir:# dmesg | tail
scsi0:A:1:0: Tagged Queuing enabled.  Depth 32
SCSI device sda: 2097152 512-byte hdwr sectors (1074 MB)
SCSI device sda: drive cache: write through
 sda: unknown partition table
Attached scsi disk sda at scsi0, channel 0, id 1, lun 0
root@zefir:# mount -treiserfs /dev/sda /mnt
root@zefir:# df -Th
Filesystem    Type    Size  Used Avail Use% Mounted on
/dev/hda1     ext3    1.9G  1.8G  138M  93% /
/dev/sda  reiserfs    1.0G   33M  991M   4% /mnt
root@zefir:# echo 'scsi rescan 0 0 1 0' >/proc/scsi/scsi 
root@zefir:# dmesg|tail -n1
SCSI device sda: 20971520 512-byte hdwr sectors (10737 MB)
root@zefir:# resize_reiserfs /dev/sda 
Quoted 7 lines

<-------------resize_reiserfs, 2002-------------> reiserfsprogs 3.6.4

root@zefir:# df -Th
Filesystem    Type    Size  Used Avail Use% Mounted on
/dev/hda1     ext3    1.9G  1.8G  138M  93% /
/dev/sda  reiserfs     10G   33M  9.9G   1% /mnt

Well, I also think it's a very important and needed feature for HA systems. I'm very interested in such things...

But, I thought resize_reiserfs only resize *unmounted* filesystem :
http://www.reiserfs.org/resize_reiserfs.html

Will it work under IO load ?

Reiserfs has a way to do it dynamically while using it, with mount option :
http://www.reiserfs.org/mount-options.html

I've tested this without a lot of success (I encounter a lot of problems).

I'm afraid that even if scsi subsystem support the resize option, FileSystems won't support it correctly...
Maybe with LVM ?

Did someone test it ?

Signature · 8 lines
Fabien SALVI      Centre de Ressources Informatiques
                   Archamps, France -- http://www.cri74.org
                   PingOO GNU/linux distribution : http://www.pingoo.org

-
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#7 lore raw
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line

Fabien Salvi (FS) writes:

FS> Well, I also think it's a very important and needed feature for FS> HA systems. I'm very interested in such things...

FS> But, I thought resize_reiserfs only resize *unmounted* filesystem FS> : http://www.reiserfs.org/resize_reiserfs.html

citate from this URL: "The resize_reiserfs program allows to grow a reiserfs on-line if there is a free space on block device."

FS> Will it work under IO load ?

I tested it several times under different load (read-only, write-only, read-write, single-thread and multi-thread tests). I think reiserfs team may give comments.

FS> Reiserfs has a way to do it dynamically while using it, with FS> mount option : http://www.reiserfs.org/mount-options.html

FS> I've tested this without a lot of success (I encounter a lot of FS> problems).

what kind of problem have you seen? what versions of kernel/reiserfs/tools?

FS> I'm afraid that even if scsi subsystem support the resize option, FS> FileSystems won't support it correctly... Maybe with LVM ?

FS> Did someone test it ?

Quoted 1 line

From filesystem point of view, LVM and resizable SCSI HDD are the same

think - just block device which may grow.

I think this patch is just one more step in the right direction.

#8 lore raw
Quoted 11 lines · [email protected] a écrit:
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 1 line

Fabien Salvi (FS) writes:

FS> Well, I also think it's a very important and needed feature for FS> HA systems. I'm very interested in such things...

FS> But, I thought resize_reiserfs only resize *unmounted* filesystem FS> : http://www.reiserfs.org/resize_reiserfs.html

citate from this URL: "The resize_reiserfs program allows to grow a reiserfs on-line if there is a free space on block device."

Yes, you're right !
I haven't seen this...
It's a bit strange, it contradicts what is said on the first line :)

Quoted 7 lines

FS> Will it work under IO load ?

I tested it several times under different load (read-only, write-only, read-write, single-thread and multi-thread tests). I think reiserfs team may give comments.

Quoted 7 lines

FS> Reiserfs has a way to do it dynamically while using it, with FS> mount option : http://www.reiserfs.org/mount-options.html

FS> I've tested this without a lot of success (I encounter a lot of FS> problems).

what kind of problem have you seen? what versions of kernel/reiserfs/tools?

Well, I made some tests one year ago, I will try this again, as you say it seems to work well, it's interested...
I will let you know my new results.

Quoted 9 lines

FS> I'm afraid that even if scsi subsystem support the resize option, FS> FileSystems won't support it correctly... Maybe with LVM ?

FS> Did someone test it ?

Quoted 1 line

From filesystem point of view, LVM and resizable SCSI HDD are the same

think - just block device which may grow.

I think this patch is just one more step in the right direction.

Yes, surely !

Signature · 8 lines
Fabien SALVI      Centre de Ressources Informatiques
                   Archamps, France -- http://www.cri74.org
                   PingOO GNU/linux distribution : http://www.pingoo.org

-
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html