Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > fc11cd6e1c513a17304da94a5390f3cd > files > 3334

kernel-2.6.18-194.11.1.el5.src.rpm

From: Rob Evers <revers@redhat.com>
Date: Mon, 27 Jul 2009 14:22:50 -0400
Subject: [scsi] cciss: add driver sysfs entries
Message-id: 20090727181756.12704.40607.sendpatchset@localhost.localdomain
O-Subject: [RHEL5.4 PATCH 1/2 v2] cciss: backport 'add cciss driver sysfs entries'
Bugzilla: 513070
RH-Acked-by: Prarit Bhargava <prarit@redhat.com>

https://bugzilla.redhat.com/show_bug.cgi?id=513070

Description:

    From scsi-misc git:

        cciss: add cciss driver sysfs entries

	Add sysfs entries to the cciss driver needed for the dm/multipath tools.

	A file for vendor, model, rev, and unique_id is added for each logical
	drive under directory /sys/bus/pci/devices/<dev>/ccissX/cXdY.  Where X =
	the controller (or host) number and Y is the logical drive number.

	A link from /sys/bus/pci/devices/<dev>/ccissX/cXdY/block:cciss!cXdY to
	/sys/block/cciss!cXdY/device is also created.  A bus is created in
	/sys/bus/cciss.  A link is created from the pci ccissX entry to
	/sys/bus/cciss/devices/ccissX.  Please consider this for inclusion.

    This is also the functionality needed to enable hal, which should enable
    rhev install on cciss hosts.

    This resubmit of the original patch includes bus_unregister() that was
    missing.  The 1st patch is identical to the original post and the
    2nd patch adds the missing bus_unregister.

Upstream Status:

    Functionality is upstream.  Commit id:

        7fe063268e73681cdca1a6496a25f93d3332f517

Brew Build #

    1907005

Testing:

    installed rpm with patch applied to a cciss host.  Booted from cciss
    device.  Found /dev/bus/cciss directory and it was populated.

    Richard Hughes followed up by installing a hal rpm w/ cciss support
    that was able to recognize cciss devices using the following command:

        hal-find-by-capability --capability storage

    Booted/rebooted with latest patch installed.

diff --git a/Documentation/ABI/testing/sysfs-bus-pci-devices-cciss b/Documentation/ABI/testing/sysfs-bus-pci-devices-cciss
new file mode 100644
index 0000000..0a92a7c
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-pci-devices-cciss
@@ -0,0 +1,33 @@
+Where:		/sys/bus/pci/devices/<dev>/ccissX/cXdY/model
+Date:		March 2009
+Kernel Version: 2.6.30
+Contact:	iss_storagedev@hp.com
+Description:	Displays the SCSI INQUIRY page 0 model for logical drive
+		Y of controller X.
+
+Where:		/sys/bus/pci/devices/<dev>/ccissX/cXdY/rev
+Date:		March 2009
+Kernel Version: 2.6.30
+Contact:	iss_storagedev@hp.com
+Description:	Displays the SCSI INQUIRY page 0 revision for logical
+		drive Y of controller X.
+
+Where:		/sys/bus/pci/devices/<dev>/ccissX/cXdY/unique_id
+Date:		March 2009
+Kernel Version: 2.6.30
+Contact:	iss_storagedev@hp.com
+Description:	Displays the SCSI INQUIRY page 83 serial number for logical
+		drive Y of controller X.
+
+Where:		/sys/bus/pci/devices/<dev>/ccissX/cXdY/vendor
+Date:		March 2009
+Kernel Version: 2.6.30
+Contact:	iss_storagedev@hp.com
+Description:	Displays the SCSI INQUIRY page 0 vendor for logical drive
+		Y of controller X.
+
+Where:		/sys/bus/pci/devices/<dev>/ccissX/cXdY/block:cciss!cXdY
+Date:		March 2009
+Kernel Version: 2.6.30
+Contact:	iss_storagedev@hp.com
+Description:	A symbolic link to /sys/block/cciss!cXdY
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index 82d1c3a..090fd2f 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -450,6 +450,215 @@ static void __devinit cciss_procinit(int i)
 }
 #endif				/* CONFIG_PROC_FS */
 
+#define MAX_PRODUCT_NAME_LEN 19
+
+#define to_hba(n) container_of(n, struct ctlr_info, dev)
+/*
+ * If ctlr_info.dev was not a pointer, we could use container_of()
+ * here also instead of overloading dev->driver_data.
+ */
+#define to_drv(n) ((n)->driver_data)
+
+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(dev->parent);
+	BYTE uid[16];
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+	if (h->busy_configuring)
+		ret = -EBUSY;
+	else
+		memcpy(uid, drv->uid, sizeof(uid));
+	spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+
+	if (ret)
+		return ret;
+	else
+		return snprintf(buf, 16 * 2 + 2,
+				"%02X%02X%02X%02X%02X%02X%02X%02X"
+				"%02X%02X%02X%02X%02X%02X%02X%02X\n",
+				uid[0], uid[1], uid[2], uid[3],
+				uid[4], uid[5], uid[6], uid[7],
+				uid[8], uid[9], uid[10], uid[11],
+				uid[12], uid[13], uid[14], uid[15]);
+}
+DEVICE_ATTR(unique_id, S_IRUGO, dev_show_unique_id, NULL);
+
+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(dev->parent);
+	char vendor[VENDOR_LEN + 1];
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+	if (h->busy_configuring)
+		ret = -EBUSY;
+	else
+		memcpy(vendor, drv->vendor, VENDOR_LEN + 1);
+	spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+
+	if (ret)
+		return ret;
+	else
+		return snprintf(buf, sizeof(vendor) + 1, "%s\n", drv->vendor);
+}
+DEVICE_ATTR(vendor, S_IRUGO, dev_show_vendor, NULL);
+
+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(dev->parent);
+	char model[MODEL_LEN + 1];
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+	if (h->busy_configuring)
+		ret = -EBUSY;
+	else
+		memcpy(model, drv->model, MODEL_LEN + 1);
+	spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+
+	if (ret)
+		return ret;
+	else
+		return snprintf(buf, sizeof(model) + 1, "%s\n", drv->model);
+}
+DEVICE_ATTR(model, S_IRUGO, dev_show_model, NULL);
+
+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(dev->parent);
+	char rev[REV_LEN + 1];
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+	if (h->busy_configuring)
+		ret = -EBUSY;
+	else
+		memcpy(rev, drv->rev, REV_LEN + 1);
+	spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+
+	if (ret)
+		return ret;
+	else
+		return snprintf(buf, sizeof(rev) + 1, "%s\n", drv->rev);
+}
+DEVICE_ATTR(rev, S_IRUGO, dev_show_rev, NULL);
+
+struct device_attribute *cciss_dev_attrs[] = {
+	&dev_attr_unique_id,
+	&dev_attr_vendor,
+	&dev_attr_model,
+	&dev_attr_rev,
+	NULL,
+};
+
+static struct bus_type cciss_bus_type = {
+	.name		= "cciss",
+};
+
+
+/*
+ * Initialize sysfs entry for each controller.  This sets up and registers
+ * the 'cciss#' directory for each individual controller under
+ * /sys/bus/pci/devices/<dev>/.
+ */
+static int cciss_create_hba_sysfs_entry(struct ctlr_info *h)
+{
+	device_initialize(&h->dev);
+	h->dev.bus = &cciss_bus_type;
+	strncpy(h->dev.bus_id, h->devname, BUS_ID_SIZE);
+	h->dev.parent = &h->pdev->dev;
+
+	return device_add(&h->dev);
+}
+
+/*
+ * Remove sysfs entries for an hba.
+ */
+static void cciss_destroy_hba_sysfs_entry(struct ctlr_info *h)
+{
+	device_del(&h->dev);
+}
+
+/*
+ * Initialize sysfs for each logical drive.  This sets up and registers
+ * the 'c#d#' directory for each individual logical drive under
+ * /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,
+				       int drv_index)
+{
+	int err;
+	int i;
+
+	drv->dev = kzalloc(sizeof(struct device), GFP_KERNEL);
+	if (!drv->dev)
+		return -ENOMEM;
+
+	/*
+	 * Stuff backpointer to drv into dev->driver_data, so we can
+	 * get back to the device in sysfs routines.  Note that we are
+	 * overloading driver_data. If this pointer is needed in the
+	 * future for sysfs driver stuff, we need to find another
+	 * method.
+	 */
+	drv->dev->driver_data = drv;
+
+	device_initialize(drv->dev);
+	(*drv->dev).bus = &cciss_bus_type;
+	snprintf((*drv->dev).bus_id, BUS_ID_SIZE, "c%dd%d", h->ctlr, drv_index);
+	(*drv->dev).parent = &h->dev;
+
+	err = device_add(drv->dev);
+	if (err)
+		goto mem;
+
+	for (i = 0; cciss_dev_attrs[i]; i++) {
+		err = device_create_file(drv->dev, cciss_dev_attrs[i]);
+		if (err)
+			goto device;
+	}
+
+	return 0;
+
+device:
+	device_del(drv->dev);
+mem:
+	kfree(drv->dev);
+	drv->dev = NULL;
+
+	return err;
+}
+
+/*
+ * Remove sysfs entries for a logical drive.
+ */
+static void cciss_destroy_ld_sysfs_entry(drive_info_struct *drv)
+{
+	device_del(drv->dev);
+	kfree(drv->dev);
+	drv->dev = NULL;
+}
+
 /*
  * For operations that cannot sleep, a command block is allocated at init,
  * and managed by cmd_alloc() and cmd_free() using a simple bitmap to track
@@ -1370,6 +1579,45 @@ static void cciss_softirq_done(struct request *rq)
 	spin_unlock_irqrestore(&h->lock, flags);
 }
 
+/* This function gets the SCSI vendor, model, and revision of a logical drive
+ * via the inquiry page 0.  Model, vendor, and rev are set to empty strings if
+ * they cannot be read.
+ */
+static void cciss_get_device_descr(int ctlr, int logvol, int withirq,
+				   char *vendor, char *model, char *rev)
+{
+	int rc;
+	InquiryData_struct *inq_buf;
+
+	*vendor = '\0';
+	*model = '\0';
+	*rev = '\0';
+
+	inq_buf = kzalloc(sizeof(InquiryData_struct), GFP_KERNEL);
+	if (!inq_buf)
+		return;
+
+	if (withirq)
+		rc = sendcmd_withirq(CISS_INQUIRY, ctlr, inq_buf,
+				     sizeof(InquiryData_struct), 1, logvol,
+				     0, TYPE_CMD);
+	else
+		rc = sendcmd(CISS_INQUIRY, ctlr, inq_buf,
+			     sizeof(InquiryData_struct), 1, logvol, 0, NULL,
+			     TYPE_CMD);
+	if (rc == IO_OK) {
+		memcpy(vendor, &inq_buf->data_byte[8], VENDOR_LEN);
+		vendor[VENDOR_LEN] = '\0';
+		memcpy(model, &inq_buf->data_byte[16], MODEL_LEN);
+		model[MODEL_LEN] = '\0';
+		memcpy(rev, &inq_buf->data_byte[32], REV_LEN);
+		rev[REV_LEN] = '\0';
+	}
+
+	kfree(inq_buf);
+	return;
+}
+
 /* This function gets the unique id number of a logical drive via
  * inquiry page 0x83.  Unique id is 16 bytes.  If the unique
  * id cannot be had, for whatever reason, 16 bytes of 0xff
@@ -1501,6 +1749,8 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time)
 	drvinfo->block_size = block_size;
 	drvinfo->nr_blocks = total_size + 1;
 
+	cciss_get_device_descr(ctlr, drv_index, 1, drvinfo->vendor,
+				drvinfo->model, drvinfo->rev);
 	cciss_get_uid(ctlr, drv_index, 1, drvinfo->uid,
 			sizeof(drvinfo->uid));
 
@@ -1548,6 +1798,9 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time)
  	h->drv[drv_index].cylinders = drvinfo->cylinders;
  	h->drv[drv_index].raid_level = drvinfo->raid_level;
  	memcpy(h->drv[drv_index].uid, drvinfo->uid, 16);
+	memcpy(h->drv[drv_index].vendor, drvinfo->vendor, VENDOR_LEN + 1);
+	memcpy(h->drv[drv_index].model, drvinfo->model, MODEL_LEN + 1);
+	memcpy(h->drv[drv_index].rev, drvinfo->rev, REV_LEN + 1);
 
 	++h->num_luns;
 	disk = h->gendisk[drv_index];
@@ -1621,6 +1874,9 @@ 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))
+		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.
@@ -1628,6 +1884,11 @@ static int cciss_add_gendisk(ctlr_info_t *h, __u32 lunid, int controller_node)
 	h->drv[drv_index].busy_configuring = 0;
 	wmb();
 	return drv_index;
+
+err_free_disk:
+	put_disk(h->gendisk[drv_index]);
+	h->gendisk[drv_index] = NULL;
+	return -1;
 }
 
 /* This is for the special case of a controller which
@@ -1753,6 +2014,7 @@ 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;
 		}
  	}
@@ -3769,12 +4031,15 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
 	hba[i]->busy_initializing = 1;
 
 	if (cciss_pci_init(hba[i], pdev) != 0)
-		goto clean1;
+		goto clean0;
 
 	sprintf(hba[i]->devname, "cciss%d", i);
 	hba[i]->ctlr = i;
 	hba[i]->pdev = pdev;
 
+	if (cciss_create_hba_sysfs_entry(hba[i]))
+		goto clean0;
+
 	/* configure PCI DMA stuff */
 	if (!pci_set_dma_mask(pdev, DMA_64BIT_MASK))
 		dac = 1;
