Skip to contentSkip to conversation

fix scan_thread, sysfs stuff, LUN addressing problem

PATCH New Stephen M. Cameron 1 participant 18 messages Aug 20, 2009

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

cover fix scan_thread, sysfs stuff, LUN addressing problem

The following series cleans up the broken scan_thread code, allows triggering of the scan_thread via sysfs, dynamically allocates the struct device for each logical drive as needed instead of all at once at driver init time, fixes a bug in logical drive addressing (seen on MSA2012), and adds lunid, raid_level, and usage_count attributes in sysfs for each logical drive.

---

Andrew Patterson (3):
Allow triggering of rescan of logical drive topology via sysfs entry Use one scan thread per controller and fix hang during rmmod Remove sysfs entries for logical drives on driver cleanup.

Stephen M. Cameron (14):
Add usage_count attribute to each logical drive in /sys
Add a "raid_level" attribute to each logical drive in /sys
Add lunid attribute to each logical drive in /sys
Preserve all 8 bytes of LUN ID for logical drives.
Silence noisy per-disk messages output by cciss_read_capacity Fix excessive gendisk freeing bug on driver unload.
Fix usage_count check in rebuild_lun_table when triggered via sysfs. Clear all sysfs-exposed data for deleted logical drives. Handle special case for sysfs attributes of the first logical drive. Handle cases when cciss_add_disk fails.
Handle failure of blk_init_queue gracefully in cciss_add_disk. Rearrange logical drive sysfs code to make the "changing a disk" path work. Dynamically allocate struct device for each logical drive as needed. Remove some unused code in rebuild_lun_table()

 .../ABI/testing/sysfs-bus-pci-devices-cciss        |   28 +
 drivers/block/cciss.c                              |  551 +++++++++++++++-----
 drivers/block/cciss.h                              |   11 
 3 files changed, 461 insertions(+), 129 deletions(-)
Signature · 1 line
-- steve
#1 lore raw

PATCH 1/17 Remove sysfs entries for logical drives on driver cleanup.

From: Andrew Patterson <[email protected]>

Sysfs entries for logical drives need to be removed when a drive is deleted during driver cleanup.

---

 drivers/block/cciss.c |    5 ++++-
 1 files changed, 4 insertions(+), 1 deletions(-)
drivers/block/cciss.c +4 -1
index a52cc7f..970c896 100644
@@ -1977,7 +1977,6 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time)
 			h->drv[i].busy_configuring = 1;
 			spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
 			return_code = deregister_disk(h, i, 1);
-			cciss_destroy_ld_sysfs_entry(&h->drv[i]);
 			h->drv[i].busy_configuring = 0;
 		}
 	}
@@ -2118,6 +2117,7 @@ static int deregister_disk(ctlr_info_t *h, int drv_index,
 				 * indicate that this element of the drive
 				 * array is free.
 				 */
+	cciss_destroy_ld_sysfs_entry(drv);
 
 	if (clear_all) {
 		/* check to see if it was the last disk */
@@ -4141,6 +4141,9 @@ static void __devexit cciss_remove_one(struct pci_dev *pdev)
 			if (q)
 				blk_cleanup_queue(q);
 		}
+		if (hba[i]->drv[j].raid_level != -1)
+			cciss_destroy_ld_sysfs_entry(&hba[i]->drv[j]);
+
 	}
 
 #ifdef CONFIG_CISS_SCSI_TAPE
#2 lore raw

PATCH 2/17 Use one scan thread per controller and fix hang during rmmod

In reply to Stephen M. Cameron

From: Andrew Patterson <[email protected]>

Replace the use of one scan kthread per controller with one per driver. Use a queue to hold a list of controllers that need to be rescanned with routines to add and remove controllers from the queue.

Fix locking and completion handling to prevent a hang during rmmod.

---

 drivers/block/cciss.c |  156 +++++++++++++++++++++++++++++++++++++++++++------
 drivers/block/cciss.h |    7 ++
 2 files changed, 141 insertions(+), 22 deletions(-)
drivers/block/cciss.c +136 -20
index 970c896..e7273a4 100644
@@ -39,6 +39,7 @@
 #include <linux/hdreg.h>
 #include <linux/spinlock.h>
 #include <linux/compat.h>
+#include <linux/mutex.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
 
@@ -155,6 +156,10 @@ static struct board_type products[] = {
 
 static ctlr_info_t *hba[MAX_CTLR];
 
+static struct task_struct *cciss_scan_thread;
+static DEFINE_MUTEX(scan_mutex);
+static LIST_HEAD(scan_q);
+
 static void do_cciss_request(struct request_queue *q);
 static irqreturn_t do_cciss_intr(int irq, void *dev_id);
 static int cciss_open(struct block_device *bdev, fmode_t mode);
@@ -3232,20 +3237,121 @@ static irqreturn_t do_cciss_intr(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
+/**
+ * add_to_scan_list() - add controller to rescan queue
+ * @h:		      Pointer to the controller.
+ *
+ * Adds the controller to the rescan queue if not already on the queue.
+ *
+ * returns 1 if added to the queue, 0 if skipped (could be on the
+ * queue already, or the controller could be initializing or shutting
+ * down).
+ **/
+static int add_to_scan_list(struct ctlr_info *h)
+{
+	struct ctlr_info *test_h;
+	int found = 0;
+	int ret = 0;
+
+	if (h->busy_initializing)
+		return 0;
+
+	if (!mutex_trylock(&h->busy_shutting_down))
+		return 0;
+
+	mutex_lock(&scan_mutex);
+	list_for_each_entry(test_h, &scan_q, scan_list) {
+		if (test_h == h) {
+			found = 1;
+			break;
+		}
+	}
+	if (!found && !h->busy_scanning) {
+		INIT_COMPLETION(h->scan_wait);
+		list_add_tail(&h->scan_list, &scan_q);
+		ret = 1;
+	}
+	mutex_unlock(&scan_mutex);
+	mutex_unlock(&h->busy_shutting_down);
+
+	return ret;
+}
+
+/**
+ * remove_from_scan_list() - remove controller from rescan queue
+ * @h:			   Pointer to the controller.
+ *
+ * Removes the controller from the rescan queue if present. Blocks if
+ * the controller is currently conducting a rescan.
+ **/
+static void remove_from_scan_list(struct ctlr_info *h)
+{
+	struct ctlr_info *test_h, *tmp_h;
+	int scanning = 0;
+
+	mutex_lock(&scan_mutex);
+	list_for_each_entry_safe(test_h, tmp_h, &scan_q, scan_list) {
+		if (test_h == h) {
+			list_del(&h->scan_list);
+			complete_all(&h->scan_wait);
+			mutex_unlock(&scan_mutex);
+			return;
+		}
+	}
+	if (&h->busy_scanning)
+		scanning = 0;
+	mutex_unlock(&scan_mutex);
+
+	if (scanning)
+		wait_for_completion(&h->scan_wait);
+}
+
+/**
+ * scan_thread() - kernel thread used to rescan controllers
+ * @data:	 Ignored.
+ *
+ * A kernel thread used scan for drive topology changes on
+ * controllers. The thread processes only one controller at a time
+ * using a queue.  Controllers are added to the queue using
+ * add_to_scan_list() and removed from the queue either after done
+ * processing or using remove_from_scan_list().
+ *
+ * returns 0.
+ **/
 static int scan_thread(void *data)
 {
-	ctlr_info_t *h = data;
-	int rc;
-	DECLARE_COMPLETION_ONSTACK(wait);
-	h->rescan_wait = &wait;
+	struct ctlr_info *h;
 
-	for (;;) {
-		rc = wait_for_completion_interruptible(&wait);
+	while (1) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule();
 		if (kthread_should_stop())
 			break;
-		if (!rc)
-			rebuild_lun_table(h, 0);
+
+		while (1) {
+			mutex_lock(&scan_mutex);
+			if (list_empty(&scan_q)) {
+				mutex_unlock(&scan_mutex);
+				break;
+			}
+
+			h = list_entry(scan_q.next,
+				       struct ctlr_info,
+				       scan_list);
+			list_del(&h->scan_list);
+			h->busy_scanning = 1;
+			mutex_unlock(&scan_mutex);
+
+			if (h) {
+				rebuild_lun_table(h, 0);
+				complete_all(&h->scan_wait);
+				mutex_lock(&scan_mutex);
+				h->busy_scanning = 0;
+				mutex_unlock(&scan_mutex);
+			}
+		}
 	}
+
 	return 0;
 }
 
@@ -3268,8 +3374,8 @@ static int check_for_unit_attention(ctlr_info_t *h, CommandList_struct *c)
 	case REPORT_LUNS_CHANGED:
 		printk(KERN_WARNING "cciss%d: report LUN data "
 			"changed\n", h->ctlr);
-		if (h->rescan_wait)
-			complete(h->rescan_wait);
+		add_to_scan_list(h);
+		wake_up_process(cciss_scan_thread);
 		return 1;
 	break;
 	case POWER_OR_RESET:
@@ -3918,6 +4024,7 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
 	hba[i]->busy_initializing = 1;
 	INIT_HLIST_HEAD(&hba[i]->cmpQ);
 	INIT_HLIST_HEAD(&hba[i]->reqQ);
+	mutex_init(&hba[i]->busy_shutting_down);
 
 	if (cciss_pci_init(hba[i], pdev) != 0)
 		goto clean0;
@@ -3926,6 +4033,8 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
 	hba[i]->ctlr = i;
 	hba[i]->pdev = pdev;
 
+	init_completion(&hba[i]->scan_wait);
+
 	if (cciss_create_hba_sysfs_entry(hba[i]))
 		goto clean0;
 
@@ -4034,14 +4143,8 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
 
 	hba[i]->cciss_max_sectors = 2048;
 
-	hba[i]->busy_initializing = 0;
-
 	rebuild_lun_table(hba[i], 1);
-	hba[i]->cciss_scan_thread = kthread_run(scan_thread, hba[i],
-				"cciss_scan%02d", i);
-	if (IS_ERR(hba[i]->cciss_scan_thread))
-		return PTR_ERR(hba[i]->cciss_scan_thread);
-
+	hba[i]->busy_initializing = 0;
 	return 1;
 
 clean4:
@@ -4125,8 +4228,9 @@ static void __devexit cciss_remove_one(struct pci_dev *pdev)
 		return;
 	}
 
-	kthread_stop(hba[i]->cciss_scan_thread);
+	mutex_lock(&hba[i]->busy_shutting_down);
 
+	remove_from_scan_list(hba[i]);
 	remove_proc_entry(hba[i]->devname, proc_cciss);
 	unregister_blkdev(hba[i]->major, hba[i]->devname);
 
@@ -4173,6 +4277,7 @@ static void __devexit cciss_remove_one(struct pci_dev *pdev)
 	pci_release_regions(pdev);
 	pci_set_drvdata(pdev, NULL);
 	cciss_destroy_hba_sysfs_entry(hba[i]);
+	mutex_unlock(&hba[i]->busy_shutting_down);
 	free_hba(i);
 }
 
