|
@@ -40,6 +40,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>
|
|
|
|
|
@@ -156,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);
|
|
@@ -3233,20 +3238,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;
|
|
|
}
|
|
|
|
|
@@ -3269,8 +3375,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:
|
|
@@ -3919,6 +4025,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;
|
|
@@ -3927,6 +4034,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;
|
|
|
|
|
@@ -4036,14 +4145,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:
|
|
@@ -4126,8 +4229,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);
|
|
|
|
|
@@ -4174,6 +4278,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);
|
|
|
}
|
|
|
|
|
@@ -4206,15 +4311,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;
|
|
|
}
|
|
|
|
|
@@ -4231,6 +4346,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);
|
|
|
}
|