|
@@ -36,9 +36,11 @@
|
|
|
#include <linux/proc_fs.h>
|
|
|
#include <linux/seq_file.h>
|
|
|
#include <linux/init.h>
|
|
|
+#include <linux/jiffies.h>
|
|
|
#include <linux/hdreg.h>
|
|
|
#include <linux/spinlock.h>
|
|
|
#include <linux/compat.h>
|
|
|
+#include <linux/mutex.h>
|
|
|
#include <asm/uaccess.h>
|
|
|
#include <asm/io.h>
|
|
|
|
|
@@ -155,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);
|
|
@@ -164,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);
|
|
@@ -189,8 +195,13 @@ 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);
|
|
|
+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);
|
|
@@ -245,7 +256,10 @@ static inline void removeQ(CommandList_struct *c)
|
|
|
|
|
|
#include "cciss_scsi.c" /* For SCSI tape support */
|
|
|
|
|
|
-#define RAID_UNKNOWN 6
|
|
|
+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
|
|
|
|
|
@@ -255,9 +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"
|
|
|
-};
|
|
|
|
|
|
static struct proc_dir_entry *proc_cciss;
|
|
|
|
|
@@ -318,7 +329,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;
|
|
@@ -331,7 +342,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",
|
|
@@ -454,9 +465,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,
|
|
@@ -560,11 +581,101 @@ 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 = to_drv(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 ssize_t cciss_show_raid_level(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);
|
|
|
+ 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 ssize_t cciss_show_usage_count(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);
|
|
|
+ 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
|
|
|
+};
|
|
|
+
|
|
|
+static struct attribute_group cciss_host_attr_group = {
|
|
|
+ .attrs = cciss_host_attrs,
|
|
|
+};
|
|
|
+
|
|
|
+static const 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,
|
|
|
+ .release = cciss_hba_release,
|
|
|
+};
|
|
|
+
|
|
|
static struct attribute *cciss_dev_attrs[] = {
|
|
|
&dev_attr_unique_id.attr,
|
|
|
&dev_attr_model.attr,
|
|
|
&dev_attr_vendor.attr,
|
|
|
&dev_attr_rev.attr,
|
|
|
+ &dev_attr_lunid.attr,
|
|
|
+ &dev_attr_raid_level.attr,
|
|
|
+ &dev_attr_usage_count.attr,
|
|
|
NULL
|
|
|
};
|
|
|
|
|
@@ -580,12 +691,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
|
|
@@ -609,6 +732,16 @@ 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)
|
|
|
+{
|
|
|
+ drive_info_struct *drv = to_drv(dev);
|
|
|
+ kfree(drv);
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -617,24 +750,39 @@ 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;
|
|
|
+
|
|
|
+ if (h->drv[drv_index]->device_initialized)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ 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]->device_initialized = 1;
|
|
|
+ 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,
|
|
|
+ int ctlr_exiting)
|
|
|
{
|
|
|
- device_del(&drv->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)
|
|
|
+ return;
|
|
|
+
|
|
|
+ device_del(dev);
|
|
|
+ put_device(dev); /* the "final" put. */
|
|
|
+ h->drv[drv_index] = NULL;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -751,7 +899,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
|
|
@@ -767,7 +915,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;
|
|
|
}
|
|
|
}
|
|
@@ -1132,12 +1281,13 @@ 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;
|
|
|
|
|
|
- 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,
|
|
@@ -1475,7 +1625,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);
|
|
|
|
|
@@ -1532,13 +1685,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
|
|
@@ -1615,16 +1766,23 @@ 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);
|
|
|
+ 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;
|
|
|
disk->fops = &cciss_fops;
|
|
|
- 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);
|
|
@@ -1642,14 +1800,21 @@ static void 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;
|
|
|
+
|
|
|
+cleanup_queue:
|
|
|
+ blk_cleanup_queue(disk->queue);
|
|
|
+ disk->queue = NULL;
|
|
|
+init_queue_failure:
|
|
|
+ return -1;
|
|
|
}
|
|
|
|
|
|
/* This function will check the usage_count of the drive to be updated/added.
|
|
@@ -1662,7 +1827,8 @@ static void cciss_add_disk(ctlr_info_t *h, struct gendisk *disk,
|
|
|
* 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;
|
|
@@ -1672,21 +1838,13 @@ 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);
|
|
|
- drvinfo = kmalloc(sizeof(*drvinfo), GFP_KERNEL);
|
|
|
+ drvinfo = kzalloc(sizeof(*drvinfo), GFP_KERNEL);
|
|
|
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,
|
|
@@ -1719,16 +1877,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;
|
|
|
|
|
@@ -1738,18 +1899,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);
|
|
|
- h->drv[drv_index].busy_configuring = 0;
|
|
|
+ ret = deregister_disk(h, drv_index, 0, via_ioctl);
|
|
|
}
|
|
|
|
|
|
/* If the disk is in use return */
|
|
@@ -1757,22 +1917,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
|
|
@@ -1780,8 +1949,15 @@ 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);
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
freeret:
|
|
|
kfree(inq_buff);
|
|
@@ -1793,28 +1969,70 @@ 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]);
|
|
|
+ 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
|
|
@@ -1824,13 +2042,15 @@ static int cciss_find_free_drive_index(int ctlr, int controller_node)
|
|
|
* 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;
|
|
|
|
|
|
- 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;
|
|
|
+
|
|
|
/*Check if the gendisk needs to be allocated */
|
|
|
if (!h->gendisk[drv_index]) {
|
|
|
h->gendisk[drv_index] =
|
|
@@ -1839,23 +2059,24 @@ static int cciss_add_gendisk(ctlr_info_t *h, __u32 lunid, int controller_node)
|
|
|
printk(KERN_ERR "cciss%d: could not "
|
|
|
"allocate a new disk %d\n",
|
|
|
h->ctlr, drv_index);
|
|
|
- return -1;
|
|
|
+ goto err_free_drive_info;
|
|
|
}
|
|
|
}
|
|
|
- h->drv[drv_index].LunID = lunid;
|
|
|
- if (cciss_create_ld_sysfs_entry(h, &h->drv[drv_index], drv_index))
|
|
|
+ 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:
|
|
|
- put_disk(h->gendisk[drv_index]);
|
|
|
- h->gendisk[drv_index] = NULL;
|
|
|
+ cciss_free_gendisk(h, drv_index);
|
|
|
+err_free_drive_info:
|
|
|
+ cciss_free_drive_info(h, drv_index);
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
@@ -1872,21 +2093,25 @@ 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);
|
|
|
- if (drv_index == -1) {
|
|
|
- printk(KERN_WARNING "cciss%d: could not "
|
|
|
- "add disk 0.\n", h->ctlr);
|
|
|
- return;
|
|
|
- }
|
|
|
- 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);
|
|
|
+ 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);
|
|
|
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);
|
|
|
+ cciss_free_drive_info(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
|
|
@@ -1897,7 +2122,8 @@ static void cciss_add_controller_node(ctlr_info_t *h)
|
|
|
* 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;
|
|
@@ -1907,7 +2133,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))
|
|
@@ -1960,13 +2186,13 @@ 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], 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;
|
|
|
}
|
|
@@ -1974,11 +2200,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);
|
|
|
- cciss_destroy_ld_sysfs_entry(&h->drv[i]);
|
|
|
- h->drv[i].busy_configuring = 0;
|
|
|
+ return_code = deregister_disk(h, i, 1, via_ioctl);
|
|
|
+ if (h->drv[i] != NULL)
|
|
|
+ h->drv[i]->busy_configuring = 0;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -1992,17 +2218,16 @@ 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
|
|
|
* the first free index and add it.
|
|
|
*/
|
|
|
for (j = 0; j <= h->highest_lun; j++) {
|
|
|
- if (h->drv[j].raid_level != -1 &&
|
|
|
- h->drv[j].LunID == lunid) {
|
|
|
+ if (h->drv[j] != NULL &&
|
|
|
+ memcmp(h->drv[j]->LunID, lunid,
|
|
|
+ sizeof(h->drv[j]->LunID)) == 0) {
|
|
|
drv_index = j;
|
|
|
drv_found = 1;
|
|
|
break;
|
|
@@ -2015,7 +2240,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:
|
|
@@ -2032,6 +2258,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:
|
|
@@ -2046,43 +2291,48 @@ mem_msg:
|
|
|
* 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;
|
|
|
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 */
|
|
|
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;
|
|
|
|
|
|
+ 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.
|
|
|
*/
|
|
|
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, 0);
|
|
|
del_gendisk(disk);
|
|
|
- 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 (q)
|
|
|
+ blk_cleanup_queue(q);
|
|
|
/* 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
|
|
@@ -2105,34 +2355,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;
|
|
|
- /* 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.
|
|
|
- */
|
|
|
-
|
|
|
- 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;
|
|
|
- }
|
|
|
|
|
|
- drv->LunID = 0;
|
|
|
+ /* 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;
|
|
|
+ }
|
|
|
+ h->highest_lun = newhighest;
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
@@ -2479,8 +2715,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
|
|
@@ -2514,9 +2748,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);
|
|
|
}
|
|
|
|
|
@@ -2568,7 +2799,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;
|
|
|
}
|
|
@@ -3053,8 +3285,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;
|
|
@@ -3232,20 +3463,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, 0);
|
|
|
+ complete_all(&h->scan_wait);
|
|
|
+ mutex_lock(&scan_mutex);
|
|
|
+ h->busy_scanning = 0;
|
|
|
+ mutex_unlock(&scan_mutex);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -3268,8 +3600,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:
|
|
@@ -3489,7 +3821,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 +3947,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
|
|
@@ -3669,15 +4001,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. */
|
|
@@ -3918,6 +4251,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;
|
|
@@ -3926,6 +4260,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;
|
|
|
|
|
@@ -4001,8 +4337,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;
|
|
|
}
|
|
|
|
|
@@ -4035,14 +4370,8 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
|
|
|
|
|
|
hba[i]->cciss_max_sectors = 2048;
|
|
|
|
|
|
+ rebuild_lun_table(hba[i], 1, 0);
|
|
|
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);
|
|
|
-
|
|
|
return 1;
|
|
|
|
|
|
clean4:
|
|
@@ -4063,12 +4392,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
|
|
@@ -4125,8 +4449,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);
|
|
|
|
|
@@ -4136,8 +4461,10 @@ 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, 1);
|
|
|
del_gendisk(disk);
|
|
|
+ }
|
|
|
if (q)
|
|
|
blk_cleanup_queue(q);
|
|
|
}
|
|
@@ -4170,6 +4497,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);
|
|
|
}
|
|
|
|
|
@@ -4202,15 +4530,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;
|
|
|
+ return err;
|
|
|
|
|
|
-err_bus_register:
|
|
|
+err_thread_stop:
|
|
|
+ kthread_stop(cciss_scan_thread);
|
|
|
+err_bus_unregister:
|
|
|
bus_unregister(&cciss_bus_type);
|
|
|
+
|
|
|
return err;
|
|
|
}
|
|
|
|
|
@@ -4227,6 +4565,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);
|
|
|
}
|