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 */