From 4d761609471f7e543c880dd47ef5e1669076081b Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 18 Sep 2009 12:58:48 -0700 Subject: cciss: fix schedule_timeout() parameters Change schedule_timeout() parameter to not be specific to HZ=1000. Signed-off-by: Randy Dunlap Acked-by: Mike Miller Cc: Marcin Slusarz Cc: "Cameron, Steve" Signed-off-by: Andrew Morton Signed-off-by: Jens Axboe --- drivers/block/cciss.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/block/cciss.c') diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 24c3e21ab26..b890f8b3c09 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -3489,7 +3490,7 @@ static int __devinit cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev) if (scratchpad == CCISS_FIRMWARE_READY) break; set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ / 10); /* wait 100ms */ + schedule_timeout(msecs_to_jiffies(100)); /* wait 100ms */ } if (scratchpad != CCISS_FIRMWARE_READY) { printk(KERN_WARNING "cciss: Board not ready. Timed out.\n"); @@ -3615,7 +3616,7 @@ static int __devinit cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev) break; /* delay and try again */ set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(10); + schedule_timeout(msecs_to_jiffies(1)); } #ifdef CCISS_DEBUG -- cgit From c64bebcd7f33a6260b6d4c9999f797a633a3fa1c Mon Sep 17 00:00:00 2001 From: Andrew Patterson Date: Thu, 17 Sep 2009 13:46:53 -0500 Subject: cciss: Remove sysfs entries for logical drives on driver cleanup. Sysfs entries for logical drives need to be removed when a drive is deleted during driver cleanup. Signed-off-by: Andrew Patterson Signed-off-by: Stephen M. Cameron Acked-by: Mike Miller Signed-off-by: Jens Axboe --- drivers/block/cciss.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/block/cciss.c') diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index b890f8b3c09..f162f96c36e 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -1978,7 +1978,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; } } @@ -2119,6 +2118,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 */ @@ -4142,6 +4142,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 -- cgit From b368c9dd65984d1860b97bff77644c0e3e46df96 Mon Sep 17 00:00:00 2001 From: Andrew Patterson Date: Thu, 17 Sep 2009 13:46:58 -0500 Subject: cciss: Use one scan thread per controller and fix hang during rmmod 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. Signed-off-by: Andrew Patterson Signed-off-by: Stephen M. Cameron Acked-by: Mike Miller Signed-off-by: Jens Axboe --- drivers/block/cciss.c | 156 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 136 insertions(+), 20 deletions(-) (limited to 'drivers/block/cciss.c') diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index f162f96c36e..4fb63b89879 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -156,6 +157,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); @@ -3233,20 +3238,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; } @@ -3269,8 +3375,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: @@ -3919,6 +4025,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; @@ -3927,6 +4034,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; @@ -4036,14 +4145,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: @@ -4126,8 +4229,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); @@ -4174,6 +4278,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); } @@ -4206,15 +4311,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; } @@ -4231,6 +4346,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); } -- cgit From d6f4965d7d2e718eb9b223cb06db5f6a53b73507 Mon Sep 17 00:00:00 2001 From: Andrew Patterson Date: Thu, 17 Sep 2009 13:47:03 -0500 Subject: cciss: Allow triggering of rescan of logical drive topology via sysfs entry Added /sys/bus/pci/devices//ccissX/rescan sysfs entry used to kick off a rescan that discovers logical drive topology changes. Signed-off-by: Andrew Patterson Signed-off-by: Stephen M. Cameron Acked-by: Mike Miller Signed-off-by: Jens Axboe --- drivers/block/cciss.c | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) (limited to 'drivers/block/cciss.c') diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 4fb63b89879..a45268554e0 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -195,6 +195,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); @@ -460,9 +461,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, @@ -566,6 +577,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, -- cgit From 21d9db0b6231ef908fcdbfacefa392352776857f Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Thu, 17 Sep 2009 13:47:08 -0500 Subject: cciss: Remove some unused code in rebuild_lun_table() Remove some unused code in rebuild_lun_table() Signed-off-by: Stephen M. Cameron Signed-off-by: Jens Axboe --- drivers/block/cciss.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'drivers/block/cciss.c') diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index a45268554e0..e15f4acf08a 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -1708,7 +1708,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); @@ -1716,13 +1715,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, -- cgit From 617e1344229d22ea9ecb6538e50808541618ed2b Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Thu, 17 Sep 2009 13:47:14 -0500 Subject: cciss: Dynamically allocate struct device for each logical drive as needed. Dynamically allocate struct device for each logical drive as needed instead of allocating the maximum we would ever need at driver init time. Signed-off-by: Stephen M. Cameron Signed-off-by: Jens Axboe --- drivers/block/cciss.c | 98 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 72 insertions(+), 26 deletions(-) (limited to 'drivers/block/cciss.c') diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index e15f4acf08a..30b328aefe7 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -198,6 +198,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); @@ -459,7 +461,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, @@ -479,8 +480,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; @@ -509,8 +510,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; @@ -533,8 +534,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; @@ -557,8 +558,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; @@ -594,6 +595,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[] = { @@ -616,12 +618,24 @@ static const 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 @@ -645,6 +659,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); } /* @@ -653,24 +676,33 @@ static void cciss_destroy_hba_sysfs_entry(struct ctlr_info *h) * /sys/bus/pci/devices/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; } /* @@ -1651,7 +1683,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); @@ -1659,8 +1694,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); @@ -1686,6 +1725,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. @@ -1871,7 +1916,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 */ @@ -2145,7 +2190,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 */ @@ -4268,8 +4313,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); } @@ -4345,7 +4391,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); -- cgit From 097d026453e7051a544722f4e05240085916499d Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Thu, 17 Sep 2009 13:47:19 -0500 Subject: cciss: Rearrange logical drive sysfs code to make the "changing a disk" path work. Rearrange logical drive sysfs code to make the "changing a disk" path work. Signed-off-by: Stephen M. Cameron Signed-off-by: Jens Axboe --- drivers/block/cciss.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers/block/cciss.c') diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 30b328aefe7..2810dd9805a 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -1916,9 +1916,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. */ @@ -2145,8 +2146,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 @@ -2190,7 +2193,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 */ @@ -4308,15 +4310,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 -- cgit From e8074f79770953be26b64539803d06a46d1a6e58 Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Thu, 17 Sep 2009 13:47:24 -0500 Subject: cciss: Handle failure of blk_init_queue gracefully in cciss_add_disk. Handle failure of blk_init_queue gracefully in cciss_add_disk. Signed-off-by: Stephen M. Cameron Signed-off-by: Jens Axboe --- drivers/block/cciss.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/block/cciss.c') diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 2810dd9805a..b1211d530da 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -1690,6 +1690,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; @@ -1730,6 +1732,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; } -- cgit From 361e9b07d11cfc8b77921a0e96910019402efe79 Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Thu, 17 Sep 2009 13:47:29 -0500 Subject: cciss: Handle cases when cciss_add_disk fails. Handle cases when cciss_add_disk fails. Signed-off-by: Stephen M. Cameron Signed-off-by: Jens Axboe --- drivers/block/cciss.c | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) (limited to 'drivers/block/cciss.c') diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index b1211d530da..ced71b006cd 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -200,6 +200,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); @@ -1856,8 +1857,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); @@ -1891,6 +1898,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 @@ -1931,8 +1944,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; } @@ -1950,11 +1962,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; @@ -1963,7 +1972,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 -- cgit From 8ce51966d3b809d6c1ae4f3902058558589480b8 Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Thu, 17 Sep 2009 13:47:34 -0500 Subject: cciss: Handle special case for sysfs attributes of the first logical drive. 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. Signed-off-by: Stephen M. Cameron Signed-off-by: Jens Axboe --- drivers/block/cciss.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'drivers/block/cciss.c') diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index ced71b006cd..aa95eeb3020 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -682,6 +682,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; @@ -698,9 +702,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; @@ -1920,6 +1930,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] = @@ -2165,7 +2176,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) { @@ -2211,7 +2222,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) { @@ -4329,7 +4339,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) -- cgit From 9ddb27b44ffeb3080b71cc493b2edff2224d9356 Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Thu, 17 Sep 2009 13:47:39 -0500 Subject: cciss: Clear all sysfs-exposed data for deleted logical drives. When removing a logical drive, clear all the information that is now exposed by sysfs (e.g. vendor, model, serial number.) Signed-off-by: Stephen M. Cameron Signed-off-by: Jens Axboe --- drivers/block/cciss.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) (limited to 'drivers/block/cciss.c') diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index aa95eeb3020..09a0f7bb433 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -2134,6 +2134,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: @@ -2212,16 +2231,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) { -- cgit From 2d11d9931f5968bddac50d9d224c4812d4be869a Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Thu, 17 Sep 2009 13:47:44 -0500 Subject: cciss: Fix usage_count check in rebuild_lun_table when triggered via sysfs. 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. Signed-off-by: Stephen M. Cameron Signed-off-by: Jens Axboe --- drivers/block/cciss.c | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) (limited to 'drivers/block/cciss.c') diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 09a0f7bb433..0a3c057c778 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -170,9 +170,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); @@ -1211,7 +1211,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; @@ -1757,7 +1757,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; @@ -1835,7 +1836,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; } @@ -2000,7 +2001,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; @@ -2079,7 +2081,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; } } @@ -2117,7 +2119,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: @@ -2167,9 +2170,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; @@ -2183,7 +2192,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; @@ -3452,7 +3461,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; @@ -4253,7 +4262,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; -- cgit From 2c935593ac1871211b43a54f023dc3bc605ad346 Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Thu, 17 Sep 2009 13:47:50 -0500 Subject: cciss: Fix excessive gendisk freeing bug on driver unload. 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. Signed-off-by: Stephen M. Cameron Signed-off-by: Jens Axboe --- drivers/block/cciss.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'drivers/block/cciss.c') diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 0a3c057c778..3a6ca7de4c9 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -3893,15 +3893,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. */ -- cgit From 983333cb0c445c56808502461bbb34876c63eb2b Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Thu, 17 Sep 2009 13:47:55 -0500 Subject: cciss: Silence noisy per-disk messages output by cciss_read_capacity Silence noisy per-disk messages output by cciss_read_capacity Signed-off-by: Stephen M. Cameron Signed-off-by: Jens Axboe --- drivers/block/cciss.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/block/cciss.c') diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 3a6ca7de4c9..67c4899ce9e 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -2602,8 +2602,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 @@ -2637,9 +2635,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); } -- cgit From 39ccf9a645dbca7f9866317380912327570787c0 Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Thu, 17 Sep 2009 13:48:00 -0500 Subject: cciss: Preserve all 8 bytes of LUN ID for logical drives. 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. Signed-off-by: Stephen M. Cameron Signed-off-by: Jens Axboe --- drivers/block/cciss.c | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) (limited to 'drivers/block/cciss.c') diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 67c4899ce9e..d6ea9376797 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -846,7 +846,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; } } @@ -1216,7 +1217,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, @@ -1611,13 +1613,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 @@ -1924,7 +1924,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; @@ -1943,7 +1944,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; @@ -1973,7 +1975,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; @@ -2012,7 +2014,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)) @@ -2069,9 +2071,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; } @@ -2096,9 +2098,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 @@ -2106,7 +2106,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; @@ -2254,8 +2255,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; } @@ -2686,7 +2686,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; } @@ -3171,8 +3172,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; -- cgit From 2e043986d584cf95656d4ee0c40fb2051e8a8460 Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Thu, 17 Sep 2009 13:48:05 -0500 Subject: cciss: Don't check h->busy_initializing in cciss_open(). Don't check h->busy_initializing in cciss_open(). Open won't be called before things are ready, but h->busy_initializing won't be unset until after the initial rebuild_lun_table is finished. But, to read the partitions, cciss_open will be called for each logical drive during rebuild_lun_table. If cciss_open checks h->busy_initializing, then the reading of the partition information during the initial rebuild_lun_table will fail, which is especially bad news if it happens to be your boot device. Signed-off-by: Stephen M. Cameron Signed-off-by: Jens Axboe --- drivers/block/cciss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/block/cciss.c') diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index d6ea9376797..79afca2e824 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -830,7 +830,7 @@ static int cciss_open(struct block_device *bdev, fmode_t mode) printk(KERN_DEBUG "cciss_open %s\n", bdev->bd_disk->disk_name); #endif /* CCISS_DEBUG */ - if (host->busy_initializing || drv->busy_configuring) + if (drv->busy_configuring) return -EBUSY; /* * Root is allowed to open raw volume zero even if it's not configured -- cgit From ce84a8aeac4a4a2cc421b3145dd2fb7cae860e4d Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Thu, 17 Sep 2009 13:48:10 -0500 Subject: cciss: Add lunid attribute to each logical drive in /sys Add lunid attribute to each logical drive at /sys/devices//ccissX/cXdY/lunid for controller X, logical drive Y Signed-off-by: Stephen M. Cameron Signed-off-by: Jens Axboe --- drivers/block/cciss.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'drivers/block/cciss.c') diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 79afca2e824..ae0cb1329e9 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -579,6 +579,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 @@ -604,6 +629,7 @@ static struct attribute *cciss_dev_attrs[] = { &dev_attr_model.attr, &dev_attr_vendor.attr, &dev_attr_rev.attr, + &dev_attr_lunid.attr, NULL }; -- cgit From fa52bec9df974096f9eb0e42a0b890512c0a0036 Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Thu, 17 Sep 2009 13:48:15 -0500 Subject: cciss: fix some magic numbers in the raid-level decoding cciss: fix some magic numbers in the raid-level decoding Signed-off-by: Stephen M. Cameron Signed-off-by: Jens Axboe --- drivers/block/cciss.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/block/cciss.c') diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index ae0cb1329e9..b674f93d4be 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -255,8 +255,6 @@ static inline void removeQ(CommandList_struct *c) #include "cciss_scsi.c" /* For SCSI tape support */ -#define RAID_UNKNOWN 6 - #ifdef CONFIG_PROC_FS /* @@ -268,6 +266,7 @@ static inline void removeQ(CommandList_struct *c) static const char *raid_label[] = { "0", "4", "1(1+0)", "5", "5+1", "ADG", "UNKNOWN" }; +#define RAID_UNKNOWN (sizeof(raid_label) / sizeof(raid_label[0])-1) static struct proc_dir_entry *proc_cciss; @@ -341,7 +340,7 @@ static int cciss_seq_show(struct seq_file *seq, void *v) vol_sz_frac *= 100; sector_div(vol_sz_frac, ENG_GIG_FACTOR); - if (drv->raid_level > 5) + if (drv->raid_level < 0 || drv->raid_level > RAID_UNKNOWN) drv->raid_level = RAID_UNKNOWN; seq_printf(seq, "cciss/c%dd%d:" "\t%4u.%02uGB\tRAID %s\n", -- cgit From 3ff1111dc6e27524eeef267ab0ca9b5690594748 Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Thu, 17 Sep 2009 13:48:21 -0500 Subject: cciss: Add a "raid_level" attribute to each logical drive in /sys and change get rid of some magic numbers in raid lavel decoding. Add raid_level attribute to each logical drive at /sys/devices//ccissX/cXdY/raid_level for controller X, logical drive Y Signed-off-by: Stephen M. Cameron Signed-off-by: Jens Axboe --- drivers/block/cciss.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'drivers/block/cciss.c') diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index b674f93d4be..063e8b0834d 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -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 > RAID_UNKNOWN) + 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 }; -- cgit From e272afecaf18912e971374df4605496975942e5c Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Thu, 17 Sep 2009 13:48:26 -0500 Subject: cciss: Add usage_count attribute to each logical drive in /sys Add usage_count attribute to each logical drive at /sys/devices//ccissX/cXdY/usage_count for controller X, logical drive Y. The usage count is the number of times the device has currently been opened. Signed-off-by: Stephen M. Cameron Signed-off-by: Jens Axboe --- drivers/block/cciss.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'drivers/block/cciss.c') diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 063e8b0834d..b808e9287b7 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -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 }; -- cgit From 9cef0d2f4f68a5a2c6ea0495f958a074d21fbd07 Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Thu, 17 Sep 2009 13:48:31 -0500 Subject: cciss: Dynamically allocate the drive_info_struct for each logical drive. cciss: Dynamically allocate the drive_info_struct for each logical drive. This reduces the size of the per-hba ctlr_info structure from 106936 bytes to 8132 bytes. That's on 32-bit systems. On 64-bit systems, the improvement is even bigger. Without this, the ctlr_info struct is so big that the driver won't even load on a 64 bit system if CISS_MAX_LUN was at it's current setting of 1024 logical drives. Signed-off-by: Stephen M. Cameron Signed-off-by: Jens Axboe --- drivers/block/cciss.c | 293 ++++++++++++++++++++++++++++---------------------- 1 file changed, 165 insertions(+), 128 deletions(-) (limited to 'drivers/block/cciss.c') diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index b808e9287b7..04036ef8ea5 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -201,6 +201,7 @@ 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); +static void cciss_free_drive_info(ctlr_info_t *h, int drv_index); #ifdef CONFIG_PROC_FS static void cciss_procinit(int i); @@ -327,7 +328,7 @@ static int cciss_seq_show(struct seq_file *seq, void *v) ctlr_info_t *h = seq->private; unsigned ctlr = h->ctlr; loff_t *pos = v; - drive_info_struct *drv = &h->drv[*pos]; + drive_info_struct *drv = h->drv[*pos]; if (*pos > h->highest_lun) return 0; @@ -461,6 +462,7 @@ 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, @@ -480,8 +482,8 @@ static ssize_t dev_show_unique_id(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); + drive_info_struct *drv = to_drv(dev); + struct ctlr_info *h = to_hba(drv->dev.parent); __u8 sn[16]; unsigned long flags; int ret = 0; @@ -510,8 +512,8 @@ static ssize_t dev_show_vendor(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); + drive_info_struct *drv = to_drv(dev); + struct ctlr_info *h = to_hba(drv->dev.parent); char vendor[VENDOR_LEN + 1]; unsigned long flags; int ret = 0; @@ -534,8 +536,8 @@ static ssize_t dev_show_model(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); + drive_info_struct *drv = to_drv(dev); + struct ctlr_info *h = to_hba(drv->dev.parent); char model[MODEL_LEN + 1]; unsigned long flags; int ret = 0; @@ -558,8 +560,8 @@ static ssize_t dev_show_rev(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); + drive_info_struct *drv = to_drv(dev); + struct ctlr_info *h = to_hba(drv->dev.parent); char rev[REV_LEN + 1]; unsigned long flags; int ret = 0; @@ -581,8 +583,8 @@ 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); + drive_info_struct *drv = to_drv(dev); + struct ctlr_info *h = to_hba(drv->dev.parent); unsigned long flags; unsigned char lunid[8]; @@ -606,8 +608,8 @@ 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); + drive_info_struct *drv = to_drv(dev); + struct ctlr_info *h = to_hba(drv->dev.parent); int raid; unsigned long flags; @@ -629,8 +631,8 @@ 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); + drive_info_struct *drv = to_drv(dev); + struct ctlr_info *h = to_hba(drv->dev.parent); unsigned long flags; int count; @@ -733,11 +735,12 @@ static void cciss_destroy_hba_sysfs_entry(struct ctlr_info *h) } /* cciss_device_release is called when the reference count - * of h->drv[x].dev goes to zero. + * of h->drv[x]dev goes to zero. */ static void cciss_device_release(struct device *dev) { - kfree(dev); + drive_info_struct *drv = to_drv(dev); + kfree(drv); } /* @@ -751,20 +754,16 @@ 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) + if (h->drv[drv_index]->device_initialized) return 0; - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) - return -ENOMEM; + dev = &h->drv[drv_index]->dev; 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]); + h->drv[drv_index]->device_initialized = 1; return device_add(dev); } @@ -774,7 +773,7 @@ static long cciss_create_ld_sysfs_entry(struct ctlr_info *h, 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; + 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) @@ -782,7 +781,7 @@ static void cciss_destroy_ld_sysfs_entry(struct ctlr_info *h, int drv_index, device_del(dev); put_device(dev); /* the "final" put. */ - h->drv[drv_index].dev = NULL; + h->drv[drv_index] = NULL; } /* @@ -1625,7 +1624,10 @@ static void cciss_check_queues(ctlr_info_t *h) /* make sure the disk has been added and the drive is real * because this can be called from the middle of init_one. */ - if (!(h->drv[curr_queue].queue) || !(h->drv[curr_queue].heads)) + if (!h->drv[curr_queue]) + continue; + if (!(h->drv[curr_queue]->queue) || + !(h->drv[curr_queue]->heads)) continue; blk_start_queue(h->gendisk[curr_queue]->queue); @@ -1685,8 +1687,8 @@ static void cciss_softirq_done(struct request *rq) static inline void log_unit_to_scsi3addr(ctlr_info_t *h, unsigned char scsi3addr[], uint32_t log_unit) { - memcpy(scsi3addr, h->drv[log_unit].LunID, - sizeof(h->drv[log_unit].LunID)); + 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 @@ -1776,12 +1778,10 @@ static int 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; + 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; /* Set up queue information */ blk_queue_bounce_limit(disk->queue, h->pdev->dma_mask); @@ -1799,13 +1799,13 @@ static int cciss_add_disk(ctlr_info_t *h, struct gendisk *disk, disk->queue->queuedata = h; blk_queue_logical_block_size(disk->queue, - h->drv[drv_index].block_size); + h->drv[drv_index]->block_size); /* Make sure all queue data is written out before */ - /* setting h->drv[drv_index].queue, as setting this */ + /* setting h->drv[drv_index]->queue, as setting this */ /* allows the interrupt handler to start the queue */ wmb(); - h->drv[drv_index].queue = disk->queue; + h->drv[drv_index]->queue = disk->queue; add_disk(disk); return 0; @@ -1840,7 +1840,7 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time, /* Get information about the disk and modify the driver structure */ inq_buff = kmalloc(sizeof(InquiryData_struct), GFP_KERNEL); - drvinfo = kmalloc(sizeof(*drvinfo), GFP_KERNEL); + drvinfo = kzalloc(sizeof(*drvinfo), GFP_KERNEL); if (inq_buff == NULL || drvinfo == NULL) goto mem_msg; @@ -1876,16 +1876,19 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time, drvinfo->model, drvinfo->rev); cciss_get_serial_no(ctlr, drv_index, 1, drvinfo->serial_no, sizeof(drvinfo->serial_no)); + /* Save the lunid in case we deregister the disk, below. */ + memcpy(drvinfo->LunID, h->drv[drv_index]->LunID, + sizeof(drvinfo->LunID)); /* Is it the same disk we already know, and nothing's changed? */ - if (h->drv[drv_index].raid_level != -1 && + if (h->drv[drv_index]->raid_level != -1 && ((memcmp(drvinfo->serial_no, - h->drv[drv_index].serial_no, 16) == 0) && - drvinfo->block_size == h->drv[drv_index].block_size && - drvinfo->nr_blocks == h->drv[drv_index].nr_blocks && - drvinfo->heads == h->drv[drv_index].heads && - drvinfo->sectors == h->drv[drv_index].sectors && - drvinfo->cylinders == h->drv[drv_index].cylinders)) + h->drv[drv_index]->serial_no, 16) == 0) && + drvinfo->block_size == h->drv[drv_index]->block_size && + drvinfo->nr_blocks == h->drv[drv_index]->nr_blocks && + drvinfo->heads == h->drv[drv_index]->heads && + drvinfo->sectors == h->drv[drv_index]->sectors && + drvinfo->cylinders == h->drv[drv_index]->cylinders)) /* The disk is unchanged, nothing to update */ goto freeret; @@ -1895,18 +1898,17 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time, * If the disk already exists then deregister it before proceeding * (unless it's the first disk (for the controller node). */ - if (h->drv[drv_index].raid_level != -1 && drv_index != 0) { + if (h->drv[drv_index]->raid_level != -1 && drv_index != 0) { printk(KERN_WARNING "disk %d has changed.\n", drv_index); spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags); - h->drv[drv_index].busy_configuring = 1; + h->drv[drv_index]->busy_configuring = 1; spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags); - /* deregister_disk sets h->drv[drv_index].queue = NULL + /* deregister_disk sets h->drv[drv_index]->queue = NULL * which keeps the interrupt handler from starting * the queue. */ ret = deregister_disk(h, drv_index, 0, via_ioctl); - h->drv[drv_index].busy_configuring = 0; } /* If the disk is in use return */ @@ -1914,22 +1916,31 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time, goto freeret; /* Save the new information from cciss_geometry_inquiry - * and serial number inquiry. + * and serial number inquiry. If the disk was deregistered + * above, then h->drv[drv_index] will be NULL. */ - h->drv[drv_index].block_size = drvinfo->block_size; - h->drv[drv_index].nr_blocks = drvinfo->nr_blocks; - h->drv[drv_index].heads = drvinfo->heads; - h->drv[drv_index].sectors = drvinfo->sectors; - h->drv[drv_index].cylinders = drvinfo->cylinders; - h->drv[drv_index].raid_level = drvinfo->raid_level; - memcpy(h->drv[drv_index].serial_no, drvinfo->serial_no, 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); + if (h->drv[drv_index] == NULL) { + drvinfo->device_initialized = 0; + h->drv[drv_index] = drvinfo; + drvinfo = NULL; /* so it won't be freed below. */ + } else { + /* special case for cxd0 */ + h->drv[drv_index]->block_size = drvinfo->block_size; + h->drv[drv_index]->nr_blocks = drvinfo->nr_blocks; + h->drv[drv_index]->heads = drvinfo->heads; + h->drv[drv_index]->sectors = drvinfo->sectors; + h->drv[drv_index]->cylinders = drvinfo->cylinders; + h->drv[drv_index]->raid_level = drvinfo->raid_level; + memcpy(h->drv[drv_index]->serial_no, drvinfo->serial_no, 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]; - set_capacity(disk, h->drv[drv_index].nr_blocks); + set_capacity(disk, h->drv[drv_index]->nr_blocks); /* If it's not disk 0 (drv_index != 0) * or if it was disk 0, but there was previously @@ -1940,6 +1951,7 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time, if (drv_index || first_time) { if (cciss_add_disk(h, disk, drv_index) != 0) { cciss_free_gendisk(h, drv_index); + cciss_free_drive_info(h, drv_index); printk(KERN_WARNING "cciss:%d could not update " "disk %d\n", h->ctlr, drv_index); --h->num_luns; @@ -1956,28 +1968,64 @@ mem_msg: } /* This function will find the first index of the controllers drive array - * that has a -1 for the raid_level and will return that index. This is - * where new drives will be added. If the index to be returned is greater - * than the highest_lun index for the controller then highest_lun is set - * to this new index. If there are no available indexes then -1 is returned. - * "controller_node" is used to know if this is a real logical drive, or just - * the controller node, which determines if this counts towards highest_lun. + * that has a null drv pointer and allocate the drive info struct and + * will return that index This is where new drives will be added. + * If the index to be returned is greater than the highest_lun index for + * the controller then highest_lun is set * to this new index. + * If there are no available indexes or if tha allocation fails, then -1 + * is returned. * "controller_node" is used to know if this is a real + * logical drive, or just the controller node, which determines if this + * counts towards highest_lun. */ -static int cciss_find_free_drive_index(int ctlr, int controller_node) +static int cciss_alloc_drive_info(ctlr_info_t *h, int controller_node) { int i; + drive_info_struct *drv; + /* Search for an empty slot for our drive info */ for (i = 0; i < CISS_MAX_LUN; i++) { - if (hba[ctlr]->drv[i].raid_level == -1) { - if (i > hba[ctlr]->highest_lun) - if (!controller_node) - hba[ctlr]->highest_lun = i; + + /* if not cxd0 case, and it's occupied, skip it. */ + if (h->drv[i] && i != 0) + continue; + /* + * If it's cxd0 case, and drv is alloc'ed already, and a + * disk is configured there, skip it. + */ + if (i == 0 && h->drv[i] && h->drv[i]->raid_level != -1) + continue; + + /* + * We've found an empty slot. Update highest_lun + * provided this isn't just the fake cxd0 controller node. + */ + if (i > h->highest_lun && !controller_node) + h->highest_lun = i; + + /* If adding a real disk at cxd0, and it's already alloc'ed */ + if (i == 0 && h->drv[i] != NULL) return i; - } + + /* + * Found an empty slot, not already alloc'ed. Allocate it. + * Mark it with raid_level == -1, so we know it's new later on. + */ + drv = kzalloc(sizeof(*drv), GFP_KERNEL); + if (!drv) + return -1; + drv->raid_level = -1; /* so we know it's new */ + h->drv[i] = drv; + return i; } return -1; } +static void cciss_free_drive_info(ctlr_info_t *h, int drv_index) +{ + kfree(h->drv[drv_index]); + h->drv[drv_index] = NULL; +} + static void cciss_free_gendisk(ctlr_info_t *h, int drv_index) { put_disk(h->gendisk[drv_index]); @@ -1998,7 +2046,7 @@ static int cciss_add_gendisk(ctlr_info_t *h, unsigned char lunid[], { int drv_index; - drv_index = cciss_find_free_drive_index(h->ctlr, controller_node); + drv_index = cciss_alloc_drive_info(h, controller_node); if (drv_index == -1) return -1; @@ -2010,24 +2058,24 @@ static int cciss_add_gendisk(ctlr_info_t *h, unsigned char lunid[], printk(KERN_ERR "cciss%d: could not " "allocate a new disk %d\n", h->ctlr, drv_index); - return -1; + goto err_free_drive_info; } } - 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; - } + memcpy(h->drv[drv_index]->LunID, lunid, + sizeof(h->drv[drv_index]->LunID)); + 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. */ - h->drv[drv_index].busy_configuring = 0; + h->drv[drv_index]->busy_configuring = 0; wmb(); return drv_index; err_free_disk: cciss_free_gendisk(h, drv_index); +err_free_drive_info: + cciss_free_drive_info(h, drv_index); return -1; } @@ -2047,17 +2095,18 @@ static void cciss_add_controller_node(ctlr_info_t *h) drv_index = cciss_add_gendisk(h, CTLR_LUNID, 1); 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; - h->drv[drv_index].sectors = 0; - h->drv[drv_index].cylinders = 0; - h->drv[drv_index].raid_level = -1; - memset(h->drv[drv_index].serial_no, 0, 16); + h->drv[drv_index]->block_size = 512; + h->drv[drv_index]->nr_blocks = 0; + h->drv[drv_index]->heads = 0; + h->drv[drv_index]->sectors = 0; + h->drv[drv_index]->cylinders = 0; + h->drv[drv_index]->raid_level = -1; + memset(h->drv[drv_index]->serial_no, 0, 16); disk = h->gendisk[drv_index]; if (cciss_add_disk(h, disk, drv_index) == 0) return; cciss_free_gendisk(h, drv_index); + cciss_free_drive_info(h, drv_index); error: printk(KERN_WARNING "cciss%d: could not " "add disk 0.\n", h->ctlr); @@ -2136,12 +2185,12 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time, drv_found = 0; /* skip holes in the array from already deleted drives */ - if (h->drv[i].raid_level == -1) + if (h->drv[i] == NULL) continue; for (j = 0; j < num_luns; j++) { memcpy(lunid, &ld_buff->LUN[j][0], sizeof(lunid)); - if (memcmp(h->drv[i].LunID, lunid, + if (memcmp(h->drv[i]->LunID, lunid, sizeof(lunid)) == 0) { drv_found = 1; break; @@ -2150,10 +2199,11 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time, if (!drv_found) { /* Deregister it from the OS, it's gone. */ spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags); - h->drv[i].busy_configuring = 1; + h->drv[i]->busy_configuring = 1; spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags); return_code = deregister_disk(h, i, 1, via_ioctl); - h->drv[i].busy_configuring = 0; + if (h->drv[i] != NULL) + h->drv[i]->busy_configuring = 0; } } @@ -2174,9 +2224,9 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time, * the first free index and add it. */ for (j = 0; j <= h->highest_lun; j++) { - if (h->drv[j].raid_level != -1 && - memcmp(h->drv[j].LunID, lunid, - sizeof(h->drv[j].LunID)) == 0) { + if (h->drv[j] != NULL && + memcmp(h->drv[j]->LunID, lunid, + sizeof(h->drv[j]->LunID)) == 0) { drv_index = j; drv_found = 1; break; @@ -2253,11 +2303,12 @@ static int deregister_disk(ctlr_info_t *h, int drv_index, int i; struct gendisk *disk; drive_info_struct *drv; + int recalculate_highest_lun; if (!capable(CAP_SYS_RAWIO)) return -EPERM; - drv = &h->drv[drv_index]; + drv = h->drv[drv_index]; disk = h->gendisk[drv_index]; /* make sure logical volume is NOT is use */ @@ -2267,6 +2318,8 @@ static int deregister_disk(ctlr_info_t *h, int drv_index, } else if (drv->usage_count > 0) return -EBUSY; + recalculate_highest_lun = (drv == h->drv[h->highest_lun]); + /* invalidate the devices and deregister the disk. If it is disk * zero do not deregister it but just zero out it's values. This * allows us to delete disk zero but keep the controller registered. @@ -2277,14 +2330,8 @@ static int deregister_disk(ctlr_info_t *h, int drv_index, cciss_destroy_ld_sysfs_entry(h, drv_index, 0); del_gendisk(disk); } - if (q) { + if (q) blk_cleanup_queue(q); - /* Set drv->queue to NULL so that we do not try - * to call blk_start_queue on this queue in the - * interrupt handler - */ - drv->queue = NULL; - } /* If clear_all is set then we are deleting the logical * drive, not just refreshing its info. For drives * other than disk 0 we will call put_disk. We do not @@ -2307,24 +2354,20 @@ static int deregister_disk(ctlr_info_t *h, int drv_index, } } else { set_capacity(disk, 0); + cciss_clear_drive_info(drv); } --h->num_luns; - cciss_clear_drive_info(drv); - - if (clear_all) { - /* check to see if it was the last disk */ - if (drv == h->drv + h->highest_lun) { - /* if so, find the new hightest lun */ - int i, newhighest = -1; - for (i = 0; i <= h->highest_lun; i++) { - /* if the disk has size > 0, it is available */ - if (h->drv[i].heads) - newhighest = i; - } - h->highest_lun = newhighest; + + /* if it was the last disk, find the new hightest lun */ + if (clear_all && recalculate_highest_lun) { + int i, newhighest = -1; + for (i = 0; i <= h->highest_lun; i++) { + /* if the disk has size > 0, it is available */ + if (h->drv[i] && h->drv[i]->heads) + newhighest = i; } - memset(drv->LunID, 0, sizeof(drv->LunID)); + h->highest_lun = newhighest; } return 0; } @@ -2755,7 +2798,7 @@ static int cciss_revalidate(struct gendisk *disk) InquiryData_struct *inq_buff = NULL; for (logvol = 0; logvol < CISS_MAX_LUN; logvol++) { - if (memcmp(h->drv[logvol].LunID, drv->LunID, + if (memcmp(h->drv[logvol]->LunID, drv->LunID, sizeof(drv->LunID)) == 0) { FOUND = 1; break; @@ -4293,8 +4336,7 @@ static int __devinit cciss_init_one(struct pci_dev *pdev, hba[i]->num_luns = 0; hba[i]->highest_lun = -1; for (j = 0; j < CISS_MAX_LUN; j++) { - hba[i]->drv[j].raid_level = -1; - hba[i]->drv[j].queue = NULL; + hba[i]->drv[j] = NULL; hba[i]->gendisk[j] = NULL; } @@ -4349,12 +4391,7 @@ 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++){ - drive_info_struct *drv = &(hba[i]->drv[j]); - if (drv->queue) - blk_cleanup_queue(drv->queue); - } + /* * Deliberately omit pci_disable_device(): it does something nasty to * Smart Array controllers that pci_enable_device does not undo -- cgit From 9f792d9f58496161b1b201e2ca440a6b6e116c39 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Fri, 18 Sep 2009 22:24:21 +0200 Subject: cciss: cciss_host_attr_groups should be const Signed-off-by: Jens Axboe --- drivers/block/cciss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/block/cciss.c') diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 04036ef8ea5..4d879b79225 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -656,7 +656,7 @@ static struct attribute_group cciss_host_attr_group = { .attrs = cciss_host_attrs, }; -static struct attribute_group *cciss_host_attr_groups[] = { +static const struct attribute_group *cciss_host_attr_groups[] = { &cciss_host_attr_group, NULL }; -- cgit From 1e6f2dc11984b81c6438ff6cd45cdf15a02e3dfd Mon Sep 17 00:00:00 2001 From: Alexander Beregalov Date: Thu, 24 Sep 2009 16:15:38 +0200 Subject: cciss: fix build when !PROC_FS Fix these build errors when CONFIG_PROC_FS is not set: drivers/block/cciss.c: In function 'cciss_show_raid_level': drivers/block/cciss.c:623: error: 'RAID_UNKNOWN' undeclared (first use in this function) drivers/block/cciss.c:626: error: 'raid_label' undeclared (first use in this function) drivers/block/cciss.c: In function 'cciss_geometry_inquiry': drivers/block/cciss.c:2696: error: 'RAID_UNKNOWN' undeclared (first use in this function) Signed-off-by: Alexander Beregalov Signed-off-by: Jens Axboe --- drivers/block/cciss.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/block/cciss.c') diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 4d879b79225..78852b10319 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -256,6 +256,11 @@ static inline void removeQ(CommandList_struct *c) #include "cciss_scsi.c" /* For SCSI tape support */ +static const char *raid_label[] = { "0", "4", "1(1+0)", "5", "5+1", "ADG", + "UNKNOWN" +}; +#define RAID_UNKNOWN (sizeof(raid_label) / sizeof(raid_label[0])-1) + #ifdef CONFIG_PROC_FS /* @@ -264,10 +269,6 @@ static inline void removeQ(CommandList_struct *c) #define ENG_GIG 1000000000 #define ENG_GIG_FACTOR (ENG_GIG/512) #define ENGAGE_SCSI "engage scsi" -static const char *raid_label[] = { "0", "4", "1(1+0)", "5", "5+1", "ADG", - "UNKNOWN" -}; -#define RAID_UNKNOWN (sizeof(raid_label) / sizeof(raid_label[0])-1) static struct proc_dir_entry *proc_cciss; -- cgit