The following series cleans up the broken scan_thread code,
allows triggering of the scan_thread via sysfs, dynamically
allocates the per device structure for each logical drive as needed
instead of all at once at driver init time, fixes a bug in
logical drive addressing (seen on MSA2012), and adds lunid,
raid_level, and usage_count attributes in sysfs for each
logical drive.
---
Andrew Patterson (3):
cciss: Allow triggering of rescan of logical drive topology via sysfs entry
cciss: Use one scan thread per controller and fix hang during rmmod
cciss: Remove sysfs entries for logical drives on driver cleanup.
Stephen M. Cameron (17):
cciss: Dynamically allocate the drive_info_struct for each logical drive.
cciss: Add usage_count attribute to each logical drive in /sys
cciss: Add a "raid_level" attribute to each logical drive in /sys
cciss: fix some magic numbers in the raid-level decoding
cciss: Add lunid attribute to each logical drive in /sys
cciss: Don't check h->busy_initializing in cciss_open().
cciss: Preserve all 8 bytes of LUN ID for logical drives.
cciss: Silence noisy per-disk messages output by cciss_read_capacity
cciss: Fix excessive gendisk freeing bug on driver unload.
cciss: Fix usage_count check in rebuild_lun_table when triggered via sysfs.
cciss: Clear all sysfs-exposed data for deleted logical drives.
cciss: Handle special case for sysfs attributes of the first logical drive.
cciss: Handle cases when cciss_add_disk fails.
cciss: Handle failure of blk_init_queue gracefully in cciss_add_disk.
cciss: Rearrange logical drive sysfs code to make the "changing a disk" path work.
cciss: Dynamically allocate struct device for each logical drive as needed.
cciss: Remove some unused code in rebuild_lun_table()
.../ABI/testing/sysfs-bus-pci-devices-cciss | 28 +
drivers/block/cciss.c | 743 +++++++++++++++-----
drivers/block/cciss.h | 12
3 files changed, 576 insertions(+), 207 deletions(-)
--
-- steve
From: Andrew Patterson <[email protected]>
Sysfs entries for logical drives need to be removed when a drive is
deleted during driver cleanup.
Signed-off-by: Andrew Patterson <[email protected]>
Signed-off-by: Stephen M. Cameron <[email protected]>
Acked-by: Mike Miller <[email protected]>
---
drivers/block/cciss.c | 5 ++++-
1 files changed, 4 insertions(+), 1 deletions(-)
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index a52cc7f..970c896 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -1977,7 +1977,6 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time)
h->drv[i].busy_configuring = 1;
spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
return_code = deregister_disk(h, i, 1);
- cciss_destroy_ld_sysfs_entry(&h->drv[i]);
h->drv[i].busy_configuring = 0;
}
}
@@ -2118,6 +2117,7 @@ static int deregister_disk(ctlr_info_t *h, int drv_index,
* indicate that this element of the drive
* array is free.
*/
+ cciss_destroy_ld_sysfs_entry(drv);
if (clear_all) {
/* check to see if it was the last disk */
@@ -4141,6 +4141,9 @@ static void __devexit cciss_remove_one(struct pci_dev *pdev)
if (q)
blk_cleanup_queue(q);
}
+ if (hba[i]->drv[j].raid_level != -1)
+ cciss_destroy_ld_sysfs_entry(&hba[i]->drv[j]);
+
}
#ifdef CONFIG_CISS_SCSI_TAPE
From: Andrew Patterson <[email protected]>
Replace the use of one scan kthread per controller with one per driver.
Use a queue to hold a list of controllers that need to be rescanned with
routines to add and remove controllers from the queue.
Fix locking and completion handling to prevent a hang during rmmod.
Signed-off-by: Andrew Patterson <[email protected]>
Signed-off-by: Stephen M. Cameron <[email protected]>
Acked-by: Mike Miller <[email protected]>
---
drivers/block/cciss.c | 156 +++++++++++++++++++++++++++++++++++++++++++------
drivers/block/cciss.h | 7 ++
2 files changed, 141 insertions(+), 22 deletions(-)
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index 970c896..e7273a4 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -39,6 +39,7 @@
#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 +156,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);
@@ -3232,20 +3237,121 @@ static irqreturn_t do_cciss_intr(int irq, void *dev_id)
return IRQ_HANDLED;
}
+/**
+ * add_to_scan_list() - add controller to rescan queue
+ * @h: Pointer to the controller.
+ *
+ * Adds the controller to the rescan queue if not already on the queue.
+ *
+ * returns 1 if added to the queue, 0 if skipped (could be on the
+ * queue already, or the controller could be initializing or shutting
+ * down).
+ **/
+static int add_to_scan_list(struct ctlr_info *h)
+{
+ struct ctlr_info *test_h;
+ int found = 0;
+ int ret = 0;
+
+ if (h->busy_initializing)
+ return 0;
+
+ if (!mutex_trylock(&h->busy_shutting_down))
+ return 0;
+
+ mutex_lock(&scan_mutex);
+ list_for_each_entry(test_h, &scan_q, scan_list) {
+ if (test_h == h) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found && !h->busy_scanning) {
+ INIT_COMPLETION(h->scan_wait);
+ list_add_tail(&h->scan_list, &scan_q);
+ ret = 1;
+ }
+ mutex_unlock(&scan_mutex);
+ mutex_unlock(&h->busy_shutting_down);
+
+ return ret;
+}
+
+/**
+ * remove_from_scan_list() - remove controller from rescan queue
+ * @h: Pointer to the controller.
+ *
+ * Removes the controller from the rescan queue if present. Blocks if
+ * the controller is currently conducting a rescan.
+ **/
+static void remove_from_scan_list(struct ctlr_info *h)
+{
+ struct ctlr_info *test_h, *tmp_h;
+ int scanning = 0;
+
+ mutex_lock(&scan_mutex);
+ list_for_each_entry_safe(test_h, tmp_h, &scan_q, scan_list) {
+ if (test_h == h) {
+ list_del(&h->scan_list);
+ complete_all(&h->scan_wait);
+ mutex_unlock(&scan_mutex);
+ return;
+ }
+ }
+ if (&h->busy_scanning)
+ scanning = 0;
+ mutex_unlock(&scan_mutex);
+
+ if (scanning)
+ wait_for_completion(&h->scan_wait);
+}
+
+/**
+ * scan_thread() - kernel thread used to rescan controllers
+ * @data: Ignored.
+ *
+ * A kernel thread used scan for drive topology changes on
+ * controllers. The thread processes only one controller at a time
+ * using a queue. Controllers are added to the queue using
+ * add_to_scan_list() and removed from the queue either after done
+ * processing or using remove_from_scan_list().
+ *
+ * returns 0.
+ **/
static int scan_thread(void *data)
{
- ctlr_info_t *h = data;
- int rc;
- DECLARE_COMPLETION_ONSTACK(wait);
- h->rescan_wait = &wait;
+ struct ctlr_info *h;
- for (;;) {
- rc = wait_for_completion_interruptible(&wait);
+ while (1) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule();
if (kthread_should_stop())
break;
- if (!rc)
- rebuild_lun_table(h, 0);
+
+ while (1) {
+ mutex_lock(&scan_mutex);
+ if (list_empty(&scan_q)) {
+ mutex_unlock(&scan_mutex);
+ break;
+ }
+
+ h = list_entry(scan_q.next,
+ struct ctlr_info,
+ scan_list);
+ list_del(&h->scan_list);
+ h->busy_scanning = 1;
+ mutex_unlock(&scan_mutex);
+
+ if (h) {
+ rebuild_lun_table(h, 0);
+ complete_all(&h->scan_wait);
+ mutex_lock(&scan_mutex);
+ h->busy_scanning = 0;
+ mutex_unlock(&scan_mutex);
+ }
+ }
}
+
return 0;
}
@@ -3268,8 +3374,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:
@@ -3918,6 +4024,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 +4033,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;
@@ -4034,14 +4143,8 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
hba[i]->cciss_max_sectors = 2048;
- hba[i]->busy_initializing = 0;
-
rebuild_lun_table(hba[i], 1);
- hba[i]->cciss_scan_thread = kthread_run(scan_thread, hba[i],
- "cciss_scan%02d", i);
- if (IS_ERR(hba[i]->cciss_scan_thread))
- return PTR_ERR(hba[i]->cciss_scan_thread);
-
+ hba[i]->busy_initializing = 0;
return 1;
clean4:
@@ -4125,8 +4228,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);
@@ -4173,6 +4277,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);
}
@@ -4205,15 +4310,25 @@ static int __init cciss_init(void)
if (err)
return err;
+ /* Start the scan thread */
+ cciss_scan_thread = kthread_run(scan_thread, NULL, "cciss_scan");
+ if (IS_ERR(cciss_scan_thread)) {
+ err = PTR_ERR(cciss_scan_thread);
+ goto err_bus_unregister;
+ }
+
/* Register for our PCI devices */
err = pci_register_driver(&cciss_pci_driver);
if (err)
- goto err_bus_register;
+ goto err_thread_stop;
return 0;
-err_bus_register:
+err_thread_stop:
+ kthread_stop(cciss_scan_thread);
+err_bus_unregister:
bus_unregister(&cciss_bus_type);
+
return err;
}
@@ -4230,6 +4345,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);
}
diff --git a/drivers/block/cciss.h b/drivers/block/cciss.h
index 06a5db2..4fb3639 100644
--- a/drivers/block/cciss.h
+++ b/drivers/block/cciss.h
@@ -2,6 +2,7 @@
#define CCISS_H
#include <linux/genhd.h>
+#include <linux/mutex.h>
#include "cciss_cmd.h"
@@ -108,6 +109,8 @@ struct ctlr_info
int nr_frees;
int busy_configuring;
int busy_initializing;
+ int busy_scanning;
+ struct mutex busy_shutting_down;
/* This element holds the zero based queue number of the last
* queue to be started. It is used for fairness.
@@ -122,8 +125,8 @@ struct ctlr_info
/* and saved for later processing */
#endif
unsigned char alive;
- struct completion *rescan_wait;
- struct task_struct *cciss_scan_thread;
+ struct list_head scan_list;
+ struct completion scan_wait;
struct device dev;
};
From: Andrew Patterson <[email protected]>
Added /sys/bus/pci/devices/<dev>/ccissX/rescan sysfs entry used
to kick off a rescan that discovers logical drive topology changes.
Signed-off-by: Andrew Patterson <[email protected]>
Signed-off-by: Stephen M. Cameron <[email protected]>
Acked-by: Mike Miller <[email protected]>
---
.../ABI/testing/sysfs-bus-pci-devices-cciss | 7 ++++
drivers/block/cciss.c | 36 ++++++++++++++++++--
2 files changed, 40 insertions(+), 3 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-bus-pci-devices-cciss b/Documentation/ABI/testing/sysfs-bus-pci-devices-cciss
index 0a92a7c..ac3429d 100644
--- a/Documentation/ABI/testing/sysfs-bus-pci-devices-cciss
+++ b/Documentation/ABI/testing/sysfs-bus-pci-devices-cciss
@@ -31,3 +31,10 @@ Date: March 2009
Kernel Version: 2.6.30
Contact: [email protected]
Description: A symbolic link to /sys/block/cciss!cXdY
+
+Where: /sys/bus/pci/devices/<dev>/ccissX/rescan
+Date: August 2009
+Kernel Version: 2.6.31
+Contact: [email protected]
+Description: Kicks of a rescan of the controller to discover logical
+ drive topology changes.
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index e7273a4..763535e 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -194,6 +194,7 @@ static int sendcmd_withirq_core(ctlr_info_t *h, CommandList_struct *c,
static int process_sendcmd_error(ctlr_info_t *h, CommandList_struct *c);
static void fail_all_cmds(unsigned long ctlr);
+static int add_to_scan_list(struct ctlr_info *h);
static int scan_thread(void *data);
static int check_for_unit_attention(ctlr_info_t *h, CommandList_struct *c);
@@ -459,9 +460,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,
@@ -565,6 +576,25 @@ static ssize_t dev_show_rev(struct device *dev,
}
DEVICE_ATTR(rev, S_IRUGO, dev_show_rev, NULL);
+static struct attribute *cciss_host_attrs[] = {
+ &dev_attr_rescan.attr,
+ NULL
+};
+
+static struct attribute_group cciss_host_attr_group = {
+ .attrs = cciss_host_attrs,
+};
+
+static struct attribute_group *cciss_host_attr_groups[] = {
+ &cciss_host_attr_group,
+ NULL
+};
+
+static struct device_type cciss_host_type = {
+ .name = "cciss_host",
+ .groups = cciss_host_attr_groups,
+};
+
static struct attribute *cciss_dev_attrs[] = {
&dev_attr_unique_id.attr,
&dev_attr_model.attr,
Remove some unused code in rebuild_lun_table()
Signed-off-by: Stephen M. Cameron <[email protected]>
---
drivers/block/cciss.c | 8 --------
1 files changed, 0 insertions(+), 8 deletions(-)
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index 763535e..1d2d614 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -1707,7 +1707,6 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time)
unsigned long flags = 0;
int ret = 0;
drive_info_struct *drvinfo;
- int was_only_controller_node;
/* Get information about the disk and modify the driver structure */
inq_buff = kmalloc(sizeof(InquiryData_struct), GFP_KERNEL);
@@ -1715,13 +1714,6 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time)
if (inq_buff == NULL || drvinfo == NULL)
goto mem_msg;
- /* See if we're trying to update the "controller node"
- * this will happen the when the first logical drive gets
- * created by ACU.
- */
- was_only_controller_node = (drv_index == 0 &&
- h->drv[0].raid_level == -1);
-
/* testing to see if 16-byte CDBs are already being used */
if (h->cciss_read == CCISS_READ_16) {
cciss_read_capacity_16(h->ctlr, drv_index, 1,
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 <[email protected]>
---
drivers/block/cciss.c | 98 ++++++++++++++++++++++++++++++++++++-------------
drivers/block/cciss.h | 2 +
2 files changed, 73 insertions(+), 27 deletions(-)
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index 1d2d614..4f9ef49 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -197,6 +197,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);
@@ -458,7 +460,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,
@@ -478,8 +479,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;
@@ -508,8 +509,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;
@@ -532,8 +533,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;
@@ -556,8 +557,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;
@@ -593,6 +594,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[] = {
@@ -615,12 +617,24 @@ static 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
@@ -644,6 +658,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);
}
/*
@@ -652,24 +675,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;
}
/*
@@ -1650,7 +1682,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);
@@ -1658,8 +1693,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);
@@ -1685,6 +1724,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.
@@ -1870,7 +1915,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 */
@@ -2144,7 +2189,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 */
@@ -4267,8 +4312,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);
}
@@ -4344,7 +4390,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);
diff --git a/drivers/block/cciss.h b/drivers/block/cciss.h
index 4fb3639..9679342 100644
--- a/drivers/block/cciss.h
+++ b/drivers/block/cciss.h
@@ -45,7 +45,7 @@ typedef struct _drive_info_struct
* to prevent it from being opened or it's
* queue from being started.
*/
- struct device dev;
+ struct device *dev;
__u8 serial_no[16]; /* from inquiry page 0x83,
* not necc. null terminated.
*/
Rearrange logical drive sysfs code to make the "changing a disk" path work.
Signed-off-by: Stephen M. Cameron <[email protected]>
---
drivers/block/cciss.c | 20 ++++++++++----------
1 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index 4f9ef49..557b958 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -1915,9 +1915,10 @@ static int cciss_add_gendisk(ctlr_info_t *h, __u32 lunid, int controller_node)
}
}
h->drv[drv_index].LunID = lunid;
- if (cciss_create_ld_sysfs_entry(h, drv_index))
- goto err_free_disk;
-
+ if (h->drv[drv_index].dev == NULL) {
+ if (cciss_create_ld_sysfs_entry(h, drv_index))
+ goto err_free_disk;
+ }
/* Don't need to mark this busy because nobody */
/* else knows about this disk yet to contend */
/* for access to it. */
@@ -2144,8 +2145,10 @@ static int deregister_disk(ctlr_info_t *h, int drv_index,
*/
if (h->gendisk[0] != disk) {
struct request_queue *q = disk->queue;
- if (disk->flags & GENHD_FL_UP)
+ if (disk->flags & GENHD_FL_UP) {
+ cciss_destroy_ld_sysfs_entry(h, drv_index);
del_gendisk(disk);
+ }
if (q) {
blk_cleanup_queue(q);
/* Set drv->queue to NULL so that we do not try
@@ -2189,7 +2192,6 @@ static int deregister_disk(ctlr_info_t *h, int drv_index,
* indicate that this element of the drive
* array is free.
*/
- cciss_destroy_ld_sysfs_entry(h, drv_index);
if (clear_all) {
/* check to see if it was the last disk */
@@ -4307,15 +4309,13 @@ static void __devexit cciss_remove_one(struct pci_dev *pdev)
if (disk) {
struct request_queue *q = disk->queue;
- if (disk->flags & GENHD_FL_UP)
+ if (disk->flags & GENHD_FL_UP) {
+ cciss_destroy_ld_sysfs_entry(hba[i], j);
del_gendisk(disk);
+ }
if (q)
blk_cleanup_queue(q);
}
- if (hba[i]->drv[j].dev != NULL &&
- (j == 0 || hba[i]->drv[j].raid_level != -1))
- cciss_destroy_ld_sysfs_entry(hba[i], j);
-
}
#ifdef CONFIG_CISS_SCSI_TAPE
Handle failure of blk_init_queue gracefully in cciss_add_disk.
Signed-off-by: Stephen M. Cameron <[email protected]>
---
drivers/block/cciss.c | 3 +++
1 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index 557b958..60fe3c6 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -1689,6 +1689,8 @@ static int cciss_add_disk(ctlr_info_t *h, struct gendisk *disk,
int drv_index)
{
disk->queue = blk_init_queue(do_cciss_request, &h->lock);
+ if (!disk->queue)
+ goto init_queue_failure;
sprintf(disk->disk_name, "cciss/c%dd%d", h->ctlr, drv_index);
disk->major = h->major;
disk->first_minor = drv_index << NWD_SHIFT;
@@ -1729,6 +1731,7 @@ static int cciss_add_disk(ctlr_info_t *h, struct gendisk *disk,
cleanup_queue:
blk_cleanup_queue(disk->queue);
disk->queue = NULL;
+init_queue_failure:
return -1;
}
Handle cases when cciss_add_disk fails.
Signed-off-by: Stephen M. Cameron <[email protected]>
---
drivers/block/cciss.c | 35 +++++++++++++++++++++++++----------
1 files changed, 25 insertions(+), 10 deletions(-)
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index 60fe3c6..7043b3b 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -199,6 +199,7 @@ static int scan_thread(void *data);
static int check_for_unit_attention(ctlr_info_t *h, CommandList_struct *c);
static void cciss_hba_release(struct device *dev);
static void cciss_device_release(struct device *dev);
+static void cciss_free_gendisk(ctlr_info_t *h, int drv_index);
#ifdef CONFIG_PROC_FS
static void cciss_procinit(int i);
@@ -1855,8 +1856,14 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time)
* (raid_leve == -1) then we want to update the
* logical drive's information.
*/
- if (drv_index || first_time)
- cciss_add_disk(h, disk, drv_index);
+ if (drv_index || first_time) {
+ if (cciss_add_disk(h, disk, drv_index) != 0) {
+ cciss_free_gendisk(h, drv_index);
+ printk(KERN_WARNING "cciss:%d could not update "
+ "disk %d\n", h->ctlr, drv_index);
+ --h->num_luns;
+ }
+ }
freeret:
kfree(inq_buff);
@@ -1890,6 +1897,12 @@ static int cciss_find_free_drive_index(int ctlr, int controller_node)
return -1;
}
+static void cciss_free_gendisk(ctlr_info_t *h, int drv_index)
+{
+ put_disk(h->gendisk[drv_index]);
+ h->gendisk[drv_index] = NULL;
+}
+
/* cciss_add_gendisk finds a free hba[]->drv structure
* and allocates a gendisk if needed, and sets the lunid
* in the drvinfo structure. It returns the index into
@@ -1930,8 +1943,7 @@ static int cciss_add_gendisk(ctlr_info_t *h, __u32 lunid, int controller_node)
return drv_index;
err_free_disk:
- put_disk(h->gendisk[drv_index]);
- h->gendisk[drv_index] = NULL;
+ cciss_free_gendisk(h, drv_index);
return -1;
}
@@ -1949,11 +1961,8 @@ static void cciss_add_controller_node(ctlr_info_t *h)
return;
drv_index = cciss_add_gendisk(h, 0, 1);
- if (drv_index == -1) {
- printk(KERN_WARNING "cciss%d: could not "
- "add disk 0.\n", h->ctlr);
- return;
- }
+ if (drv_index == -1)
+ goto error;
h->drv[drv_index].block_size = 512;
h->drv[drv_index].nr_blocks = 0;
h->drv[drv_index].heads = 0;
@@ -1962,7 +1971,13 @@ static void cciss_add_controller_node(ctlr_info_t *h)
h->drv[drv_index].raid_level = -1;
memset(h->drv[drv_index].serial_no, 0, 16);
disk = h->gendisk[drv_index];
- cciss_add_disk(h, disk, drv_index);
+ if (cciss_add_disk(h, disk, drv_index) == 0)
+ return;
+ cciss_free_gendisk(h, drv_index);
+error:
+ printk(KERN_WARNING "cciss%d: could not "
+ "add disk 0.\n", h->ctlr);
+ return;
}
/* This function will add and remove logical drives from the Logical
For c0dx where x is not 0, we handle deletion and addition simply,
but for c0d0, there is the special case that even when there's no
disk, the device node exists so that the controller may be accessed.
So, for c0d0, we only create the sysfs entries once, when a controller
is added, and only remove them once, when a controller is being
taken down.
Signed-off-by: Stephen M. Cameron <[email protected]>
---
drivers/block/cciss.c | 18 ++++++++++++++----
1 files changed, 14 insertions(+), 4 deletions(-)
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index 7043b3b..98d8efe 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -681,6 +681,10 @@ static long cciss_create_ld_sysfs_entry(struct ctlr_info *h,
{
struct device *dev;
+ /* Special case for c*d0, we only create it once. */
+ if (drv_index == 0 && h->drv[drv_index].dev != NULL)
+ return 0;
+
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
@@ -697,9 +701,15 @@ static long cciss_create_ld_sysfs_entry(struct ctlr_info *h,
/*
* Remove sysfs entries for a logical drive.
*/
-static void cciss_destroy_ld_sysfs_entry(struct ctlr_info *h, int drv_index)
+static void cciss_destroy_ld_sysfs_entry(struct ctlr_info *h, int drv_index,
+ int ctlr_exiting)
{
struct device *dev = h->drv[drv_index].dev;
+
+ /* special case for c*d0, we only destroy it on controller exit */
+ if (drv_index == 0 && !ctlr_exiting)
+ return;
+
device_del(dev);
put_device(dev); /* the "final" put. */
h->drv[drv_index].dev = NULL;
@@ -1919,6 +1929,7 @@ static int cciss_add_gendisk(ctlr_info_t *h, __u32 lunid, int controller_node)
drv_index = cciss_find_free_drive_index(h->ctlr, controller_node);
if (drv_index == -1)
return -1;
+
/*Check if the gendisk needs to be allocated */
if (!h->gendisk[drv_index]) {
h->gendisk[drv_index] =
@@ -2164,7 +2175,7 @@ static int deregister_disk(ctlr_info_t *h, int drv_index,
if (h->gendisk[0] != disk) {
struct request_queue *q = disk->queue;
if (disk->flags & GENHD_FL_UP) {
- cciss_destroy_ld_sysfs_entry(h, drv_index);
+ cciss_destroy_ld_sysfs_entry(h, drv_index, 0);
del_gendisk(disk);
}
if (q) {
@@ -2210,7 +2221,6 @@ static int deregister_disk(ctlr_info_t *h, int drv_index,
* indicate that this element of the drive
* array is free.
*/
-
if (clear_all) {
/* check to see if it was the last disk */
if (drv == h->drv + h->highest_lun) {
@@ -4328,7 +4338,7 @@ static void __devexit cciss_remove_one(struct pci_dev *pdev)
struct request_queue *q = disk->queue;
if (disk->flags & GENHD_FL_UP) {
- cciss_destroy_ld_sysfs_entry(hba[i], j);
+ cciss_destroy_ld_sysfs_entry(hba[i], j, 1);
del_gendisk(disk);
}
if (q)
When removing a logical drive, clear all the information that is
now exposed by sysfs (e.g. vendor, model, serial number.)
Signed-off-by: Stephen M. Cameron <[email protected]>
---
drivers/block/cciss.c | 31 +++++++++++++++++++++----------
1 files changed, 21 insertions(+), 10 deletions(-)
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index 98d8efe..8cd2f8e 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -2133,6 +2133,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:
@@ -2211,16 +2230,8 @@ static int deregister_disk(ctlr_info_t *h, int drv_index,
}
--h->num_luns;
- /* zero out the disk size info */
- drv->nr_blocks = 0;
- drv->block_size = 0;
- drv->heads = 0;
- drv->sectors = 0;
- drv->cylinders = 0;
- drv->raid_level = -1; /* This can be used as a flag variable to
- * indicate that this element of the drive
- * array is free.
- */
+ cciss_clear_drive_info(drv);
+
if (clear_all) {
/* check to see if it was the last disk */
if (drv == h->drv + h->highest_lun) {
When rebuild_lun_table is reached via sysfs, the usage count that
is checked prior to messing with c0d0 has different constraints
(must be zero) than if rebuild_lun_table is reached via ioctl
(must be one.) Fix rebuild_lun_table to take that into account.
Signed-off-by: Stephen M. Cameron <[email protected]>
---
drivers/block/cciss.c | 33 +++++++++++++++++++++------------
1 files changed, 21 insertions(+), 12 deletions(-)
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index 8cd2f8e..14d4cd6 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -169,9 +169,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);
@@ -1210,7 +1210,7 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode,
case CCISS_DEREGDISK:
case CCISS_REGNEWD:
case CCISS_REVALIDVOLS:
- return rebuild_lun_table(host, 0);
+ return rebuild_lun_table(host, 0, 1);
case CCISS_GETLUNINFO:{
LogvolInfo_struct luninfo;
@@ -1756,7 +1756,8 @@ init_queue_failure:
* is also the controller node. Any changes to disk 0 will show up on
* the next reboot.
*/
-static void cciss_update_drive_info(int ctlr, int drv_index, int first_time)
+static void cciss_update_drive_info(int ctlr, int drv_index, int first_time,
+ int via_ioctl)
{
ctlr_info_t *h = hba[ctlr];
struct gendisk *disk;
@@ -1834,7 +1835,7 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time)
* which keeps the interrupt handler from starting
* the queue.
*/
- ret = deregister_disk(h, drv_index, 0);
+ ret = deregister_disk(h, drv_index, 0, via_ioctl);
h->drv[drv_index].busy_configuring = 0;
}
@@ -1999,7 +2000,8 @@ error:
* INPUT
* h = The controller to perform the operations on
*/
-static int rebuild_lun_table(ctlr_info_t *h, int first_time)
+static int rebuild_lun_table(ctlr_info_t *h, int first_time,
+ int via_ioctl)
{
int ctlr = h->ctlr;
int num_luns;
@@ -2078,7 +2080,7 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time)
spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
h->drv[i].busy_configuring = 1;
spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
- return_code = deregister_disk(h, i, 1);
+ return_code = deregister_disk(h, i, 1, via_ioctl);
h->drv[i].busy_configuring = 0;
}
}
@@ -2116,7 +2118,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:
@@ -2166,9 +2169,15 @@ static void cciss_clear_drive_info(drive_info_struct *drive_info)
* the disk in preparation for re-adding it. In this case
* the highest_lun should be left unchanged and the LunID
* should not be cleared.
+ * via_ioctl
+ * This indicates whether we've reached this path via ioctl.
+ * This affects the maximum usage count allowed for c0d0 to be messed with.
+ * If this path is reached via ioctl(), then the max_usage_count will
+ * be 1, as the process calling ioctl() has got to have the device open.
+ * If we get here via sysfs, then the max usage count will be zero.
*/
static int deregister_disk(ctlr_info_t *h, int drv_index,
- int clear_all)
+ int clear_all, int via_ioctl)
{
int i;
struct gendisk *disk;
@@ -2182,7 +2191,7 @@ static int deregister_disk(ctlr_info_t *h, int drv_index,
/* make sure logical volume is NOT is use */
if (clear_all || (h->gendisk[0] == disk)) {
- if (drv->usage_count > 1)
+ if (drv->usage_count > via_ioctl)
return -EBUSY;
} else if (drv->usage_count > 0)
return -EBUSY;
@@ -3451,7 +3460,7 @@ static int scan_thread(void *data)
mutex_unlock(&scan_mutex);
if (h) {
- rebuild_lun_table(h, 0);
+ rebuild_lun_table(h, 0, 0);
complete_all(&h->scan_wait);
mutex_lock(&scan_mutex);
h->busy_scanning = 0;
@@ -4251,7 +4260,7 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
hba[i]->cciss_max_sectors = 2048;
- rebuild_lun_table(hba[i], 1);
+ rebuild_lun_table(hba[i], 1, 0);
hba[i]->busy_initializing = 0;
return 1;
Fix bug that free_hba was calling put_disk for all gendisk[]
pointers -- all 1024 of them -- regardless of whether the were
used or not (NULL). This bug could cause rmmod to oops if logical
drives had been deleted during the driver's lifetime.
Signed-off-by: Stephen M. Cameron <[email protected]>
---
drivers/block/cciss.c | 15 ++++++++-------
1 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index 14d4cd6..a4cb041 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -3892,15 +3892,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. */
Silence noisy per-disk messages output by cciss_read_capacity
Signed-off-by: Stephen M. Cameron <[email protected]>
---
drivers/block/cciss.c | 5 -----
1 files changed, 0 insertions(+), 5 deletions(-)
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index a4cb041..239c4f5 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -2601,8 +2601,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
@@ -2636,9 +2634,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);
}
Preserve all 8 bytes of the LunID field returned
by CCISS_REPORT_LOGICAL instead of only saving 4 bytes.
This fixes a bug with logical volume addressing encountered on
an MSA2012.
Signed-off-by: Stephen M. Cameron <[email protected]>
---
drivers/block/cciss.c | 48 ++++++++++++++++++++++++------------------------
drivers/block/cciss.h | 2 +-
2 files changed, 25 insertions(+), 25 deletions(-)
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index 239c4f5..56eeb68 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -845,7 +845,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;
}
}
@@ -1215,7 +1216,8 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode,
case CCISS_GETLUNINFO:{
LogvolInfo_struct luninfo;
- luninfo.LunID = drv->LunID;
+ memcpy(&luninfo.LunID, drv->LunID,
+ sizeof(luninfo.LunID));
luninfo.num_opens = drv->usage_count;
luninfo.num_parts = 0;
if (copy_to_user(argp, &luninfo,
@@ -1610,13 +1612,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
@@ -1923,7 +1923,8 @@ static void cciss_free_gendisk(ctlr_info_t *h, int drv_index)
* a means to talk to the controller in case no logical
* drives have yet been configured.
*/
-static int cciss_add_gendisk(ctlr_info_t *h, __u32 lunid, int controller_node)
+static int cciss_add_gendisk(ctlr_info_t *h, unsigned char lunid[],
+ int controller_node)
{
int drv_index;
@@ -1942,7 +1943,8 @@ static int cciss_add_gendisk(ctlr_info_t *h, __u32 lunid, int controller_node)
return -1;
}
}
- h->drv[drv_index].LunID = lunid;
+ memcpy(h->drv[drv_index].LunID, lunid,
+ sizeof(h->drv[drv_index].LunID));
if (h->drv[drv_index].dev == NULL) {
if (cciss_create_ld_sysfs_entry(h, drv_index))
goto err_free_disk;
@@ -1972,7 +1974,7 @@ static void cciss_add_controller_node(ctlr_info_t *h)
if (h->gendisk[0] != NULL) /* already did this? Then bail. */
return;
- drv_index = cciss_add_gendisk(h, 0, 1);
+ drv_index = cciss_add_gendisk(h, CTLR_LUNID, 1);
if (drv_index == -1)
goto error;
h->drv[drv_index].block_size = 512;
@@ -2011,7 +2013,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))
@@ -2068,9 +2070,9 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time,
continue;
for (j = 0; j < num_luns; j++) {
- memcpy(&lunid, &ld_buff->LUN[j][0], 4);
- lunid = le32_to_cpu(lunid);
- if (h->drv[i].LunID == lunid) {
+ memcpy(lunid, &ld_buff->LUN[j][0], sizeof(lunid));
+ if (memcmp(h->drv[i].LunID, lunid,
+ sizeof(lunid)) == 0) {
drv_found = 1;
break;
}
@@ -2095,9 +2097,7 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time,
drv_found = 0;
- memcpy(&lunid, &ld_buff->LUN[i][0], 4);
- lunid = le32_to_cpu(lunid);
-
+ memcpy(lunid, &ld_buff->LUN[i][0], sizeof(lunid));
/* Find if the LUN is already in the drive array
* of the driver. If so then update its info
* if not in use. If it does not exist then find
@@ -2105,7 +2105,8 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time,
*/
for (j = 0; j <= h->highest_lun; j++) {
if (h->drv[j].raid_level != -1 &&
- h->drv[j].LunID == lunid) {
+ memcmp(h->drv[j].LunID, lunid,
+ sizeof(h->drv[j].LunID)) == 0) {
drv_index = j;
drv_found = 1;
break;
@@ -2253,8 +2254,7 @@ static int deregister_disk(ctlr_info_t *h, int drv_index,
}
h->highest_lun = newhighest;
}
-
- drv->LunID = 0;
+ memset(drv->LunID, 0, sizeof(drv->LunID));
}
return 0;
}
@@ -2685,7 +2685,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;
}
@@ -3170,8 +3171,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;
diff --git a/drivers/block/cciss.h b/drivers/block/cciss.h
index 9679342..5188f71 100644
--- a/drivers/block/cciss.h
+++ b/drivers/block/cciss.h
@@ -30,7 +30,7 @@ struct access_method {
};
typedef struct _drive_info_struct
{
- __u32 LunID;
+ unsigned char LunID[8];
int usage_count;
struct request_queue *queue;
sector_t nr_blocks;
Don't check h->busy_initializing in cciss_open(). Open won't be
called before things are ready, but h->busy_initializing won't be
unset until after the initial rebuild_lun_table is finished. But,
to read the partitions, cciss_open will be called for each logical
drive during rebuild_lun_table. If cciss_open checks h->busy_initializing,
then the reading of the partition information during the initial
rebuild_lun_table will fail, which is especially bad news if it
happens to be your boot device.
Signed-off-by: Stephen M. Cameron <[email protected]>
---
drivers/block/cciss.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index 56eeb68..0ff665e 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -829,7 +829,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
Add lunid attribute to each logical drive at
/sys/devices/<dev>/ccissX/cXdY/lunid for controller X,
logical drive Y
Signed-off-by: Stephen M. Cameron <[email protected]>
---
.../ABI/testing/sysfs-bus-pci-devices-cciss | 7 +++++
drivers/block/cciss.c | 26 ++++++++++++++++++++
2 files changed, 33 insertions(+), 0 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-bus-pci-devices-cciss b/Documentation/ABI/testing/sysfs-bus-pci-devices-cciss
index ac3429d..5a6c8d3 100644
--- a/Documentation/ABI/testing/sysfs-bus-pci-devices-cciss
+++ b/Documentation/ABI/testing/sysfs-bus-pci-devices-cciss
@@ -38,3 +38,10 @@ Kernel Version: 2.6.31
Contact: [email protected]
Description: Kicks of a rescan of the controller to discover logical
drive topology changes.
+
+Where: /sys/bus/pci/devices/<dev>/ccissX/cXdY/lunid
+Date: August 2009
+Kernel Version: 2.6.31
+Contact: [email protected]
+Description: Displays the 8-byte LUN ID used to address logical
+ drive Y of controller X.
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index 0ff665e..a8684d0 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -578,6 +578,31 @@ static ssize_t dev_show_rev(struct device *dev,
}
DEVICE_ATTR(rev, S_IRUGO, dev_show_rev, NULL);
+static ssize_t cciss_show_lunid(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ drive_info_struct *drv = dev_get_drvdata(dev);
+ struct ctlr_info *h = to_hba(drv->dev->parent);
+ unsigned long flags;
+ unsigned char lunid[8];
+
+ spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+ if (h->busy_configuring) {
+ spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+ return -EBUSY;
+ }
+ if (!drv->heads) {
+ spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+ return -ENOTTY;
+ }
+ memcpy(lunid, drv->LunID, sizeof(lunid));
+ spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+ return snprintf(buf, 20, "0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
+ lunid[0], lunid[1], lunid[2], lunid[3],
+ lunid[4], lunid[5], lunid[6], lunid[7]);
+}
+DEVICE_ATTR(lunid, S_IRUGO, cciss_show_lunid, NULL);
+
static struct attribute *cciss_host_attrs[] = {
&dev_attr_rescan.attr,
NULL
@@ -603,6 +628,7 @@ static struct attribute *cciss_dev_attrs[] = {
&dev_attr_model.attr,
&dev_attr_vendor.attr,
&dev_attr_rev.attr,
+ &dev_attr_lunid.attr,
NULL
};
cciss: fix some magic numbers in the raid-level decoding
Signed-off-by: Stephen M. Cameron <[email protected]>
---
drivers/block/cciss.c | 5 ++---
1 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index a8684d0..84f15ec 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -254,8 +254,6 @@ static inline void removeQ(CommandList_struct *c)
#include "cciss_scsi.c" /* For SCSI tape support */
-#define RAID_UNKNOWN 6
-
#ifdef CONFIG_PROC_FS
/*
@@ -267,6 +265,7 @@ static inline void removeQ(CommandList_struct *c)
static const char *raid_label[] = { "0", "4", "1(1+0)", "5", "5+1", "ADG",
"UNKNOWN"
};
+#define RAID_UNKNOWN (sizeof(raid_label) / sizeof(raid_label[0])-1)
static struct proc_dir_entry *proc_cciss;
@@ -340,7 +339,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",
and change get rid of some magic numbers in raid lavel decoding.
Add raid_level attribute to each logical drive at
/sys/devices/<dev>/ccissX/cXdY/raid_level for controller X,
logical drive Y
Signed-off-by: Stephen M. Cameron <[email protected]>
---
.../ABI/testing/sysfs-bus-pci-devices-cciss | 7 ++++++
drivers/block/cciss.c | 24 ++++++++++++++++++++
2 files changed, 31 insertions(+), 0 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-bus-pci-devices-cciss b/Documentation/ABI/testing/sysfs-bus-pci-devices-cciss
index 5a6c8d3..8d02602 100644
--- a/Documentation/ABI/testing/sysfs-bus-pci-devices-cciss
+++ b/Documentation/ABI/testing/sysfs-bus-pci-devices-cciss
@@ -45,3 +45,10 @@ Kernel Version: 2.6.31
Contact: [email protected]
Description: Displays the 8-byte LUN ID used to address logical
drive Y of controller X.
+
+Where: /sys/bus/pci/devices/<dev>/ccissX/cXdY/raid_level
+Date: August 2009
+Kernel Version: 2.6.31
+Contact: [email protected]
+Description: Displays the RAID level of logical drive Y of
+ controller X.
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index 84f15ec..dc3461e 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -602,6 +602,29 @@ static ssize_t cciss_show_lunid(struct device *dev,
}
DEVICE_ATTR(lunid, S_IRUGO, cciss_show_lunid, NULL);
+static ssize_t cciss_show_raid_level(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ drive_info_struct *drv = dev_get_drvdata(dev);
+ struct ctlr_info *h = to_hba(drv->dev->parent);
+ int raid;
+ unsigned long flags;
+
+ spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+ if (h->busy_configuring) {
+ spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+ return -EBUSY;
+ }
+ raid = drv->raid_level;
+ spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+ if (raid < 0 || raid > RAID_UNKNOWN)
+ raid = RAID_UNKNOWN;
+
+ return snprintf(buf, strlen(raid_label[raid]) + 7, "RAID %s\n",
+ raid_label[raid]);
+}
+DEVICE_ATTR(raid_level, S_IRUGO, cciss_show_raid_level, NULL);
+
static struct attribute *cciss_host_attrs[] = {
&dev_attr_rescan.attr,
NULL
@@ -628,6 +651,7 @@ static struct attribute *cciss_dev_attrs[] = {
&dev_attr_vendor.attr,
&dev_attr_rev.attr,
&dev_attr_lunid.attr,
+ &dev_attr_raid_level.attr,
NULL
};
Add usage_count attribute to each logical drive at
/sys/devices/<dev>/ccissX/cXdY/usage_count for controller X,
logical drive Y. The usage count is the number of times
the device has currently been opened.
Signed-off-by: Stephen M. Cameron <[email protected]>
---
.../ABI/testing/sysfs-bus-pci-devices-cciss | 7 +++++++
drivers/block/cciss.c | 20 ++++++++++++++++++++
2 files changed, 27 insertions(+), 0 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-bus-pci-devices-cciss b/Documentation/ABI/testing/sysfs-bus-pci-devices-cciss
index 8d02602..4f29e5f 100644
--- a/Documentation/ABI/testing/sysfs-bus-pci-devices-cciss
+++ b/Documentation/ABI/testing/sysfs-bus-pci-devices-cciss
@@ -52,3 +52,10 @@ Kernel Version: 2.6.31
Contact: [email protected]
Description: Displays the RAID level of logical drive Y of
controller X.
+
+Where: /sys/bus/pci/devices/<dev>/ccissX/cXdY/usage_count
+Date: August 2009
+Kernel Version: 2.6.31
+Contact: [email protected]
+Description: Displays the usage count (number of opens) of logical drive Y
+ of controller X.
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index dc3461e..f3f30c5 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -625,6 +625,25 @@ static ssize_t cciss_show_raid_level(struct device *dev,
}
DEVICE_ATTR(raid_level, S_IRUGO, cciss_show_raid_level, NULL);
+static ssize_t cciss_show_usage_count(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ drive_info_struct *drv = dev_get_drvdata(dev);
+ struct ctlr_info *h = to_hba(drv->dev->parent);
+ unsigned long flags;
+ int count;
+
+ spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+ if (h->busy_configuring) {
+ spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+ return -EBUSY;
+ }
+ count = drv->usage_count;
+ spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+ return snprintf(buf, 20, "%d\n", count);
+}
+DEVICE_ATTR(usage_count, S_IRUGO, cciss_show_usage_count, NULL);
+
static struct attribute *cciss_host_attrs[] = {
&dev_attr_rescan.attr,
NULL
@@ -652,6 +671,7 @@ static struct attribute *cciss_dev_attrs[] = {
&dev_attr_rev.attr,
&dev_attr_lunid.attr,
&dev_attr_raid_level.attr,
+ &dev_attr_usage_count.attr,
NULL
};
cciss: Dynamically allocate the drive_info_struct for each logical drive.
This reduces the size of the per-hba ctlr_info structure from 106936
bytes to 8132 bytes. That's on 32-bit systems. On 64-bit systems, the
improvement is even bigger. Without this, the ctlr_info struct is so big
that the driver won't even load on a 64 bit system if CISS_MAX_LUN was
at it's current setting of 1024 logical drives.
Signed-off-by: Stephen M. Cameron <[email protected]>
---
drivers/block/cciss.c | 293 ++++++++++++++++++++++++++++---------------------
drivers/block/cciss.h | 5 +
2 files changed, 168 insertions(+), 130 deletions(-)
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index f3f30c5..3c74ddf 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -200,6 +200,7 @@ static int check_for_unit_attention(ctlr_info_t *h, CommandList_struct *c);
static void cciss_hba_release(struct device *dev);
static void cciss_device_release(struct device *dev);
static void cciss_free_gendisk(ctlr_info_t *h, int drv_index);
+static void cciss_free_drive_info(ctlr_info_t *h, int drv_index);
#ifdef CONFIG_PROC_FS
static void cciss_procinit(int i);
@@ -326,7 +327,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;
@@ -460,6 +461,7 @@ static void __devinit cciss_procinit(int i)
#define MAX_PRODUCT_NAME_LEN 19
#define to_hba(n) container_of(n, struct ctlr_info, dev)
+#define to_drv(n) container_of(n, drive_info_struct, dev)
static ssize_t host_store_rescan(struct device *dev,
struct device_attribute *attr,
@@ -479,8 +481,8 @@ static ssize_t dev_show_unique_id(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- drive_info_struct *drv = dev_get_drvdata(dev);
- struct ctlr_info *h = to_hba(drv->dev->parent);
+ drive_info_struct *drv = to_drv(dev);
+ struct ctlr_info *h = to_hba(drv->dev.parent);
__u8 sn[16];
unsigned long flags;
int ret = 0;
@@ -509,8 +511,8 @@ static ssize_t dev_show_vendor(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- drive_info_struct *drv = dev_get_drvdata(dev);
- struct ctlr_info *h = to_hba(drv->dev->parent);
+ drive_info_struct *drv = to_drv(dev);
+ struct ctlr_info *h = to_hba(drv->dev.parent);
char vendor[VENDOR_LEN + 1];
unsigned long flags;
int ret = 0;
@@ -533,8 +535,8 @@ static ssize_t dev_show_model(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- drive_info_struct *drv = dev_get_drvdata(dev);
- struct ctlr_info *h = to_hba(drv->dev->parent);
+ drive_info_struct *drv = to_drv(dev);
+ struct ctlr_info *h = to_hba(drv->dev.parent);
char model[MODEL_LEN + 1];
unsigned long flags;
int ret = 0;
@@ -557,8 +559,8 @@ static ssize_t dev_show_rev(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- drive_info_struct *drv = dev_get_drvdata(dev);
- struct ctlr_info *h = to_hba(drv->dev->parent);
+ drive_info_struct *drv = to_drv(dev);
+ struct ctlr_info *h = to_hba(drv->dev.parent);
char rev[REV_LEN + 1];
unsigned long flags;
int ret = 0;
@@ -580,8 +582,8 @@ DEVICE_ATTR(rev, S_IRUGO, dev_show_rev, NULL);
static ssize_t cciss_show_lunid(struct device *dev,
struct device_attribute *attr, char *buf)
{
- drive_info_struct *drv = dev_get_drvdata(dev);
- struct ctlr_info *h = to_hba(drv->dev->parent);
+ drive_info_struct *drv = to_drv(dev);
+ struct ctlr_info *h = to_hba(drv->dev.parent);
unsigned long flags;
unsigned char lunid[8];
@@ -605,8 +607,8 @@ DEVICE_ATTR(lunid, S_IRUGO, cciss_show_lunid, NULL);
static ssize_t cciss_show_raid_level(struct device *dev,
struct device_attribute *attr, char *buf)
{
- drive_info_struct *drv = dev_get_drvdata(dev);
- struct ctlr_info *h = to_hba(drv->dev->parent);
+ drive_info_struct *drv = to_drv(dev);
+ struct ctlr_info *h = to_hba(drv->dev.parent);
int raid;
unsigned long flags;
@@ -628,8 +630,8 @@ DEVICE_ATTR(raid_level, S_IRUGO, cciss_show_raid_level, NULL);
static ssize_t cciss_show_usage_count(struct device *dev,
struct device_attribute *attr, char *buf)
{
- drive_info_struct *drv = dev_get_drvdata(dev);
- struct ctlr_info *h = to_hba(drv->dev->parent);
+ drive_info_struct *drv = to_drv(dev);
+ struct ctlr_info *h = to_hba(drv->dev.parent);
unsigned long flags;
int count;
@@ -732,11 +734,12 @@ static void cciss_destroy_hba_sysfs_entry(struct ctlr_info *h)
}
/* cciss_device_release is called when the reference count
- * of h->drv[x].dev goes to zero.
+ * of h->drv[x]dev goes to zero.
*/
static void cciss_device_release(struct device *dev)
{
- kfree(dev);
+ drive_info_struct *drv = to_drv(dev);
+ kfree(drv);
}
/*
@@ -750,20 +753,16 @@ static long cciss_create_ld_sysfs_entry(struct ctlr_info *h,
{
struct device *dev;
- /* Special case for c*d0, we only create it once. */
- if (drv_index == 0 && h->drv[drv_index].dev != NULL)
+ if (h->drv[drv_index]->device_initialized)
return 0;
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (!dev)
- return -ENOMEM;
+ dev = &h->drv[drv_index]->dev;
device_initialize(dev);
dev->type = &cciss_dev_type;
dev->bus = &cciss_bus_type;
dev_set_name(dev, "c%dd%d", h->ctlr, drv_index);
dev->parent = &h->dev;
- h->drv[drv_index].dev = dev;
- dev_set_drvdata(dev, &h->drv[drv_index]);
+ h->drv[drv_index]->device_initialized = 1;
return device_add(dev);
}
@@ -773,7 +772,7 @@ static long cciss_create_ld_sysfs_entry(struct ctlr_info *h,
static void cciss_destroy_ld_sysfs_entry(struct ctlr_info *h, int drv_index,
int ctlr_exiting)
{
- struct device *dev = h->drv[drv_index].dev;
+ struct device *dev = &h->drv[drv_index]->dev;
/* special case for c*d0, we only destroy it on controller exit */
if (drv_index == 0 && !ctlr_exiting)
@@ -781,7 +780,7 @@ static void cciss_destroy_ld_sysfs_entry(struct ctlr_info *h, int drv_index,
device_del(dev);
put_device(dev); /* the "final" put. */
- h->drv[drv_index].dev = NULL;
+ h->drv[drv_index] = NULL;
}
/*
@@ -1624,7 +1623,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);
@@ -1684,8 +1686,8 @@ static void cciss_softirq_done(struct request *rq)
static inline void log_unit_to_scsi3addr(ctlr_info_t *h,
unsigned char scsi3addr[], uint32_t log_unit)
{
- memcpy(scsi3addr, h->drv[log_unit].LunID,
- sizeof(h->drv[log_unit].LunID));
+ memcpy(scsi3addr, h->drv[log_unit]->LunID,
+ sizeof(h->drv[log_unit]->LunID));
}
/* This function gets the SCSI vendor, model, and revision of a logical drive
@@ -1775,12 +1777,10 @@ static int cciss_add_disk(ctlr_info_t *h, struct gendisk *disk,
disk->major = h->major;
disk->first_minor = drv_index << NWD_SHIFT;
disk->fops = &cciss_fops;
- if (h->drv[drv_index].dev == NULL) {
- if (cciss_create_ld_sysfs_entry(h, drv_index))
- goto cleanup_queue;
- }
- disk->private_data = &h->drv[drv_index];
- disk->driverfs_dev = h->drv[drv_index].dev;
+ if (cciss_create_ld_sysfs_entry(h, drv_index))
+ goto cleanup_queue;
+ disk->private_data = h->drv[drv_index];
+ disk->driverfs_dev = &h->drv[drv_index]->dev;
/* Set up queue information */
blk_queue_bounce_limit(disk->queue, h->pdev->dma_mask);
@@ -1798,13 +1798,13 @@ static int cciss_add_disk(ctlr_info_t *h, struct gendisk *disk,
disk->queue->queuedata = h;
blk_queue_logical_block_size(disk->queue,
- h->drv[drv_index].block_size);
+ h->drv[drv_index]->block_size);
/* Make sure all queue data is written out before */
- /* setting h->drv[drv_index].queue, as setting this */
+ /* setting h->drv[drv_index]->queue, as setting this */
/* allows the interrupt handler to start the queue */
wmb();
- h->drv[drv_index].queue = disk->queue;
+ h->drv[drv_index]->queue = disk->queue;
add_disk(disk);
return 0;
@@ -1839,7 +1839,7 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time,
/* Get information about the disk and modify the driver structure */
inq_buff = kmalloc(sizeof(InquiryData_struct), GFP_KERNEL);
- drvinfo = kmalloc(sizeof(*drvinfo), GFP_KERNEL);
+ drvinfo = kzalloc(sizeof(*drvinfo), GFP_KERNEL);
if (inq_buff == NULL || drvinfo == NULL)
goto mem_msg;
@@ -1875,16 +1875,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;
@@ -1894,18 +1897,17 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time,
* If the disk already exists then deregister it before proceeding
* (unless it's the first disk (for the controller node).
*/
- if (h->drv[drv_index].raid_level != -1 && drv_index != 0) {
+ if (h->drv[drv_index]->raid_level != -1 && drv_index != 0) {
printk(KERN_WARNING "disk %d has changed.\n", drv_index);
spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
- h->drv[drv_index].busy_configuring = 1;
+ h->drv[drv_index]->busy_configuring = 1;
spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
- /* deregister_disk sets h->drv[drv_index].queue = NULL
+ /* deregister_disk sets h->drv[drv_index]->queue = NULL
* which keeps the interrupt handler from starting
* the queue.
*/
ret = deregister_disk(h, drv_index, 0, via_ioctl);
- h->drv[drv_index].busy_configuring = 0;
}
/* If the disk is in use return */
@@ -1913,22 +1915,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
@@ -1939,6 +1950,7 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time,
if (drv_index || first_time) {
if (cciss_add_disk(h, disk, drv_index) != 0) {
cciss_free_gendisk(h, drv_index);
+ cciss_free_drive_info(h, drv_index);
printk(KERN_WARNING "cciss:%d could not update "
"disk %d\n", h->ctlr, drv_index);
--h->num_luns;
@@ -1955,28 +1967,64 @@ mem_msg:
}
/* This function will find the first index of the controllers drive array
- * that has a -1 for the raid_level and will return that index. This is
- * where new drives will be added. If the index to be returned is greater
- * than the highest_lun index for the controller then highest_lun is set
- * to this new index. If there are no available indexes then -1 is returned.
- * "controller_node" is used to know if this is a real logical drive, or just
- * the controller node, which determines if this counts towards highest_lun.
+ * that has a null drv pointer and allocate the drive info struct and
+ * will return that index This is where new drives will be added.
+ * If the index to be returned is greater than the highest_lun index for
+ * the controller then highest_lun is set * to this new index.
+ * If there are no available indexes or if tha allocation fails, then -1
+ * is returned. * "controller_node" is used to know if this is a real
+ * logical drive, or just the controller node, which determines if this
+ * counts towards highest_lun.
*/
-static int cciss_find_free_drive_index(int ctlr, int controller_node)
+static int cciss_alloc_drive_info(ctlr_info_t *h, int controller_node)
{
int i;
+ drive_info_struct *drv;
+ /* Search for an empty slot for our drive info */
for (i = 0; i < CISS_MAX_LUN; i++) {
- if (hba[ctlr]->drv[i].raid_level == -1) {
- if (i > hba[ctlr]->highest_lun)
- if (!controller_node)
- hba[ctlr]->highest_lun = i;
+
+ /* if not cxd0 case, and it's occupied, skip it. */
+ if (h->drv[i] && i != 0)
+ continue;
+ /*
+ * If it's cxd0 case, and drv is alloc'ed already, and a
+ * disk is configured there, skip it.
+ */
+ if (i == 0 && h->drv[i] && h->drv[i]->raid_level != -1)
+ continue;
+
+ /*
+ * We've found an empty slot. Update highest_lun
+ * provided this isn't just the fake cxd0 controller node.
+ */
+ if (i > h->highest_lun && !controller_node)
+ h->highest_lun = i;
+
+ /* If adding a real disk at cxd0, and it's already alloc'ed */
+ if (i == 0 && h->drv[i] != NULL)
return i;
- }
+
+ /*
+ * Found an empty slot, not already alloc'ed. Allocate it.
+ * Mark it with raid_level == -1, so we know it's new later on.
+ */
+ drv = kzalloc(sizeof(*drv), GFP_KERNEL);
+ if (!drv)
+ return -1;
+ drv->raid_level = -1; /* so we know it's new */
+ h->drv[i] = drv;
+ return i;
}
return -1;
}
+static void cciss_free_drive_info(ctlr_info_t *h, int drv_index)
+{
+ kfree(h->drv[drv_index]);
+ h->drv[drv_index] = NULL;
+}
+
static void cciss_free_gendisk(ctlr_info_t *h, int drv_index)
{
put_disk(h->gendisk[drv_index]);
@@ -1997,7 +2045,7 @@ static int cciss_add_gendisk(ctlr_info_t *h, unsigned char lunid[],
{
int drv_index;
- drv_index = cciss_find_free_drive_index(h->ctlr, controller_node);
+ drv_index = cciss_alloc_drive_info(h, controller_node);
if (drv_index == -1)
return -1;
@@ -2009,24 +2057,24 @@ static int cciss_add_gendisk(ctlr_info_t *h, unsigned char lunid[],
printk(KERN_ERR "cciss%d: could not "
"allocate a new disk %d\n",
h->ctlr, drv_index);
- return -1;
+ goto err_free_drive_info;
}
}
- memcpy(h->drv[drv_index].LunID, lunid,
- sizeof(h->drv[drv_index].LunID));
- if (h->drv[drv_index].dev == NULL) {
- if (cciss_create_ld_sysfs_entry(h, drv_index))
- goto err_free_disk;
- }
+ memcpy(h->drv[drv_index]->LunID, lunid,
+ sizeof(h->drv[drv_index]->LunID));
+ if (cciss_create_ld_sysfs_entry(h, drv_index))
+ goto err_free_disk;
/* Don't need to mark this busy because nobody */
/* else knows about this disk yet to contend */
/* for access to it. */
- h->drv[drv_index].busy_configuring = 0;
+ h->drv[drv_index]->busy_configuring = 0;
wmb();
return drv_index;
err_free_disk:
cciss_free_gendisk(h, drv_index);
+err_free_drive_info:
+ cciss_free_drive_info(h, drv_index);
return -1;
}
@@ -2046,17 +2094,18 @@ static void cciss_add_controller_node(ctlr_info_t *h)
drv_index = cciss_add_gendisk(h, CTLR_LUNID, 1);
if (drv_index == -1)
goto error;
- h->drv[drv_index].block_size = 512;
- h->drv[drv_index].nr_blocks = 0;
- h->drv[drv_index].heads = 0;
- h->drv[drv_index].sectors = 0;
- h->drv[drv_index].cylinders = 0;
- h->drv[drv_index].raid_level = -1;
- memset(h->drv[drv_index].serial_no, 0, 16);
+ h->drv[drv_index]->block_size = 512;
+ h->drv[drv_index]->nr_blocks = 0;
+ h->drv[drv_index]->heads = 0;
+ h->drv[drv_index]->sectors = 0;
+ h->drv[drv_index]->cylinders = 0;
+ h->drv[drv_index]->raid_level = -1;
+ memset(h->drv[drv_index]->serial_no, 0, 16);
disk = h->gendisk[drv_index];
if (cciss_add_disk(h, disk, drv_index) == 0)
return;
cciss_free_gendisk(h, drv_index);
+ cciss_free_drive_info(h, drv_index);
error:
printk(KERN_WARNING "cciss%d: could not "
"add disk 0.\n", h->ctlr);
@@ -2135,12 +2184,12 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time,
drv_found = 0;
/* skip holes in the array from already deleted drives */
- if (h->drv[i].raid_level == -1)
+ if (h->drv[i] == NULL)
continue;
for (j = 0; j < num_luns; j++) {
memcpy(lunid, &ld_buff->LUN[j][0], sizeof(lunid));
- if (memcmp(h->drv[i].LunID, lunid,
+ if (memcmp(h->drv[i]->LunID, lunid,
sizeof(lunid)) == 0) {
drv_found = 1;
break;
@@ -2149,10 +2198,11 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time,
if (!drv_found) {
/* Deregister it from the OS, it's gone. */
spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
- h->drv[i].busy_configuring = 1;
+ h->drv[i]->busy_configuring = 1;
spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
return_code = deregister_disk(h, i, 1, via_ioctl);
- h->drv[i].busy_configuring = 0;
+ if (h->drv[i] != NULL)
+ h->drv[i]->busy_configuring = 0;
}
}
@@ -2173,9 +2223,9 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time,
* the first free index and add it.
*/
for (j = 0; j <= h->highest_lun; j++) {
- if (h->drv[j].raid_level != -1 &&
- memcmp(h->drv[j].LunID, lunid,
- sizeof(h->drv[j].LunID)) == 0) {
+ if (h->drv[j] != NULL &&
+ memcmp(h->drv[j]->LunID, lunid,
+ sizeof(h->drv[j]->LunID)) == 0) {
drv_index = j;
drv_found = 1;
break;
@@ -2252,11 +2302,12 @@ static int deregister_disk(ctlr_info_t *h, int drv_index,
int i;
struct gendisk *disk;
drive_info_struct *drv;
+ int recalculate_highest_lun;
if (!capable(CAP_SYS_RAWIO))
return -EPERM;
- drv = &h->drv[drv_index];
+ drv = h->drv[drv_index];
disk = h->gendisk[drv_index];
/* make sure logical volume is NOT is use */
@@ -2266,6 +2317,8 @@ static int deregister_disk(ctlr_info_t *h, int drv_index,
} else if (drv->usage_count > 0)
return -EBUSY;
+ recalculate_highest_lun = (drv == h->drv[h->highest_lun]);
+
/* invalidate the devices and deregister the disk. If it is disk
* zero do not deregister it but just zero out it's values. This
* allows us to delete disk zero but keep the controller registered.
@@ -2276,14 +2329,8 @@ static int deregister_disk(ctlr_info_t *h, int drv_index,
cciss_destroy_ld_sysfs_entry(h, drv_index, 0);
del_gendisk(disk);
}
- if (q) {
+ if (q)
blk_cleanup_queue(q);
- /* Set drv->queue to NULL so that we do not try
- * to call blk_start_queue on this queue in the
- * interrupt handler
- */
- drv->queue = NULL;
- }
/* If clear_all is set then we are deleting the logical
* drive, not just refreshing its info. For drives
* other than disk 0 we will call put_disk. We do not
@@ -2306,24 +2353,20 @@ static int deregister_disk(ctlr_info_t *h, int drv_index,
}
} else {
set_capacity(disk, 0);
+ cciss_clear_drive_info(drv);
}
--h->num_luns;
- cciss_clear_drive_info(drv);
-
- if (clear_all) {
- /* check to see if it was the last disk */
- if (drv == h->drv + h->highest_lun) {
- /* if so, find the new hightest lun */
- int i, newhighest = -1;
- for (i = 0; i <= h->highest_lun; i++) {
- /* if the disk has size > 0, it is available */
- if (h->drv[i].heads)
- newhighest = i;
- }
- h->highest_lun = newhighest;
+
+ /* if it was the last disk, find the new hightest lun */
+ if (clear_all && recalculate_highest_lun) {
+ int i, newhighest = -1;
+ for (i = 0; i <= h->highest_lun; i++) {
+ /* if the disk has size > 0, it is available */
+ if (h->drv[i] && h->drv[i]->heads)
+ newhighest = i;
}
- memset(drv->LunID, 0, sizeof(drv->LunID));
+ h->highest_lun = newhighest;
}
return 0;
}
@@ -2754,7 +2797,7 @@ static int cciss_revalidate(struct gendisk *disk)
InquiryData_struct *inq_buff = NULL;
for (logvol = 0; logvol < CISS_MAX_LUN; logvol++) {
- if (memcmp(h->drv[logvol].LunID, drv->LunID,
+ if (memcmp(h->drv[logvol]->LunID, drv->LunID,
sizeof(drv->LunID)) == 0) {
FOUND = 1;
break;
@@ -4292,8 +4335,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;
}
@@ -4348,12 +4390,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
diff --git a/drivers/block/cciss.h b/drivers/block/cciss.h
index 5188f71..31524cf 100644
--- a/drivers/block/cciss.h
+++ b/drivers/block/cciss.h
@@ -45,13 +45,14 @@ typedef struct _drive_info_struct
* to prevent it from being opened or it's
* queue from being started.
*/
- struct device *dev;
+ struct device dev;
__u8 serial_no[16]; /* from inquiry page 0x83,
* not necc. null terminated.
*/
char vendor[VENDOR_LEN + 1]; /* SCSI vendor string */
char model[MODEL_LEN + 1]; /* SCSI model string */
char rev[REV_LEN + 1]; /* SCSI revision string */
+ char device_initialized; /* indicates whether dev is initialized */
} drive_info_struct;
struct ctlr_info
@@ -87,7 +88,7 @@ struct ctlr_info
BYTE cciss_read_capacity;
// information about each logical volume
- drive_info_struct drv[CISS_MAX_LUN];
+ drive_info_struct *drv[CISS_MAX_LUN];
struct access_method access;
On Thu, Sep 17 2009, Stephen M. Cameron wrote:
> The following series cleans up the broken scan_thread code,
> allows triggering of the scan_thread via sysfs, dynamically
> allocates the per device structure for each logical drive as needed
> instead of all at once at driver init time, fixes a bug in
> logical drive addressing (seen on MSA2012), and adds lunid,
> raid_level, and usage_count attributes in sysfs for each
> logical drive.
Thanks Stephen, I've queued it up. I'll look over it and comment if I
find something problematic.
--
Jens Axboe