@@ -3914,6 +4179,8 @@ clean4:
       clean2:
 	unregister_blkdev(hba[i]->major, hba[i]->devname);
       clean1:
+	cciss_destroy_hba_sysfs_entry(hba[i]);
+      clean0:
 	hba[i]->busy_initializing = 0;
 	/* cleanup any queues that may have been initialized */
 	for (j=0; j <= hba[i]->highest_lun; j++){
@@ -4023,6 +4290,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]);
 	free_hba(i);
 }
 
@@ -4040,10 +4308,24 @@ static struct pci_driver cciss_pci_driver = {
  */
 static int __init cciss_init(void)
 {
+	int err;
+
 	printk(KERN_INFO DRIVER_NAME "\n");
 
+	err = bus_register(&cciss_bus_type);
+	if (err)
+		return err;
+
 	/* Register for our PCI devices */
-	return pci_register_driver(&cciss_pci_driver);
+	err = pci_register_driver(&cciss_pci_driver);
+	if (err)
+		goto err_bus_register;
+
+	return 0;
+
+err_bus_register:
+	bus_unregister(&cciss_bus_type);
+	return err;
 }
 
 static void __exit cciss_cleanup(void)
diff --git a/drivers/block/cciss.h b/drivers/block/cciss.h
index d0cc02e..0ac4f1f 100644
--- a/drivers/block/cciss.h
+++ b/drivers/block/cciss.h
@@ -12,6 +12,10 @@
 #define IO_OK		0
 #define IO_ERROR	1
 
+#define VENDOR_LEN	8
+#define MODEL_LEN	16
+#define REV_LEN		4
+
 struct ctlr_info;
 typedef struct ctlr_info ctlr_info_t;
 
@@ -35,18 +39,29 @@ typedef struct _drive_info_struct
 	int	raid_level;	/* set to -1 to indicate that
 			 	 * the drive is not in use/configured
 				 */
+	/*
+	 * Warning: We should be using struct device dev here instead
+	 * of a pointer.  This struct is 616 bytes on an x86 system
+	 * and ctrl_info.dev uses CISS_MAX_LUN (512) drive_info_struct
+	 * elements or 308K each.  Kmalloc cannot allocate that much
+	 * memory so we use a pointer here instead (we really should
+	 * be using pointers to drive_info_structs in
+	 * ctrl_info.dev). Using a pointer here means that we cannot
+	 * use the container_of macro which creates problems with
+	 * sysfs (Noted later in the code).
+	 */
+	struct device *dev;
 	int	busy_configuring;	/* This is set when the drive is being
 				    	 * removed to prevent it from being
 					 * opened or it's queue from being
 					 * started.
 					 */
-	char	vendor[9];
-	char	model[17];
-	char	rev[5];
+	char	vendor[VENDOR_LEN + 1];
+	char	model[MODEL_LEN + 1];
+	char	rev[REV_LEN + 1];
 	BYTE	uid[16];	/* from inquiry page 0x83
 				 * not neccesarily NULL terminated
 				 */
-	struct device *dev_info;
 } drive_info_struct;
 
 #ifdef CONFIG_CISS_SCSI_TAPE
@@ -128,6 +143,7 @@ struct ctlr_info
 	unsigned char alive;
 	struct completion *rescan_wait;
 	struct task_struct *cciss_scan_thread;
+	struct device dev;
 };
 
 /*  Defining the diffent access_menthods */