diff options
author | Stephen M. Cameron <scameron@beardog.cce.hp.com> | 2009-09-17 13:47:14 -0500 |
---|---|---|
committer | Jens Axboe <jens.axboe@oracle.com> | 2009-10-01 21:15:41 +0200 |
commit | 617e1344229d22ea9ecb6538e50808541618ed2b (patch) | |
tree | 8e6020a20ccb2c74b1547a6563e989be0c0eb133 /drivers/block/cciss.c | |
parent | 21d9db0b6231ef908fcdbfacefa392352776857f (diff) |
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 <scameron@beardog.cce.hp.com>
Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
Diffstat (limited to 'drivers/block/cciss.c')
-rw-r--r-- | drivers/block/cciss.c | 98 |
1 files changed, 72 insertions, 26 deletions
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/ccis#/. We also create a link from * /sys/block/cciss!c#d# to this entry. */ -static int cciss_create_ld_sysfs_entry(struct ctlr_info *h, - drive_info_struct *drv, +static long cciss_create_ld_sysfs_entry(struct ctlr_info *h, int drv_index) { - device_initialize(&drv->dev); - drv->dev.type = &cciss_dev_type; - drv->dev.bus = &cciss_bus_type; - dev_set_name(&drv->dev, "c%dd%d", h->ctlr, drv_index); - drv->dev.parent = &h->dev; - return device_add(&drv->dev); + struct device *dev; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + device_initialize(dev); + dev->type = &cciss_dev_type; + dev->bus = &cciss_bus_type; + dev_set_name(dev, "c%dd%d", h->ctlr, drv_index); + dev->parent = &h->dev; + h->drv[drv_index].dev = dev; + dev_set_drvdata(dev, &h->drv[drv_index]); + return device_add(dev); } /* * Remove sysfs entries for a logical drive. */ -static void cciss_destroy_ld_sysfs_entry(drive_info_struct *drv) +static void cciss_destroy_ld_sysfs_entry(struct ctlr_info *h, int drv_index) { - device_del(&drv->dev); + struct device *dev = h->drv[drv_index].dev; + device_del(dev); + put_device(dev); /* the "final" put. */ + h->drv[drv_index].dev = NULL; } /* @@ -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); |