@@ -4205,15 +4310,25 @@ static int __init cciss_init(void)
 	if (err)
 		return err;
 
+	/* Start the scan thread */
+	cciss_scan_thread = kthread_run(scan_thread, NULL, "cciss_scan");
+	if (IS_ERR(cciss_scan_thread)) {
+		err = PTR_ERR(cciss_scan_thread);
+		goto err_bus_unregister;
+	}
+
 	/* Register for our PCI devices */
 	err = pci_register_driver(&cciss_pci_driver);
 	if (err)
-		goto err_bus_register;
+		goto err_thread_stop;
 
 	return 0;
 
-err_bus_register:
+err_thread_stop:
+	kthread_stop(cciss_scan_thread);
+err_bus_unregister:
 	bus_unregister(&cciss_bus_type);
+
 	return err;
 }
 
@@ -4230,6 +4345,7 @@ static void __exit cciss_cleanup(void)
 			cciss_remove_one(hba[i]->pdev);
 		}
 	}
+	kthread_stop(cciss_scan_thread);
 	remove_proc_entry("driver/cciss", NULL);
 	bus_unregister(&cciss_bus_type);
 }
drivers/block/cciss.h +5 -2
index 06a5db2..4fb3639 100644
@@ -2,6 +2,7 @@
 #define CCISS_H
 
 #include <linux/genhd.h>
+#include <linux/mutex.h>
 
 #include "cciss_cmd.h"
 
@@ -108,6 +109,8 @@ struct ctlr_info
 	int			nr_frees; 
 	int			busy_configuring;
 	int			busy_initializing;
+	int			busy_scanning;
+	struct mutex		busy_shutting_down;
 
 	/* This element holds the zero based queue number of the last
 	 * queue to be started.  It is used for fairness.
@@ -122,8 +125,8 @@ struct ctlr_info
 	/* and saved for later processing */
 #endif
 	unsigned char alive;
-	struct completion *rescan_wait;
-	struct task_struct *cciss_scan_thread;
+	struct list_head scan_list;
+	struct completion scan_wait;
 	struct device dev;
 };
 
#3 lore raw

PATCH 3/17 Allow triggering of rescan of logical drive topology via sysfs entry

In reply to Stephen M. Cameron

From: Andrew Patterson <[email protected]>

Added /sys/bus/pci/devices/<dev>/ccissX/rescan sysfs entry used to kick off a rescan that discovers logical drive topology changes.

---

 .../ABI/testing/sysfs-bus-pci-devices-cciss        |    7 ++++
 drivers/block/cciss.c                              |   36 ++++++++++++++++++--
 2 files changed, 40 insertions(+), 3 deletions(-)
Documentation/ABI/testing/sysfs-bus-pci-devices-cciss +7 -0
index 0a92a7c..ac3429d 100644
@@ -31,3 +31,10 @@ Date:		March 2009
 Kernel Version: 2.6.30
 Contact:	[email protected]
 Description:	A symbolic link to /sys/block/cciss!cXdY
