Commit 9cef0d2f authored by Stephen M. Cameron's avatar Stephen M. Cameron Committed by Jens Axboe

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: default avatarStephen M. Cameron <scameron@beardog.cce.hp.com>
Signed-off-by: default avatarJens Axboe <jens.axboe@oracle.com>
parent e272afec
...@@ -201,6 +201,7 @@ static int check_for_unit_attention(ctlr_info_t *h, CommandList_struct *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_hba_release(struct device *dev);
static void cciss_device_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_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 #ifdef CONFIG_PROC_FS
static void cciss_procinit(int i); static void cciss_procinit(int i);
...@@ -327,7 +328,7 @@ static int cciss_seq_show(struct seq_file *seq, void *v) ...@@ -327,7 +328,7 @@ static int cciss_seq_show(struct seq_file *seq, void *v)
ctlr_info_t *h = seq->private; ctlr_info_t *h = seq->private;
unsigned ctlr = h->ctlr; unsigned ctlr = h->ctlr;
loff_t *pos = v; loff_t *pos = v;
drive_info_struct *drv = &h->drv[*pos]; drive_info_struct *drv = h->drv[*pos];
if (*pos > h->highest_lun) if (*pos > h->highest_lun)
return 0; return 0;
...@@ -461,6 +462,7 @@ static void __devinit cciss_procinit(int i) ...@@ -461,6 +462,7 @@ static void __devinit cciss_procinit(int i)
#define MAX_PRODUCT_NAME_LEN 19 #define MAX_PRODUCT_NAME_LEN 19
#define to_hba(n) container_of(n, struct ctlr_info, dev) #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, static ssize_t host_store_rescan(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
...@@ -480,8 +482,8 @@ static ssize_t dev_show_unique_id(struct device *dev, ...@@ -480,8 +482,8 @@ static ssize_t dev_show_unique_id(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
char *buf) char *buf)
{ {
drive_info_struct *drv = dev_get_drvdata(dev); drive_info_struct *drv = to_drv(dev);
struct ctlr_info *h = to_hba(drv->dev->parent); struct ctlr_info *h = to_hba(drv->dev.parent);
__u8 sn[16]; __u8 sn[16];
unsigned long flags; unsigned long flags;
int ret = 0; int ret = 0;
...@@ -510,8 +512,8 @@ static ssize_t dev_show_vendor(struct device *dev, ...@@ -510,8 +512,8 @@ static ssize_t dev_show_vendor(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
char *buf) char *buf)
{ {
drive_info_struct *drv = dev_get_drvdata(dev); drive_info_struct *drv = to_drv(dev);
struct ctlr_info *h = to_hba(drv->dev->parent); struct ctlr_info *h = to_hba(drv->dev.parent);
char vendor[VENDOR_LEN + 1]; char vendor[VENDOR_LEN + 1];
unsigned long flags; unsigned long flags;
int ret = 0; int ret = 0;
...@@ -534,8 +536,8 @@ static ssize_t dev_show_model(struct device *dev, ...@@ -534,8 +536,8 @@ static ssize_t dev_show_model(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
char *buf) char *buf)
{ {
drive_info_struct *drv = dev_get_drvdata(dev); drive_info_struct *drv = to_drv(dev);
struct ctlr_info *h = to_hba(drv->dev->parent); struct ctlr_info *h = to_hba(drv->dev.parent);
char model[MODEL_LEN + 1]; char model[MODEL_LEN + 1];
unsigned long flags; unsigned long flags;
int ret = 0; int ret = 0;
...@@ -558,8 +560,8 @@ static ssize_t dev_show_rev(struct device *dev, ...@@ -558,8 +560,8 @@ static ssize_t dev_show_rev(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
char *buf) char *buf)
{ {
drive_info_struct *drv = dev_get_drvdata(dev); drive_info_struct *drv = to_drv(dev);
struct ctlr_info *h = to_hba(drv->dev->parent); struct ctlr_info *h = to_hba(drv->dev.parent);
char rev[REV_LEN + 1]; char rev[REV_LEN + 1];
unsigned long flags; unsigned long flags;
int ret = 0; int ret = 0;
...@@ -581,8 +583,8 @@ DEVICE_ATTR(rev, S_IRUGO, dev_show_rev, NULL); ...@@ -581,8 +583,8 @@ DEVICE_ATTR(rev, S_IRUGO, dev_show_rev, NULL);
static ssize_t cciss_show_lunid(struct device *dev, static ssize_t cciss_show_lunid(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
{ {
drive_info_struct *drv = dev_get_drvdata(dev); drive_info_struct *drv = to_drv(dev);
struct ctlr_info *h = to_hba(drv->dev->parent); struct ctlr_info *h = to_hba(drv->dev.parent);
unsigned long flags; unsigned long flags;
unsigned char lunid[8]; unsigned char lunid[8];
...@@ -606,8 +608,8 @@ DEVICE_ATTR(lunid, S_IRUGO, cciss_show_lunid, NULL); ...@@ -606,8 +608,8 @@ DEVICE_ATTR(lunid, S_IRUGO, cciss_show_lunid, NULL);
static ssize_t cciss_show_raid_level(struct device *dev, static ssize_t cciss_show_raid_level(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
{ {
drive_info_struct *drv = dev_get_drvdata(dev); drive_info_struct *drv = to_drv(dev);
struct ctlr_info *h = to_hba(drv->dev->parent); struct ctlr_info *h = to_hba(drv->dev.parent);
int raid; int raid;
unsigned long flags; unsigned long flags;
...@@ -629,8 +631,8 @@ DEVICE_ATTR(raid_level, S_IRUGO, cciss_show_raid_level, NULL); ...@@ -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, static ssize_t cciss_show_usage_count(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
{ {
drive_info_struct *drv = dev_get_drvdata(dev); drive_info_struct *drv = to_drv(dev);
struct ctlr_info *h = to_hba(drv->dev->parent); struct ctlr_info *h = to_hba(drv->dev.parent);
unsigned long flags; unsigned long flags;
int count; int count;
...@@ -733,11 +735,12 @@ static void cciss_destroy_hba_sysfs_entry(struct ctlr_info *h) ...@@ -733,11 +735,12 @@ static void cciss_destroy_hba_sysfs_entry(struct ctlr_info *h)
} }
/* cciss_device_release is called when the reference count /* 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) 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, ...@@ -751,20 +754,16 @@ static long cciss_create_ld_sysfs_entry(struct ctlr_info *h,
{ {
struct device *dev; struct device *dev;
/* Special case for c*d0, we only create it once. */ if (h->drv[drv_index]->device_initialized)
if (drv_index == 0 && h->drv[drv_index].dev != NULL)
return 0; return 0;
dev = kzalloc(sizeof(*dev), GFP_KERNEL); dev = &h->drv[drv_index]->dev;
if (!dev)
return -ENOMEM;
device_initialize(dev); device_initialize(dev);
dev->type = &cciss_dev_type; dev->type = &cciss_dev_type;
dev->bus = &cciss_bus_type; dev->bus = &cciss_bus_type;
dev_set_name(dev, "c%dd%d", h->ctlr, drv_index); dev_set_name(dev, "c%dd%d", h->ctlr, drv_index);
dev->parent = &h->dev; dev->parent = &h->dev;
h->drv[drv_index].dev = dev; h->drv[drv_index]->device_initialized = 1;
dev_set_drvdata(dev, &h->drv[drv_index]);
return device_add(dev); return device_add(dev);
} }
...@@ -774,7 +773,7 @@ static long cciss_create_ld_sysfs_entry(struct ctlr_info *h, ...@@ -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, static void cciss_destroy_ld_sysfs_entry(struct ctlr_info *h, int drv_index,
int ctlr_exiting) 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 */ /* special case for c*d0, we only destroy it on controller exit */
if (drv_index == 0 && !ctlr_exiting) if (drv_index == 0 && !ctlr_exiting)
...@@ -782,7 +781,7 @@ static void cciss_destroy_ld_sysfs_entry(struct ctlr_info *h, int drv_index, ...@@ -782,7 +781,7 @@ static void cciss_destroy_ld_sysfs_entry(struct ctlr_info *h, int drv_index,
device_del(dev); device_del(dev);
put_device(dev); /* the "final" put. */ 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) ...@@ -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 /* make sure the disk has been added and the drive is real
* because this can be called from the middle of init_one. * 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; continue;
blk_start_queue(h->gendisk[curr_queue]->queue); blk_start_queue(h->gendisk[curr_queue]->queue);
...@@ -1685,8 +1687,8 @@ static void cciss_softirq_done(struct request *rq) ...@@ -1685,8 +1687,8 @@ static void cciss_softirq_done(struct request *rq)
static inline void log_unit_to_scsi3addr(ctlr_info_t *h, static inline void log_unit_to_scsi3addr(ctlr_info_t *h,
unsigned char scsi3addr[], uint32_t log_unit) unsigned char scsi3addr[], uint32_t log_unit)
{ {
memcpy(scsi3addr, h->drv[log_unit].LunID, memcpy(scsi3addr, h->drv[log_unit]->LunID,
sizeof(h->drv[log_unit].LunID)); sizeof(h->drv[log_unit]->LunID));
} }
/* This function gets the SCSI vendor, model, and revision of a logical drive /* 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, ...@@ -1776,12 +1778,10 @@ static int cciss_add_disk(ctlr_info_t *h, struct gendisk *disk,
disk->major = h->major; disk->major = h->major;
disk->first_minor = drv_index << NWD_SHIFT; disk->first_minor = drv_index << NWD_SHIFT;
disk->fops = &cciss_fops; disk->fops = &cciss_fops;
if (h->drv[drv_index].dev == NULL) { if (cciss_create_ld_sysfs_entry(h, drv_index))
if (cciss_create_ld_sysfs_entry(h, drv_index)) goto cleanup_queue;
goto cleanup_queue; disk->private_data = h->drv[drv_index];
} disk->driverfs_dev = &h->drv[drv_index]->dev;
disk->private_data = &h->drv[drv_index];
disk->driverfs_dev = h->drv[drv_index].dev;
/* Set up queue information */ /* Set up queue information */
blk_queue_bounce_limit(disk->queue, h->pdev->dma_mask); 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, ...@@ -1799,13 +1799,13 @@ static int cciss_add_disk(ctlr_info_t *h, struct gendisk *disk,
disk->queue->queuedata = h; disk->queue->queuedata = h;
blk_queue_logical_block_size(disk->queue, 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 */ /* 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 */ /* allows the interrupt handler to start the queue */
wmb(); wmb();
h->drv[drv_index].queue = disk->queue; h->drv[drv_index]->queue = disk->queue;
add_disk(disk); add_disk(disk);
return 0; return 0;
...@@ -1840,7 +1840,7 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time, ...@@ -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 */ /* Get information about the disk and modify the driver structure */
inq_buff = kmalloc(sizeof(InquiryData_struct), GFP_KERNEL); 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) if (inq_buff == NULL || drvinfo == NULL)
goto mem_msg; goto mem_msg;
...@@ -1876,16 +1876,19 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time, ...@@ -1876,16 +1876,19 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time,
drvinfo->model, drvinfo->rev); drvinfo->model, drvinfo->rev);
cciss_get_serial_no(ctlr, drv_index, 1, drvinfo->serial_no, cciss_get_serial_no(ctlr, drv_index, 1, drvinfo->serial_no,
sizeof(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? */ /* 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, ((memcmp(drvinfo->serial_no,
h->drv[drv_index].serial_no, 16) == 0) && h->drv[drv_index]->serial_no, 16) == 0) &&
drvinfo->block_size == h->drv[drv_index].block_size && drvinfo->block_size == h->drv[drv_index]->block_size &&
drvinfo->nr_blocks == h->drv[drv_index].nr_blocks && drvinfo->nr_blocks == h->drv[drv_index]->nr_blocks &&
drvinfo->heads == h->drv[drv_index].heads && drvinfo->heads == h->drv[drv_index]->heads &&
drvinfo->sectors == h->drv[drv_index].sectors && drvinfo->sectors == h->drv[drv_index]->sectors &&
drvinfo->cylinders == h->drv[drv_index].cylinders)) drvinfo->cylinders == h->drv[drv_index]->cylinders))
/* The disk is unchanged, nothing to update */ /* The disk is unchanged, nothing to update */
goto freeret; goto freeret;
...@@ -1895,18 +1898,17 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time, ...@@ -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 * If the disk already exists then deregister it before proceeding
* (unless it's the first disk (for the controller node). * (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); printk(KERN_WARNING "disk %d has changed.\n", drv_index);
spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags); 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); 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 * which keeps the interrupt handler from starting
* the queue. * the queue.
*/ */
ret = deregister_disk(h, drv_index, 0, via_ioctl); ret = deregister_disk(h, drv_index, 0, via_ioctl);
h->drv[drv_index].busy_configuring = 0;
} }
/* If the disk is in use return */ /* 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, ...@@ -1914,22 +1916,31 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time,
goto freeret; goto freeret;
/* Save the new information from cciss_geometry_inquiry /* 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; if (h->drv[drv_index] == NULL) {
h->drv[drv_index].nr_blocks = drvinfo->nr_blocks; drvinfo->device_initialized = 0;
h->drv[drv_index].heads = drvinfo->heads; h->drv[drv_index] = drvinfo;
h->drv[drv_index].sectors = drvinfo->sectors; drvinfo = NULL; /* so it won't be freed below. */
h->drv[drv_index].cylinders = drvinfo->cylinders; } else {
h->drv[drv_index].raid_level = drvinfo->raid_level; /* special case for cxd0 */
memcpy(h->drv[drv_index].serial_no, drvinfo->serial_no, 16); h->drv[drv_index]->block_size = drvinfo->block_size;
memcpy(h->drv[drv_index].vendor, drvinfo->vendor, VENDOR_LEN + 1); h->drv[drv_index]->nr_blocks = drvinfo->nr_blocks;
memcpy(h->drv[drv_index].model, drvinfo->model, MODEL_LEN + 1); h->drv[drv_index]->heads = drvinfo->heads;
memcpy(h->drv[drv_index].rev, drvinfo->rev, REV_LEN + 1); 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; ++h->num_luns;
disk = h->gendisk[drv_index]; 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) /* If it's not disk 0 (drv_index != 0)
* or if it was disk 0, but there was previously * 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, ...@@ -1940,6 +1951,7 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time,
if (drv_index || first_time) { if (drv_index || first_time) {
if (cciss_add_disk(h, disk, drv_index) != 0) { if (cciss_add_disk(h, disk, drv_index) != 0) {
cciss_free_gendisk(h, drv_index); cciss_free_gendisk(h, drv_index);
cciss_free_drive_info(h, drv_index);
printk(KERN_WARNING "cciss:%d could not update " printk(KERN_WARNING "cciss:%d could not update "
"disk %d\n", h->ctlr, drv_index); "disk %d\n", h->ctlr, drv_index);
--h->num_luns; --h->num_luns;
...@@ -1956,28 +1968,64 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time, ...@@ -1956,28 +1968,64 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time,
} }
/* This function will find the first index of the controllers drive array /* 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 * that has a null drv pointer and allocate the drive info struct and
* where new drives will be added. If the index to be returned is greater * will return that index This is where new drives will be added.
* than the highest_lun index for the controller then highest_lun is set * If the index to be returned is greater than the highest_lun index for
* to this new index. If there are no available indexes then -1 is returned. * the controller then highest_lun is set * to this new index.
* "controller_node" is used to know if this is a real logical drive, or just * If there are no available indexes or if tha allocation fails, then -1
* the controller node, which determines if this counts towards highest_lun. * 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; int i;
drive_info_struct *drv;
/* Search for an empty slot for our drive info */
for (i = 0; i < CISS_MAX_LUN; i++) { for (i = 0; i < CISS_MAX_LUN; i++) {
if (hba[ctlr]->drv[i].raid_level == -1) {
if (i > hba[ctlr]->highest_lun) /* if not cxd0 case, and it's occupied, skip it. */
if (!controller_node) if (h->drv[i] && i != 0)
hba[ctlr]->highest_lun = i; 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; 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; 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) static void cciss_free_gendisk(ctlr_info_t *h, int drv_index)
{ {
put_disk(h->gendisk[drv_index]); put_disk(h->gendisk[drv_index]);
...@@ -1998,7 +2046,7 @@ static int cciss_add_gendisk(ctlr_info_t *h, unsigned char lunid[], ...@@ -1998,7 +2046,7 @@ static int cciss_add_gendisk(ctlr_info_t *h, unsigned char lunid[],
{ {
int drv_index; 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) if (drv_index == -1)
return -1; return -1;
...@@ -2010,24 +2058,24 @@ static int cciss_add_gendisk(ctlr_info_t *h, unsigned char lunid[], ...@@ -2010,24 +2058,24 @@ static int cciss_add_gendisk(ctlr_info_t *h, unsigned char lunid[],
printk(KERN_ERR "cciss%d: could not " printk(KERN_ERR "cciss%d: could not "
"allocate a new disk %d\n", "allocate a new disk %d\n",
h->ctlr, drv_index); h->ctlr, drv_index);
return -1; goto err_free_drive_info;
} }
} }
memcpy(h->drv[drv_index].LunID, lunid, memcpy(h->drv[drv_index]->LunID, lunid,
sizeof(h->drv[drv_index].LunID)); sizeof(h->drv[drv_index]->LunID));
if (h->drv[drv_index].dev == NULL) { if (cciss_create_ld_sysfs_entry(h, drv_index))
if (cciss_create_ld_sysfs_entry(h, drv_index)) goto err_free_disk;
goto err_free_disk;
}
/* Don't need to mark this busy because nobody */ /* Don't need to mark this busy because nobody */
/* else knows about this disk yet to contend */ /* else knows about this disk yet to contend */
/* for access to it. */ /* for access to it. */
h->drv[drv_index].busy_configuring = 0; h->drv[drv_index]->busy_configuring = 0;
wmb(); wmb();
return drv_index; return drv_index;
err_free_disk: err_free_disk:
cciss_free_gendisk(h, drv_index); cciss_free_gendisk(h, drv_index);
err_free_drive_info:
cciss_free_drive_info(h, drv_index);
return -1; return -1;
} }
...@@ -2047,17 +2095,18 @@ static void cciss_add_controller_node(ctlr_info_t *h) ...@@ -2047,17 +2095,18 @@ static void cciss_add_controller_node(ctlr_info_t *h)
drv_index = cciss_add_gendisk(h, CTLR_LUNID, 1); drv_index = cciss_add_gendisk(h, CTLR_LUNID, 1);
if (drv_index == -1) if (drv_index == -1)
goto error; goto error;
h->drv[drv_index].block_size = 512; h->drv[drv_index]->block_size = 512;
h->drv[drv_index].nr_blocks = 0; h->drv[drv_index]->nr_blocks = 0;
h->drv[drv_index].heads = 0; h->drv[drv_index]->heads = 0;
h->drv[drv_index].sectors = 0; h->drv[drv_index]->sectors = 0;
h->drv[drv_index].cylinders = 0; h->drv[drv_index]->cylinders = 0;
h->drv[drv_index].raid_level = -1; h->drv[drv_index]->raid_level = -1;
memset(h->drv[drv_index].serial_no, 0, 16); memset(h->drv[drv_index]->serial_no, 0, 16);
disk = h->gendisk[drv_index]; disk = h->gendisk[drv_index];
if (cciss_add_disk(h, disk, drv_index) == 0) if (cciss_add_disk(h, disk, drv_index) == 0)
return; return;
cciss_free_gendisk(h, drv_index); cciss_free_gendisk(h, drv_index);
cciss_free_drive_info(h, drv_index);
error: error:
printk(KERN_WARNING "cciss%d: could not " printk(KERN_WARNING "cciss%d: could not "
"add disk 0.\n", h->ctlr); "add disk 0.\n", h->ctlr);
...@@ -2136,12 +2185,12 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time, ...@@ -2136,12 +2185,12 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time,
drv_found = 0; drv_found = 0;
/* skip holes in the array from already deleted drives */ /* skip holes in the array from already deleted drives */
if (h->drv[i].raid_level == -1) if (h->drv[i] == NULL)
continue; continue;
for (j = 0; j < num_luns; j++) { for (j = 0; j < num_luns; j++) {
memcpy(lunid, &ld_buff->LUN[j][0], sizeof(lunid)); 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) { sizeof(lunid)) == 0) {
drv_found = 1; drv_found = 1;
break; break;
...@@ -2150,10 +2199,11 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time, ...@@ -2150,10 +2199,11 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time,
if (!drv_found) { if (!drv_found) {
/* Deregister it from the OS, it's gone. */ /* Deregister it from the OS, it's gone. */
spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags); 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); spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
return_code = deregister_disk(h, i, 1, via_ioctl); 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, ...@@ -2174,9 +2224,9 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time,
* the first free index and add it. * the first free index and add it.
*/ */
for (j = 0; j <= h->highest_lun; j++) { for (j = 0; j <= h->highest_lun; j++) {
if (h->drv[j].raid_level != -1 && if (h->drv[j] != NULL &&
memcmp(h->drv[j].LunID, lunid, memcmp(h->drv[j]->LunID, lunid,
sizeof(h->drv[j].LunID)) == 0) { sizeof(h->drv[j]->LunID)) == 0) {
drv_index = j; drv_index = j;
drv_found = 1; drv_found = 1;
break; break;
...@@ -2253,11 +2303,12 @@ static int deregister_disk(ctlr_info_t *h, int drv_index, ...@@ -2253,11 +2303,12 @@ static int deregister_disk(ctlr_info_t *h, int drv_index,
int i; int i;
struct gendisk *disk; struct gendisk *disk;
drive_info_struct *drv; drive_info_struct *drv;
int recalculate_highest_lun;
if (!capable(CAP_SYS_RAWIO)) if (!capable(CAP_SYS_RAWIO))
return -EPERM; return -EPERM;
drv = &h->drv[drv_index]; drv = h->drv[drv_index];
disk = h->gendisk[drv_index]; disk = h->gendisk[drv_index];
/* make sure logical volume is NOT is use */ /* make sure logical volume is NOT is use */
...@@ -2267,6 +2318,8 @@ static int deregister_disk(ctlr_info_t *h, int drv_index, ...@@ -2267,6 +2318,8 @@ static int deregister_disk(ctlr_info_t *h, int drv_index,
} else if (drv->usage_count > 0) } else if (drv->usage_count > 0)
return -EBUSY; return -EBUSY;
recalculate_highest_lun = (drv == h->drv[h->highest_lun]);
/* invalidate the devices and deregister the disk. If it is disk /* invalidate the devices and deregister the disk. If it is disk
* zero do not deregister it but just zero out it's values. This * zero do not deregister it but just zero out it's values. This
* allows us to delete disk zero but keep the controller registered. * 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, ...@@ -2277,14 +2330,8 @@ static int deregister_disk(ctlr_info_t *h, int drv_index,
cciss_destroy_ld_sysfs_entry(h, drv_index, 0); cciss_destroy_ld_sysfs_entry(h, drv_index, 0);
del_gendisk(disk); del_gendisk(disk);
} }
if (q) { if (q)
blk_cleanup_queue(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 /* If clear_all is set then we are deleting the logical
* drive, not just refreshing its info. For drives * drive, not just refreshing its info. For drives
* other than disk 0 we will call put_disk. We do not * 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, ...@@ -2307,24 +2354,20 @@ static int deregister_disk(ctlr_info_t *h, int drv_index,
} }
} else { } else {
set_capacity(disk, 0); set_capacity(disk, 0);
cciss_clear_drive_info(drv);
} }
--h->num_luns; --h->num_luns;
cciss_clear_drive_info(drv);
/* if it was the last disk, find the new hightest lun */
if (clear_all) { if (clear_all && recalculate_highest_lun) {
/* check to see if it was the last disk */ int i, newhighest = -1;
if (drv == h->drv + h->highest_lun) { for (i = 0; i <= h->highest_lun; i++) {
/* if so, find the new hightest lun */ /* if the disk has size > 0, it is available */
int i, newhighest = -1; if (h->drv[i] && h->drv[i]->heads)
for (i = 0; i <= h->highest_lun; i++) { newhighest = i;
/* if the disk has size > 0, it is available */
if (h->drv[i].heads)
newhighest = i;
}
h->highest_lun = newhighest;
} }
memset(drv->LunID, 0, sizeof(drv->LunID)); h->highest_lun = newhighest;
} }
return 0; return 0;
} }
...@@ -2755,7 +2798,7 @@ static int cciss_revalidate(struct gendisk *disk) ...@@ -2755,7 +2798,7 @@ static int cciss_revalidate(struct gendisk *disk)
InquiryData_struct *inq_buff = NULL; InquiryData_struct *inq_buff = NULL;
for (logvol = 0; logvol < CISS_MAX_LUN; logvol++) { 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) { sizeof(drv->LunID)) == 0) {
FOUND = 1; FOUND = 1;
break; break;
...@@ -4293,8 +4336,7 @@ static int __devinit cciss_init_one(struct pci_dev *pdev, ...@@ -4293,8 +4336,7 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
hba[i]->num_luns = 0; hba[i]->num_luns = 0;
hba[i]->highest_lun = -1; hba[i]->highest_lun = -1;
for (j = 0; j < CISS_MAX_LUN; j++) { for (j = 0; j < CISS_MAX_LUN; j++) {
hba[i]->drv[j].raid_level = -1; hba[i]->drv[j] = NULL;
hba[i]->drv[j].queue = NULL;
hba[i]->gendisk[j] = NULL; hba[i]->gendisk[j] = NULL;
} }
...@@ -4349,12 +4391,7 @@ static int __devinit cciss_init_one(struct pci_dev *pdev, ...@@ -4349,12 +4391,7 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
cciss_destroy_hba_sysfs_entry(hba[i]); cciss_destroy_hba_sysfs_entry(hba[i]);
clean0: clean0:
hba[i]->busy_initializing = 0; 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 * Deliberately omit pci_disable_device(): it does something nasty to
* Smart Array controllers that pci_enable_device does not undo * Smart Array controllers that pci_enable_device does not undo
......
...@@ -45,13 +45,14 @@ typedef struct _drive_info_struct ...@@ -45,13 +45,14 @@ typedef struct _drive_info_struct
* to prevent it from being opened or it's * to prevent it from being opened or it's
* queue from being started. * queue from being started.
*/ */
struct device *dev; struct device dev;
__u8 serial_no[16]; /* from inquiry page 0x83, __u8 serial_no[16]; /* from inquiry page 0x83,
* not necc. null terminated. * not necc. null terminated.
*/ */
char vendor[VENDOR_LEN + 1]; /* SCSI vendor string */ char vendor[VENDOR_LEN + 1]; /* SCSI vendor string */
char model[MODEL_LEN + 1]; /* SCSI model string */ char model[MODEL_LEN + 1]; /* SCSI model string */
char rev[REV_LEN + 1]; /* SCSI revision string */ char rev[REV_LEN + 1]; /* SCSI revision string */
char device_initialized; /* indicates whether dev is initialized */
} drive_info_struct; } drive_info_struct;
struct ctlr_info struct ctlr_info
...@@ -87,7 +88,7 @@ struct ctlr_info ...@@ -87,7 +88,7 @@ struct ctlr_info
BYTE cciss_read_capacity; BYTE cciss_read_capacity;
// information about each logical volume // information about each logical volume
drive_info_struct drv[CISS_MAX_LUN]; drive_info_struct *drv[CISS_MAX_LUN];
struct access_method access; struct access_method access;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment