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>
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index e15f4ac..30b328a 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -198,6 +198,8 @@
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 @@
#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 @@
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 @@
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 @@
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 @@
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 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 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 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 @@
* /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 @@
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 @@
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 @@
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 @@
}
}
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 @@
* 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 @@
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 @@
if (err)
goto err_thread_stop;
- return 0;
+ return err;
err_thread_stop:
kthread_stop(cciss_scan_thread);