+
+Where:		/sys/bus/pci/devices/<dev>/ccissX/rescan
+Date:		August 2009
+Kernel Version:	2.6.31
+Contact:	[email protected]
+Description:	Kicks of a rescan of the controller to discover logical
+		drive topology changes.
drivers/block/cciss.c +33 -3
index e7273a4..763535e 100644
@@ -194,6 +194,7 @@ static int sendcmd_withirq_core(ctlr_info_t *h, CommandList_struct *c,
 static int process_sendcmd_error(ctlr_info_t *h, CommandList_struct *c);
 
 static void fail_all_cmds(unsigned long ctlr);
+static int add_to_scan_list(struct ctlr_info *h);
 static int scan_thread(void *data);
 static int check_for_unit_attention(ctlr_info_t *h, CommandList_struct *c);
 
@@ -459,9 +460,19 @@ static void __devinit cciss_procinit(int i)
 #define to_hba(n) container_of(n, struct ctlr_info, dev)
 #define to_drv(n) container_of(n, drive_info_struct, dev)
 
-static struct device_type cciss_host_type = {
-	.name		= "cciss_host",
-};
+static ssize_t host_store_rescan(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf, size_t count)
+{
+	struct ctlr_info *h = to_hba(dev);
+
+	add_to_scan_list(h);
+	wake_up_process(cciss_scan_thread);
+	wait_for_completion_interruptible(&h->scan_wait);
+
+	return count;
+}
+DEVICE_ATTR(rescan, S_IWUSR, NULL, host_store_rescan);
 
 static ssize_t dev_show_unique_id(struct device *dev,
 				 struct device_attribute *attr,
@@ -565,6 +576,25 @@ static ssize_t dev_show_rev(struct device *dev,
 }
 DEVICE_ATTR(rev, S_IRUGO, dev_show_rev, NULL);
 
+static struct attribute *cciss_host_attrs[] = {
+	&dev_attr_rescan.attr,
+	NULL
+};
+
+static struct attribute_group cciss_host_attr_group = {
+	.attrs = cciss_host_attrs,
+};
+
+static struct attribute_group *cciss_host_attr_groups[] = {
+	&cciss_host_attr_group,
+	NULL
+};
+
+static struct device_type cciss_host_type = {
+	.name		= "cciss_host",
+	.groups		= cciss_host_attr_groups,
+};
+
 static struct attribute *cciss_dev_attrs[] = {
 	&dev_attr_unique_id.attr,
 	&dev_attr_model.attr,
#4 lore raw

PATCH 4/17 Remove some unused code in rebuild_lun_table()

In reply to Stephen M. Cameron

Remove some unused code in rebuild_lun_table()

---

 drivers/block/cciss.c |    8 --------
 1 files changed, 0 insertions(+), 8 deletions(-)
drivers/block/cciss.c +0 -8
index 763535e..1d2d614 100644
@@ -1707,7 +1707,6 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time)
 	unsigned long flags = 0;
 	int ret = 0;
 	drive_info_struct *drvinfo;
-	int was_only_controller_node;
 
 	/* Get information about the disk and modify the driver structure */
 	inq_buff = kmalloc(sizeof(InquiryData_struct), GFP_KERNEL);
@@ -1715,13 +1714,6 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time)
 	if (inq_buff == NULL || drvinfo == NULL)
 		goto mem_msg;
 
-	/* See if we're trying to update the "controller node"
-	 * this will happen the when the first logical drive gets
-	 * created by ACU.
-	 */
-	was_only_controller_node = (drv_index == 0 &&
-				h->drv[0].raid_level == -1);
-
 	/* testing to see if 16-byte CDBs are already being used */
 	if (h->cciss_read == CCISS_READ_16) {
 		cciss_read_capacity_16(h->ctlr, drv_index, 1,
#5 lore raw

PATCH 5/17 Dynamically allocate struct device for each logical drive as needed.

In reply to Stephen M. Cameron

Dynamically allocate struct device for each logical drive as needed instead of allocating the maximum we would ever need at driver init time.

---

 drivers/block/cciss.c |   98 ++++++++++++++++++++++++++++++++++++-------------
 drivers/block/cciss.h |    2 +
 2 files changed, 73 insertions(+), 27 deletions(-)
drivers/block/cciss.c +72 -26
index 1d2d614..4f9ef49 100644
@@ -197,6 +197,8 @@ static void fail_all_cmds(unsigned long ctlr);
 static int add_to_scan_list(struct ctlr_info *h);
 static int scan_thread(void *data);
 static int check_for_unit_attention(ctlr_info_t *h, CommandList_struct *c);
+static void cciss_hba_release(struct device *dev);
+static void cciss_device_release(struct device *dev);
 
 #ifdef CONFIG_PROC_FS
 static void cciss_procinit(int i);
@@ -458,7 +460,6 @@ static void __devinit cciss_procinit(int i)
 #define MAX_PRODUCT_NAME_LEN 19
 
 #define to_hba(n) container_of(n, struct ctlr_info, dev)
-#define to_drv(n) container_of(n, drive_info_struct, dev)
 
 static ssize_t host_store_rescan(struct device *dev,
 				 struct device_attribute *attr,
@@ -478,8 +479,8 @@ static ssize_t dev_show_unique_id(struct device *dev,
 				 struct device_attribute *attr,
 				 char *buf)
 {
-	drive_info_struct *drv = to_drv(dev);
-	struct ctlr_info *h = to_hba(drv->dev.parent);
+	drive_info_struct *drv = dev_get_drvdata(dev);
+	struct ctlr_info *h = to_hba(drv->dev->parent);
 	__u8 sn[16];
 	unsigned long flags;
 	int ret = 0;
@@ -508,8 +509,8 @@ static ssize_t dev_show_vendor(struct device *dev,
 			       struct device_attribute *attr,
 			       char *buf)
 {
-	drive_info_struct *drv = to_drv(dev);
-	struct ctlr_info *h = to_hba(drv->dev.parent);
+	drive_info_struct *drv = dev_get_drvdata(dev);
+	struct ctlr_info *h = to_hba(drv->dev->parent);
 	char vendor[VENDOR_LEN + 1];
 	unsigned long flags;
 	int ret = 0;
@@ -532,8 +533,8 @@ static ssize_t dev_show_model(struct device *dev,
 			      struct device_attribute *attr,
 			      char *buf)
 {
-	drive_info_struct *drv = to_drv(dev);
-	struct ctlr_info *h = to_hba(drv->dev.parent);
+	drive_info_struct *drv = dev_get_drvdata(dev);
+	struct ctlr_info *h = to_hba(drv->dev->parent);
 	char model[MODEL_LEN + 1];
 	unsigned long flags;
 	int ret = 0;
@@ -556,8 +557,8 @@ static ssize_t dev_show_rev(struct device *dev,
 			    struct device_attribute *attr,
 			    char *buf)
 {
-	drive_info_struct *drv = to_drv(dev);
-	struct ctlr_info *h = to_hba(drv->dev.parent);
+	drive_info_struct *drv = dev_get_drvdata(dev);
+	struct ctlr_info *h = to_hba(drv->dev->parent);
 	char rev[REV_LEN + 1];
 	unsigned long flags;
 	int ret = 0;
@@ -593,6 +594,7 @@ static struct attribute_group *cciss_host_attr_groups[] = {
 static struct device_type cciss_host_type = {
 	.name		= "cciss_host",
 	.groups		= cciss_host_attr_groups,
+	.release	= cciss_hba_release,
 };
 
 static struct attribute *cciss_dev_attrs[] = {
@@ -615,12 +617,24 @@ static struct attribute_group *cciss_dev_attr_groups[] = {
 static struct device_type cciss_dev_type = {
 	.name		= "cciss_device",
 	.groups		= cciss_dev_attr_groups,
+	.release	= cciss_device_release,
 };
 
 static struct bus_type cciss_bus_type = {
 	.name		= "cciss",
 };
 
+/*
+ * cciss_hba_release is called when the reference count
+ * of h->dev goes to zero.
+ */
+static void cciss_hba_release(struct device *dev)
+{
+	/*
+	 * nothing to do, but need this to avoid a warning
+	 * about not having a release handler from lib/kref.c.
+	 */
+}
 
 /*
  * Initialize sysfs entry for each controller.  This sets up and registers
@@ -644,6 +658,15 @@ static int cciss_create_hba_sysfs_entry(struct ctlr_info *h)
 static void cciss_destroy_hba_sysfs_entry(struct ctlr_info *h)
 {
 	device_del(&h->dev);
+	put_device(&h->dev); /* final put. */
+}
+
+/* cciss_device_release is called when the reference count
+ * of h->drv[x].dev goes to zero.
+ */
+static void cciss_device_release(struct device *dev)
+{
+	kfree(dev);
 }
 
 /*
@@ -652,24 +675,33 @@ static void cciss_destroy_hba_sysfs_entry(struct ctlr_info *h)
  * /sys/bus/pci/devices/<dev/ccis#/. We also create a link from
  * /sys/block/cciss!c#d# to this entry.
  */
-static int cciss_create_ld_sysfs_entry(struct ctlr_info *h,
-				       drive_info_struct *drv,
+static long cciss_create_ld_sysfs_entry(struct ctlr_info *h,
 				       int drv_index)
 {
-	device_initialize(&drv->dev);
-	drv->dev.type = &cciss_dev_type;
-	drv->dev.bus = &cciss_bus_type;
-	dev_set_name(&drv->dev, "c%dd%d", h->ctlr, drv_index);
-	drv->dev.parent = &h->dev;
-	return device_add(&drv->dev);
+	struct device *dev;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+	device_initialize(dev);
+	dev->type = &cciss_dev_type;
+	dev->bus = &cciss_bus_type;
+	dev_set_name(dev, "c%dd%d", h->ctlr, drv_index);
+	dev->parent = &h->dev;
+	h->drv[drv_index].dev = dev;
+	dev_set_drvdata(dev, &h->drv[drv_index]);
+	return device_add(dev);
 }
 
 /*
  * Remove sysfs entries for a logical drive.
  */
-static void cciss_destroy_ld_sysfs_entry(drive_info_struct *drv)
+static void cciss_destroy_ld_sysfs_entry(struct ctlr_info *h, int drv_index)
 {
-	device_del(&drv->dev);
+	struct device *dev = h->drv[drv_index].dev;
+	device_del(dev);
+	put_device(dev); /* the "final" put. */
+	h->drv[drv_index].dev = NULL;
 }
 
 /*
@@ -1650,7 +1682,10 @@ static void cciss_get_serial_no(int ctlr, int logvol, int withirq,
 	return;
 }
 
-static void cciss_add_disk(ctlr_info_t *h, struct gendisk *disk,
+/*
+ * cciss_add_disk sets up the block device queue for a logical drive
+ */
+static int cciss_add_disk(ctlr_info_t *h, struct gendisk *disk,
 				int drv_index)
 {
 	disk->queue = blk_init_queue(do_cciss_request, &h->lock);
@@ -1658,8 +1693,12 @@ static void cciss_add_disk(ctlr_info_t *h, struct gendisk *disk,
 	disk->major = h->major;
 	disk->first_minor = drv_index << NWD_SHIFT;
 	disk->fops = &cciss_fops;
+	if (h->drv[drv_index].dev == NULL) {
+		if (cciss_create_ld_sysfs_entry(h, drv_index))
+			goto cleanup_queue;
+	}
 	disk->private_data = &h->drv[drv_index];
-	disk->driverfs_dev = &h->drv[drv_index].dev;
+	disk->driverfs_dev = h->drv[drv_index].dev;
 
 	/* Set up queue information */
 	blk_queue_bounce_limit(disk->queue, h->pdev->dma_mask);
@@ -1685,6 +1724,12 @@ static void cciss_add_disk(ctlr_info_t *h, struct gendisk *disk,
 	wmb();
 	h->drv[drv_index].queue = disk->queue;
 	add_disk(disk);
+	return 0;
+
+cleanup_queue:
+	blk_cleanup_queue(disk->queue);
+	disk->queue = NULL;
+	return -1;
 }
 
 /* This function will check the usage_count of the drive to be updated/added.
@@ -1870,7 +1915,7 @@ static int cciss_add_gendisk(ctlr_info_t *h, __u32 lunid, int controller_node)
 		}
 	}
 	h->drv[drv_index].LunID = lunid;
-	if (cciss_create_ld_sysfs_entry(h, &h->drv[drv_index], drv_index))
+	if (cciss_create_ld_sysfs_entry(h, drv_index))
 		goto err_free_disk;
 
 	/* Don't need to mark this busy because nobody */
@@ -2144,7 +2189,7 @@ static int deregister_disk(ctlr_info_t *h, int drv_index,
 				 * indicate that this element of the drive
 				 * array is free.
 				 */
-	cciss_destroy_ld_sysfs_entry(drv);
+	cciss_destroy_ld_sysfs_entry(h, drv_index);
 
 	if (clear_all) {
 		/* check to see if it was the last disk */
@@ -4267,8 +4312,9 @@ static void __devexit cciss_remove_one(struct pci_dev *pdev)
 			if (q)
 				blk_cleanup_queue(q);
 		}
-		if (hba[i]->drv[j].raid_level != -1)
-			cciss_destroy_ld_sysfs_entry(&hba[i]->drv[j]);
+		if (hba[i]->drv[j].dev != NULL &&
+			(j == 0 || hba[i]->drv[j].raid_level != -1))
+				cciss_destroy_ld_sysfs_entry(hba[i], j);
 
 	}
 
@@ -4344,7 +4390,7 @@ static int __init cciss_init(void)
 	if (err)
 		goto err_thread_stop;
 
-	return 0;
+	return err;
 
 err_thread_stop:
 	kthread_stop(cciss_scan_thread);
drivers/block/cciss.h +1 -1
index 4fb3639..9679342 100644
@@ -45,7 +45,7 @@ typedef struct _drive_info_struct
 				   * to prevent it from being opened or it's
 				   * queue from being started.
 				   */
-	struct	device dev;
+	struct	device *dev;
 	__u8 serial_no[16]; /* from inquiry page 0x83,
 			     * not necc. null terminated.
 			     */
#6 lore raw

PATCH 6/17 Rearrange logical drive sysfs code to make the "changing a disk" path work.

In reply to Stephen M. Cameron

Rearrange logical drive sysfs code to make the "changing a disk" path work.

---

 drivers/block/cciss.c |   20 ++++++++++----------
 1 files changed, 10 insertions(+), 10 deletions(-)
drivers/block/cciss.c +10 -10
index 4f9ef49..557b958 100644
@@ -1915,9 +1915,10 @@ static int cciss_add_gendisk(ctlr_info_t *h, __u32 lunid, int controller_node)
 		}
 	}
 	h->drv[drv_index].LunID = lunid;
-	if (cciss_create_ld_sysfs_entry(h, drv_index))
-		goto err_free_disk;
-
+	if (h->drv[drv_index].dev == NULL) {
+		if (cciss_create_ld_sysfs_entry(h, drv_index))
+			goto err_free_disk;
+	}
 	/* Don't need to mark this busy because nobody */
 	/* else knows about this disk yet to contend */
 	/* for access to it. */
@@ -2144,8 +2145,10 @@ static int deregister_disk(ctlr_info_t *h, int drv_index,
 	 */
 	if (h->gendisk[0] != disk) {
 		struct request_queue *q = disk->queue;
-		if (disk->flags & GENHD_FL_UP)
+		if (disk->flags & GENHD_FL_UP) {
+			cciss_destroy_ld_sysfs_entry(h, drv_index);
 			del_gendisk(disk);
+		}
 		if (q) {
 			blk_cleanup_queue(q);
 			/* Set drv->queue to NULL so that we do not try
@@ -2189,7 +2192,6 @@ static int deregister_disk(ctlr_info_t *h, int drv_index,
 				 * indicate that this element of the drive
 				 * array is free.
 				 */
-	cciss_destroy_ld_sysfs_entry(h, drv_index);
 
 	if (clear_all) {
 		/* check to see if it was the last disk */
@@ -4307,15 +4309,13 @@ static void __devexit cciss_remove_one(struct pci_dev *pdev)
 		if (disk) {
 			struct request_queue *q = disk->queue;
 
-			if (disk->flags & GENHD_FL_UP)
+			if (disk->flags & GENHD_FL_UP) {
+				cciss_destroy_ld_sysfs_entry(hba[i], j);
 				del_gendisk(disk);
+			}
 			if (q)
 				blk_cleanup_queue(q);
 		}
-		if (hba[i]->drv[j].dev != NULL &&
-			(j == 0 || hba[i]->drv[j].raid_level != -1))
-				cciss_destroy_ld_sysfs_entry(hba[i], j);
-
 	}
 
 #ifdef CONFIG_CISS_SCSI_TAPE
#7 lore raw

PATCH 7/17 Handle failure of blk_init_queue gracefully in cciss_add_disk.

In reply to Stephen M. Cameron

Handle failure of blk_init_queue gracefully in cciss_add_disk.

---

 drivers/block/cciss.c |    3 +++
 1 files changed, 3 insertions(+), 0 deletions(-)
drivers/block/cciss.c +3 -0
index 557b958..60fe3c6 100644
@@ -1689,6 +1689,8 @@ static int cciss_add_disk(ctlr_info_t *h, struct gendisk *disk,
 				int drv_index)
 {
 	disk->queue = blk_init_queue(do_cciss_request, &h->lock);
+	if (!disk->queue)
+		goto init_queue_failure;
 	sprintf(disk->disk_name, "cciss/c%dd%d", h->ctlr, drv_index);
 	disk->major = h->major;
 	disk->first_minor = drv_index << NWD_SHIFT;
@@ -1729,6 +1731,7 @@ static int cciss_add_disk(ctlr_info_t *h, struct gendisk *disk,
 cleanup_queue:
 	blk_cleanup_queue(disk->queue);
 	disk->queue = NULL;
+init_queue_failure:
 	return -1;
 }
 
#8 lore raw

PATCH 8/17 Handle cases when cciss_add_disk fails.

In reply to Stephen M. Cameron

Handle cases when cciss_add_disk fails.

---

 drivers/block/cciss.c |   35 +++++++++++++++++++++++++----------
 1 files changed, 25 insertions(+), 10 deletions(-)
drivers/block/cciss.c +25 -10
index 60fe3c6..7043b3b 100644
@@ -199,6 +199,7 @@ static int scan_thread(void *data);
 static int check_for_unit_attention(ctlr_info_t *h, CommandList_struct *c);
 static void cciss_hba_release(struct device *dev);
 static void cciss_device_release(struct device *dev);
+static void cciss_free_gendisk(ctlr_info_t *h, int drv_index);
 
 #ifdef CONFIG_PROC_FS
 static void cciss_procinit(int i);
@@ -1855,8 +1856,14 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time)
 	 * (raid_leve == -1) then we want to update the
 	 * logical drive's information.
 	 */
-	if (drv_index || first_time)
-		cciss_add_disk(h, disk, drv_index);
+	if (drv_index || first_time) {
+		if (cciss_add_disk(h, disk, drv_index) != 0) {
+			cciss_free_gendisk(h, drv_index);
+			printk(KERN_WARNING "cciss:%d could not update "
+				"disk %d\n", h->ctlr, drv_index);
+			--h->num_luns;
+		}
+	}
 
 freeret:
 	kfree(inq_buff);
@@ -1890,6 +1897,12 @@ static int cciss_find_free_drive_index(int ctlr, int controller_node)
 	return -1;
 }
 
+static void cciss_free_gendisk(ctlr_info_t *h, int drv_index)
+{
+	put_disk(h->gendisk[drv_index]);
+	h->gendisk[drv_index] = NULL;
+}
+
 /* cciss_add_gendisk finds a free hba[]->drv structure
  * and allocates a gendisk if needed, and sets the lunid
  * in the drvinfo structure.   It returns the index into
@@ -1930,8 +1943,7 @@ static int cciss_add_gendisk(ctlr_info_t *h, __u32 lunid, int controller_node)
 	return drv_index;
 
 err_free_disk:
-	put_disk(h->gendisk[drv_index]);
-	h->gendisk[drv_index] = NULL;
+	cciss_free_gendisk(h, drv_index);
 	return -1;
 }
 
@@ -1949,11 +1961,8 @@ static void cciss_add_controller_node(ctlr_info_t *h)
 		return;
 
 	drv_index = cciss_add_gendisk(h, 0, 1);
-	if (drv_index == -1) {
-		printk(KERN_WARNING "cciss%d: could not "
-			"add disk 0.\n", h->ctlr);
-		return;
-	}
+	if (drv_index == -1)
+		goto error;
 	h->drv[drv_index].block_size = 512;
 	h->drv[drv_index].nr_blocks = 0;
 	h->drv[drv_index].heads = 0;
@@ -1962,7 +1971,13 @@ static void cciss_add_controller_node(ctlr_info_t *h)
 	h->drv[drv_index].raid_level = -1;
 	memset(h->drv[drv_index].serial_no, 0, 16);
 	disk = h->gendisk[drv_index];
-	cciss_add_disk(h, disk, drv_index);
+	if (cciss_add_disk(h, disk, drv_index) == 0)
+		return;
+	cciss_free_gendisk(h, drv_index);
+error:
+	printk(KERN_WARNING "cciss%d: could not "
+		"add disk 0.\n", h->ctlr);
+	return;
 }
 
 /* This function will add and remove logical drives from the Logical
#9 lore raw

PATCH 9/17 Handle special case for sysfs attributes of the first logical drive.

In reply to Stephen M. Cameron

For c0dx where x is not 0, we handle deletion and addition simply, but for c0d0, there is the special case that even when there's no disk, the device node exists so that the controller may be accessed. So, for c0d0, we only create the sysfs entries once, when a controller is added, and only remove them once, when a controller is being taken down.

---

 drivers/block/cciss.c |   18 ++++++++++++++----
 1 files changed, 14 insertions(+), 4 deletions(-)
drivers/block/cciss.c +14 -4
index 7043b3b..98d8efe 100644
@@ -681,6 +681,10 @@ static long cciss_create_ld_sysfs_entry(struct ctlr_info *h,
 {
 	struct device *dev;
 
+	/* Special case for c*d0, we only create it once. */
+	if (drv_index == 0 && h->drv[drv_index].dev != NULL)
+		return 0;
+
 	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
 	if (!dev)
 		return -ENOMEM;
@@ -697,9 +701,15 @@ static long cciss_create_ld_sysfs_entry(struct ctlr_info *h,
 /*
  * Remove sysfs entries for a logical drive.
  */
-static void cciss_destroy_ld_sysfs_entry(struct ctlr_info *h, int drv_index)
+static void cciss_destroy_ld_sysfs_entry(struct ctlr_info *h, int drv_index,
+	int ctlr_exiting)
 {
 	struct device *dev = h->drv[drv_index].dev;
+
+	/* special case for c*d0, we only destroy it on controller exit */
+	if (drv_index == 0 && !ctlr_exiting)
+		return;
+
 	device_del(dev);
 	put_device(dev); /* the "final" put. */
 	h->drv[drv_index].dev = NULL;
@@ -1919,6 +1929,7 @@ static int cciss_add_gendisk(ctlr_info_t *h, __u32 lunid, int controller_node)
 	drv_index = cciss_find_free_drive_index(h->ctlr, controller_node);
 	if (drv_index == -1)
 		return -1;
+
 	/*Check if the gendisk needs to be allocated */
 	if (!h->gendisk[drv_index]) {
 		h->gendisk[drv_index] =
@@ -2164,7 +2175,7 @@ static int deregister_disk(ctlr_info_t *h, int drv_index,
 	if (h->gendisk[0] != disk) {
 		struct request_queue *q = disk->queue;
 		if (disk->flags & GENHD_FL_UP) {
-			cciss_destroy_ld_sysfs_entry(h, drv_index);
+			cciss_destroy_ld_sysfs_entry(h, drv_index, 0);
 			del_gendisk(disk);
 		}
 		if (q) {
@@ -2210,7 +2221,6 @@ static int deregister_disk(ctlr_info_t *h, int drv_index,
 				 * indicate that this element of the drive
 				 * array is free.
 				 */
-
 	if (clear_all) {
 		/* check to see if it was the last disk */
 		if (drv == h->drv + h->highest_lun) {
@@ -4328,7 +4338,7 @@ static void __devexit cciss_remove_one(struct pci_dev *pdev)
 			struct request_queue *q = disk->queue;
 
 			if (disk->flags & GENHD_FL_UP) {
-				cciss_destroy_ld_sysfs_entry(hba[i], j);
+				cciss_destroy_ld_sysfs_entry(hba[i], j, 1);
 				del_gendisk(disk);
 			}
 			if (q)
#10 lore raw

PATCH 10/17 Clear all sysfs-exposed data for deleted logical drives.

In reply to Stephen M. Cameron

When removing a logical drive, clear all the information that is now exposed by sysfs (e.g. vendor, model, serial number.)

---

 drivers/block/cciss.c |   31 +++++++++++++++++++++----------
 1 files changed, 21 insertions(+), 10 deletions(-)
drivers/block/cciss.c +21 -10
index 98d8efe..8cd2f8e 100644
@@ -2133,6 +2133,25 @@ mem_msg:
 	goto freeret;
 }
 
+static void cciss_clear_drive_info(drive_info_struct *drive_info)
+{
+	/* zero out the disk size info */
+	drive_info->nr_blocks = 0;
+	drive_info->block_size = 0;
+	drive_info->heads = 0;
+	drive_info->sectors = 0;
+	drive_info->cylinders = 0;
+	drive_info->raid_level = -1;
+	memset(drive_info->serial_no, 0, sizeof(drive_info->serial_no));
+	memset(drive_info->model, 0, sizeof(drive_info->model));
+	memset(drive_info->rev, 0, sizeof(drive_info->rev));
+	memset(drive_info->vendor, 0, sizeof(drive_info->vendor));
+	/*
+	 * don't clear the LUNID though, we need to remember which
+	 * one this one is.
+	 */
+}
+
 /* This function will deregister the disk and it's queue from the
  * kernel.  It must be called with the controller lock held and the
  * drv structures busy_configuring flag set.  It's parameters are:
@@ -2211,16 +2230,8 @@ static int deregister_disk(ctlr_info_t *h, int drv_index,
 	}
 
 	--h->num_luns;
-	/* zero out the disk size info */
-	drv->nr_blocks = 0;
-	drv->block_size = 0;
-	drv->heads = 0;
-	drv->sectors = 0;
-	drv->cylinders = 0;
-	drv->raid_level = -1;	/* This can be used as a flag variable to
-				 * indicate that this element of the drive
-				 * array is free.
-				 */
+	cciss_clear_drive_info(drv);
+
 	if (clear_all) {
 		/* check to see if it was the last disk */
 		if (drv == h->drv + h->highest_lun) {
#11 lore raw

PATCH 11/17 Fix usage_count check in rebuild_lun_table when triggered via sysfs.

In reply to Stephen M. Cameron

When rebuild_lun_table is reached via sysfs, the usage count that is checked prior to messing with c0d0 has different constraints (must be zero) than if rebuild_lun_table is reached via ioctl (must be one.) Fix rebuild_lun_table to take that into account.

---

 drivers/block/cciss.c |   33 +++++++++++++++++++++------------
 1 files changed, 21 insertions(+), 12 deletions(-)
drivers/block/cciss.c +21 -12
index 8cd2f8e..14d4cd6 100644
@@ -169,9 +169,9 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode,
 static int cciss_getgeo(struct block_device *bdev, struct hd_geometry *geo);
 
 static int cciss_revalidate(struct gendisk *disk);
-static int rebuild_lun_table(ctlr_info_t *h, int first_time);
+static int rebuild_lun_table(ctlr_info_t *h, int first_time, int via_ioctl);
 static int deregister_disk(ctlr_info_t *h, int drv_index,
-			   int clear_all);
+			   int clear_all, int via_ioctl);
 
 static void cciss_read_capacity(int ctlr, int logvol, int withirq,
 			sector_t *total_size, unsigned int *block_size);
@@ -1210,7 +1210,7 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode,
 	case CCISS_DEREGDISK:
 	case CCISS_REGNEWD:
 	case CCISS_REVALIDVOLS:
-		return rebuild_lun_table(host, 0);
+		return rebuild_lun_table(host, 0, 1);
 
 	case CCISS_GETLUNINFO:{
 			LogvolInfo_struct luninfo;
@@ -1756,7 +1756,8 @@ init_queue_failure:
  * is also the controller node.  Any changes to disk 0 will show up on
  * the next reboot.
  */
-static void cciss_update_drive_info(int ctlr, int drv_index, int first_time)
+static void cciss_update_drive_info(int ctlr, int drv_index, int first_time,
+	int via_ioctl)
 {
 	ctlr_info_t *h = hba[ctlr];
 	struct gendisk *disk;
@@ -1834,7 +1835,7 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time)
 		 * which keeps the interrupt handler from starting
 		 * the queue.
 		 */
-		ret = deregister_disk(h, drv_index, 0);
+		ret = deregister_disk(h, drv_index, 0, via_ioctl);
 		h->drv[drv_index].busy_configuring = 0;
 	}
 
@@ -1999,7 +2000,8 @@ error:
  * INPUT
  * h		= The controller to perform the operations on
  */
-static int rebuild_lun_table(ctlr_info_t *h, int first_time)
+static int rebuild_lun_table(ctlr_info_t *h, int first_time,
+	int via_ioctl)
 {
 	int ctlr = h->ctlr;
 	int num_luns;
@@ -2078,7 +2080,7 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time)
 			spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
 			h->drv[i].busy_configuring = 1;
 			spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
-			return_code = deregister_disk(h, i, 1);
+			return_code = deregister_disk(h, i, 1, via_ioctl);
 			h->drv[i].busy_configuring = 0;
 		}
 	}
@@ -2116,7 +2118,8 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time)
 			if (drv_index == -1)
 				goto freeret;
 		}
-		cciss_update_drive_info(ctlr, drv_index, first_time);
+		cciss_update_drive_info(ctlr, drv_index, first_time,
+			via_ioctl);
 	}		/* end for */
 
 freeret:
@@ -2166,9 +2169,15 @@ static void cciss_clear_drive_info(drive_info_struct *drive_info)
  *             the disk in preparation for re-adding it.  In this case
  *             the highest_lun should be left unchanged and the LunID
  *             should not be cleared.
+ * via_ioctl
+ *    This indicates whether we've reached this path via ioctl.
+ *    This affects the maximum usage count allowed for c0d0 to be messed with.
+ *    If this path is reached via ioctl(), then the max_usage_count will
+ *    be 1, as the process calling ioctl() has got to have the device open.
+ *    If we get here via sysfs, then the max usage count will be zero.
 */
 static int deregister_disk(ctlr_info_t *h, int drv_index,
-			   int clear_all)
+			   int clear_all, int via_ioctl)
 {
 	int i;
 	struct gendisk *disk;
@@ -2182,7 +2191,7 @@ static int deregister_disk(ctlr_info_t *h, int drv_index,
 
 	/* make sure logical volume is NOT is use */
 	if (clear_all || (h->gendisk[0] == disk)) {
-		if (drv->usage_count > 1)
+		if (drv->usage_count > via_ioctl)
 			return -EBUSY;
 	} else if (drv->usage_count > 0)
 		return -EBUSY;
@@ -3451,7 +3460,7 @@ static int scan_thread(void *data)
 			mutex_unlock(&scan_mutex);
 
 			if (h) {
-				rebuild_lun_table(h, 0);
+				rebuild_lun_table(h, 0, 0);
 				complete_all(&h->scan_wait);
 				mutex_lock(&scan_mutex);
 				h->busy_scanning = 0;
@@ -4251,7 +4260,7 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
 
 	hba[i]->cciss_max_sectors = 2048;
 
-	rebuild_lun_table(hba[i], 1);
+	rebuild_lun_table(hba[i], 1, 0);
 	hba[i]->busy_initializing = 0;
 	return 1;
 
#12 lore raw

PATCH 12/17 Fix excessive gendisk freeing bug on driver unload.

In reply to Stephen M. Cameron

Fix bug that free_hba was calling put_disk for all gendisk[] pointers -- all 1024 of them -- regardless of whether the were used or not (NULL). This bug could cause rmmod to oops if logical drives had been deleted during the driver's lifetime.

---

 drivers/block/cciss.c |   15 ++++++++-------
 1 files changed, 8 insertions(+), 7 deletions(-)
drivers/block/cciss.c +8 -7
index 14d4cd6..a4cb041 100644
@@ -3892,15 +3892,16 @@ Enomem:
 	return -1;
 }
 
-static void free_hba(int i)
+static void free_hba(int n)
 {
-	ctlr_info_t *p = hba[i];
-	int n;
+	ctlr_info_t *h = hba[n];
+	int i;
 
-	hba[i] = NULL;
-	for (n = 0; n < CISS_MAX_LUN; n++)
-		put_disk(p->gendisk[n]);
-	kfree(p);
+	hba[n] = NULL;
+	for (i = 0; i < h->highest_lun + 1; i++)
+		if (h->gendisk[i] != NULL)
+			put_disk(h->gendisk[i]);
+	kfree(h);
 }
 
 /* Send a message CDB to the firmware. */
#13 lore raw

PATCH 13/17 Silence noisy per-disk messages output by cciss_read_capacity

In reply to Stephen M. Cameron

Silence noisy per-disk messages output by cciss_read_capacity

---

 drivers/block/cciss.c |    5 -----
 1 files changed, 0 insertions(+), 5 deletions(-)
drivers/block/cciss.c +0 -5
index a4cb041..239c4f5 100644
@@ -2601,8 +2601,6 @@ static void cciss_geometry_inquiry(int ctlr, int logvol,
 	} else {		/* Get geometry failed */
 		printk(KERN_WARNING "cciss: reading geometry failed\n");
 	}
-	printk(KERN_INFO "      heads=%d, sectors=%d, cylinders=%d\n\n",
-	       drv->heads, drv->sectors, drv->cylinders);
 }
 
 static void
@@ -2636,9 +2634,6 @@ cciss_read_capacity(int ctlr, int logvol, int withirq, sector_t *total_size,
 		*total_size = 0;
 		*block_size = BLOCK_SIZE;
 	}
-	if (*total_size != 0)
-		printk(KERN_INFO "      blocks= %llu block_size= %d\n",
-		(unsigned long long)*total_size+1, *block_size);
 	kfree(buf);
 }
 
#14 lore raw

PATCH 14/17 Preserve all 8 bytes of LUN ID for logical drives.

In reply to Stephen M. Cameron

Preserve all 8 bytes of the LunID field returned
by CCISS_REPORT_LOGICAL instead of only saving 4 bytes. This fixes a bug with logical volume addressing encountered on an MSA2012.

---

 drivers/block/cciss.c |   48 ++++++++++++++++++++++++------------------------
 drivers/block/cciss.h |    2 +-
 2 files changed, 25 insertions(+), 25 deletions(-)
drivers/block/cciss.c +24 -24
index 239c4f5..56eeb68 100644
@@ -845,7 +845,8 @@ static int cciss_open(struct block_device *bdev, fmode_t mode)
 			if (MINOR(bdev->bd_dev) & 0x0f) {
 				return -ENXIO;
 				/* if it is, make sure we have a LUN ID */
-			} else if (drv->LunID == 0) {
+			} else if (memcmp(drv->LunID, CTLR_LUNID,
+				sizeof(drv->LunID))) {
 				return -ENXIO;
 			}
 		}
@@ -1215,7 +1216,8 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode,
 	case CCISS_GETLUNINFO:{
 			LogvolInfo_struct luninfo;
 
-			luninfo.LunID = drv->LunID;
+			memcpy(&luninfo.LunID, drv->LunID,
+				sizeof(luninfo.LunID));
 			luninfo.num_opens = drv->usage_count;
 			luninfo.num_parts = 0;
 			if (copy_to_user(argp, &luninfo,
@@ -1610,13 +1612,11 @@ static void cciss_softirq_done(struct request *rq)
 	spin_unlock_irqrestore(&h->lock, flags);
 }
 
-static void log_unit_to_scsi3addr(ctlr_info_t *h, unsigned char scsi3addr[],
-	uint32_t log_unit)
+static inline void log_unit_to_scsi3addr(ctlr_info_t *h,
+	unsigned char scsi3addr[], uint32_t log_unit)
 {
-	log_unit = h->drv[log_unit].LunID & 0x03fff;
-	memset(&scsi3addr[4], 0, 4);
-	memcpy(&scsi3addr[0], &log_unit, 4);
-	scsi3addr[3] |= 0x40;
+	memcpy(scsi3addr, h->drv[log_unit].LunID,
+		sizeof(h->drv[log_unit].LunID));
 }
 
 /* This function gets the SCSI vendor, model, and revision of a logical drive
@@ -1923,7 +1923,8 @@ static void cciss_free_gendisk(ctlr_info_t *h, int drv_index)
  * a means to talk to the controller in case no logical
  * drives have yet been configured.
  */
-static int cciss_add_gendisk(ctlr_info_t *h, __u32 lunid, int controller_node)
+static int cciss_add_gendisk(ctlr_info_t *h, unsigned char lunid[],
+	int controller_node)
 {
 	int drv_index;
 
@@ -1942,7 +1943,8 @@ static int cciss_add_gendisk(ctlr_info_t *h, __u32 lunid, int controller_node)
 			return -1;
 		}
 	}
-	h->drv[drv_index].LunID = lunid;
+	memcpy(h->drv[drv_index].LunID, lunid,
+		sizeof(h->drv[drv_index].LunID));
 	if (h->drv[drv_index].dev == NULL) {
 		if (cciss_create_ld_sysfs_entry(h, drv_index))
 			goto err_free_disk;
@@ -1972,7 +1974,7 @@ static void cciss_add_controller_node(ctlr_info_t *h)
 	if (h->gendisk[0] != NULL) /* already did this? Then bail. */
 		return;
 
-	drv_index = cciss_add_gendisk(h, 0, 1);
+	drv_index = cciss_add_gendisk(h, CTLR_LUNID, 1);
 	if (drv_index == -1)
 		goto error;
 	h->drv[drv_index].block_size = 512;
@@ -2011,7 +2013,7 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time,
 	int i;
 	int drv_found;
 	int drv_index = 0;
-	__u32 lunid = 0;
+	unsigned char lunid[8] = CTLR_LUNID;
 	unsigned long flags;
 
 	if (!capable(CAP_SYS_RAWIO))
@@ -2068,9 +2070,9 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time,
 			continue;
 
 		for (j = 0; j < num_luns; j++) {
-			memcpy(&lunid, &ld_buff->LUN[j][0], 4);
-			lunid = le32_to_cpu(lunid);
-			if (h->drv[i].LunID == lunid) {
+			memcpy(lunid, &ld_buff->LUN[j][0], sizeof(lunid));
+			if (memcmp(h->drv[i].LunID, lunid,
+				sizeof(lunid)) == 0) {
 				drv_found = 1;
 				break;
 			}
@@ -2095,9 +2097,7 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time,
 
 		drv_found = 0;
 
-		memcpy(&lunid, &ld_buff->LUN[i][0], 4);
-		lunid = le32_to_cpu(lunid);
-
+		memcpy(lunid, &ld_buff->LUN[i][0], sizeof(lunid));
 		/* Find if the LUN is already in the drive array
 		 * of the driver.  If so then update its info
 		 * if not in use.  If it does not exist then find
@@ -2105,7 +2105,8 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time,
 		 */
 		for (j = 0; j <= h->highest_lun; j++) {
 			if (h->drv[j].raid_level != -1 &&
-				h->drv[j].LunID == lunid) {
+				memcmp(h->drv[j].LunID, lunid,
+					sizeof(h->drv[j].LunID)) == 0) {
 				drv_index = j;
 				drv_found = 1;
 				break;
@@ -2253,8 +2254,7 @@ static int deregister_disk(ctlr_info_t *h, int drv_index,
 			}
 			h->highest_lun = newhighest;
 		}
-
-		drv->LunID = 0;
+		memset(drv->LunID, 0, sizeof(drv->LunID));
 	}
 	return 0;
 }
@@ -2685,7 +2685,8 @@ static int cciss_revalidate(struct gendisk *disk)
 	InquiryData_struct *inq_buff = NULL;
 
 	for (logvol = 0; logvol < CISS_MAX_LUN; logvol++) {
-		if (h->drv[logvol].LunID == drv->LunID) {
+		if (memcmp(h->drv[logvol].LunID, drv->LunID,
+			sizeof(drv->LunID)) == 0) {
 			FOUND = 1;
 			break;
 		}
@@ -3170,8 +3171,7 @@ static void do_cciss_request(struct request_queue *q)
 	/* The first 2 bits are reserved for controller error reporting. */
 	c->Header.Tag.lower = (c->cmdindex << 3);
 	c->Header.Tag.lower |= 0x04;	/* flag for direct lookup. */
-	c->Header.LUN.LogDev.VolId = drv->LunID;
-	c->Header.LUN.LogDev.Mode = 1;
+	memcpy(&c->Header.LUN, drv->LunID, sizeof(drv->LunID));
 	c->Request.CDBLen = 10;	// 12 byte commands not in FW yet;
 	c->Request.Type.Type = TYPE_CMD;	// It is a command.
 	c->Request.Type.Attribute = ATTR_SIMPLE;
drivers/block/cciss.h +1 -1
index 9679342..5188f71 100644
@@ -30,7 +30,7 @@ struct access_method {
 };
 typedef struct _drive_info_struct
 {
- 	__u32   LunID;	
+	unsigned char LunID[8];
 	int 	usage_count;
 	struct request_queue *queue;
 	sector_t nr_blocks;
#15 lore raw

PATCH 15/17 Add lunid attribute to each logical drive in /sys

In reply to Stephen M. Cameron

Add lunid attribute to each logical drive at /sys/devices/<dev>/ccissX/cXdY/lunid for controller X, logical drive Y

---

 .../ABI/testing/sysfs-bus-pci-devices-cciss        |    7 +++++
 drivers/block/cciss.c                              |   26 ++++++++++++++++++++
 2 files changed, 33 insertions(+), 0 deletions(-)
Documentation/ABI/testing/sysfs-bus-pci-devices-cciss +7 -0
index ac3429d..5a6c8d3 100644
@@ -38,3 +38,10 @@ Kernel Version:	2.6.31
 Contact:	[email protected]
 Description:	Kicks of a rescan of the controller to discover logical
 		drive topology changes.
+
+Where:		/sys/bus/pci/devices/<dev>/ccissX/cXdY/lunid
+Date:		August 2009
+Kernel Version: 2.6.31
+Contact:	[email protected]
+Description:	Displays the 8-byte LUN ID used to address logical
+		drive Y of controller X.
drivers/block/cciss.c +26 -0
index 56eeb68..373ef43 100644
@@ -578,6 +578,31 @@ static ssize_t dev_show_rev(struct device *dev,
 }
 DEVICE_ATTR(rev, S_IRUGO, dev_show_rev, NULL);
 
+static ssize_t cciss_show_lunid(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	drive_info_struct *drv = dev_get_drvdata(dev);
+	struct ctlr_info *h = to_hba(drv->dev->parent);
+	unsigned long flags;
+	unsigned char lunid[8];
+
+	spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+	if (h->busy_configuring) {
+		spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+		return -EBUSY;
+	}
+	if (!drv->heads) {
+		spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+		return -ENOTTY;
+	}
+	memcpy(lunid, drv->LunID, sizeof(lunid));
+	spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+	return snprintf(buf, 20, "0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
+		lunid[0], lunid[1], lunid[2], lunid[3],
+		lunid[4], lunid[5], lunid[6], lunid[7]);
+}
+DEVICE_ATTR(lunid, S_IRUGO, cciss_show_lunid, NULL);
+
 static struct attribute *cciss_host_attrs[] = {
 	&dev_attr_rescan.attr,
 	NULL
@@ -603,6 +628,7 @@ static struct attribute *cciss_dev_attrs[] = {
 	&dev_attr_model.attr,
 	&dev_attr_vendor.attr,
 	&dev_attr_rev.attr,
+	&dev_attr_lunid.attr,
 	NULL
 };
 
#16 lore raw

PATCH 16/17 Add a "raid_level" attribute to each logical drive in /sys

In reply to Stephen M. Cameron

Add raid_level attribute to each logical drive at /sys/devices/<dev>/ccissX/cXdY/raid_level for controller X, logical drive Y

---

 .../ABI/testing/sysfs-bus-pci-devices-cciss        |    7 ++++++
 drivers/block/cciss.c                              |   24 ++++++++++++++++++++
 2 files changed, 31 insertions(+), 0 deletions(-)
Documentation/ABI/testing/sysfs-bus-pci-devices-cciss +7 -0
index 5a6c8d3..8d02602 100644
@@ -45,3 +45,10 @@ Kernel Version: 2.6.31
 Contact:	[email protected]
 Description:	Displays the 8-byte LUN ID used to address logical
 		drive Y of controller X.
+
+Where:		/sys/bus/pci/devices/<dev>/ccissX/cXdY/raid_level
+Date:		August 2009
+Kernel Version: 2.6.31
+Contact:	[email protected]
+Description:	Displays the RAID level of logical drive Y of
+		controller X.
drivers/block/cciss.c +24 -0
index 373ef43..2ad089e 100644
@@ -603,6 +603,29 @@ static ssize_t cciss_show_lunid(struct device *dev,
 }
 DEVICE_ATTR(lunid, S_IRUGO, cciss_show_lunid, NULL);
 
+static ssize_t cciss_show_raid_level(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	drive_info_struct *drv = dev_get_drvdata(dev);
+	struct ctlr_info *h = to_hba(drv->dev->parent);
+	int raid;
+	unsigned long flags;
+
+	spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+	if (h->busy_configuring) {
+		spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+		return -EBUSY;
+	}
+	raid = drv->raid_level;
+	spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+	if (raid < 0 || raid > 5)
+		raid = RAID_UNKNOWN;
+
+	return snprintf(buf, strlen(raid_label[raid]) + 7, "RAID %s\n",
+			raid_label[raid]);
+}
+DEVICE_ATTR(raid_level, S_IRUGO, cciss_show_raid_level, NULL);
+
 static struct attribute *cciss_host_attrs[] = {
 	&dev_attr_rescan.attr,
 	NULL
@@ -629,6 +652,7 @@ static struct attribute *cciss_dev_attrs[] = {
 	&dev_attr_vendor.attr,
 	&dev_attr_rev.attr,
 	&dev_attr_lunid.attr,
+	&dev_attr_raid_level.attr,
 	NULL
 };
 
#17 lore raw

PATCH 17/17 Add usage_count attribute to each logical drive in /sys

In reply to Stephen M. Cameron

Add usage_count attribute to each logical drive at /sys/devices/<dev>/ccissX/cXdY/usage_count for controller X, logical drive Y. The usage count is the number of times the device has currently been opened.

---

 .../ABI/testing/sysfs-bus-pci-devices-cciss        |    7 +++++++
 drivers/block/cciss.c                              |   20 ++++++++++++++++++++
 2 files changed, 27 insertions(+), 0 deletions(-)
Documentation/ABI/testing/sysfs-bus-pci-devices-cciss +7 -0
index 8d02602..4f29e5f 100644
@@ -52,3 +52,10 @@ Kernel Version: 2.6.31
 Contact:	[email protected]
 Description:	Displays the RAID level of logical drive Y of
 		controller X.
+
+Where:		/sys/bus/pci/devices/<dev>/ccissX/cXdY/usage_count
+Date:		August 2009
+Kernel Version: 2.6.31
+Contact:	[email protected]
+Description:	Displays the usage count (number of opens) of logical drive Y
+		of controller X.
drivers/block/cciss.c +20 -0
index 2ad089e..a8b7aef 100644
@@ -626,6 +626,25 @@ static ssize_t cciss_show_raid_level(struct device *dev,
 }
 DEVICE_ATTR(raid_level, S_IRUGO, cciss_show_raid_level, NULL);
 
+static ssize_t cciss_show_usage_count(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	drive_info_struct *drv = dev_get_drvdata(dev);
+	struct ctlr_info *h = to_hba(drv->dev->parent);
+	unsigned long flags;
+	int count;
+
+	spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+	if (h->busy_configuring) {
+		spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+		return -EBUSY;
+	}
+	count = drv->usage_count;
+	spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+	return snprintf(buf, 20, "%d\n", count);
+}
+DEVICE_ATTR(usage_count, S_IRUGO, cciss_show_usage_count, NULL);
+
 static struct attribute *cciss_host_attrs[] = {
 	&dev_attr_rescan.attr,
 	NULL
@@ -653,6 +672,7 @@ static struct attribute *cciss_dev_attrs[] = {
 	&dev_attr_rev.attr,
 	&dev_attr_lunid.attr,
 	&dev_attr_raid_level.attr,
+	&dev_attr_usage_count.attr,
 	NULL
 };