Commit 011771ec authored by Stephen M. Cameron's avatar Stephen M. Cameron Committed by james toy

Add thread to allow controllers to register for rescan for new devices

(borrowed code from cciss.)
Signed-off-by: default avatarStephen M. Cameron <scameron@beardog.cce.hp.com>
Cc: James Bottomley <James.Bottomley@HansenPartnership.com>
Cc: Jens Axboe <jens.axboe@oracle.com>
Cc: Mike Miller <mikem@beardog.cce.hp.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
parent b76cd2f3
......@@ -47,6 +47,7 @@
#include <linux/string.h>
#include <linux/bitmap.h>
#include <asm/atomic.h>
#include <linux/kthread.h>
#include "hpsa_cmd.h"
#include "hpsa.h"
......@@ -145,6 +146,7 @@ static ssize_t lunid_show(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t unique_id_show(struct device *dev,
struct device_attribute *attr, char *buf);
static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno);
DEVICE_ATTR(raid_level, S_IRUGO, raid_level_show, NULL);
DEVICE_ATTR(lunid, S_IRUGO, lunid_show, NULL);
......@@ -183,6 +185,129 @@ static inline struct ctlr_info *sdev_to_hba(struct scsi_device *sdev)
return (struct ctlr_info *) *priv;
}
static struct task_struct *hpsa_scan_thread;
static DEFINE_MUTEX(scan_mutex);
static LIST_HEAD(scan_q);
static int scan_thread(void *data);
/**
* 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. The controller
* can be in one of three states:
* 1. Doesn't need a scan
* 2. On the scan list, but not scanning yet (we remove it)
* 3. Busy scanning (and not on the list). In this case we want to wait for
* the scan to complete to make sure the scanning thread for this
* controller is completely idle.
**/
static void remove_from_scan_list(struct ctlr_info *h)
{
struct ctlr_info *test_h, *tmp_h;
mutex_lock(&scan_mutex);
list_for_each_entry_safe(test_h, tmp_h, &scan_q, scan_list) {
if (test_h == h) { /* state 2. */
list_del(&h->scan_list);
complete_all(&h->scan_wait);
mutex_unlock(&scan_mutex);
return;
}
}
if (h->busy_scanning) { /* state 3. */
mutex_unlock(&scan_mutex);
wait_for_completion(&h->scan_wait);
} else { /* state 1, nothing to do. */
mutex_unlock(&scan_mutex);
}
}
/* 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(__attribute__((unused)) void *data)
{
struct ctlr_info *h;
int host_no;
while (1) {
set_current_state(TASK_INTERRUPTIBLE);
schedule();
if (kthread_should_stop())
break;
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);
host_no = h->scsi_host ? h->scsi_host->host_no : -1;
hpsa_update_scsi_devices(h, host_no);
complete_all(&h->scan_wait);
mutex_lock(&scan_mutex);
h->busy_scanning = 0;
mutex_unlock(&scan_mutex);
}
}
return 0;
}
/* Enqueuing and dequeuing functions for cmdlists. */
static inline void addQ(struct hlist_head *list, struct CommandList *c)
{
......@@ -3269,8 +3394,11 @@ static int __devinit hpsa_init_one(struct pci_dev *pdev,
if (!h)
return -1;
h->busy_initializing = 1;
INIT_HLIST_HEAD(&h->cmpQ);
INIT_HLIST_HEAD(&h->reqQ);
mutex_init(&h->busy_shutting_down);
init_completion(&h->scan_wait);
if (hpsa_pci_init(h, pdev) != 0)
goto clean1;
......@@ -3330,7 +3458,7 @@ static int __devinit hpsa_init_one(struct pci_dev *pdev,
h->access.set_intr_mask(h, HPSA_INTR_ON);
hpsa_register_scsi(h); /* hook ourselves into SCSI subsystem */
h->busy_initializing = 0;
return 1;
clean4:
......@@ -3347,6 +3475,7 @@ clean4:
free_irq(h->intr[SIMPLE_MODE_INT], h);
clean2:
clean1:
h->busy_initializing = 0;
kfree(h);
return -1;
}
......@@ -3387,6 +3516,8 @@ static void __devexit hpsa_remove_one(struct pci_dev *pdev)
return;
}
h = pci_get_drvdata(pdev);
mutex_lock(&h->busy_shutting_down);
remove_from_scan_list(h);
hpsa_unregister_scsi(h); /* unhook from SCSI subsystem */
hpsa_shutdown(pdev);
iounmap(h->vaddr);
......@@ -3403,6 +3534,7 @@ static void __devexit hpsa_remove_one(struct pci_dev *pdev)
*/
pci_release_regions(pdev);
pci_set_drvdata(pdev, NULL);
mutex_unlock(&h->busy_shutting_down);
kfree(h);
}
......@@ -3433,12 +3565,23 @@ static struct pci_driver hpsa_pci_driver = {
*/
static int __init hpsa_init(void)
{
return pci_register_driver(&hpsa_pci_driver);
int err;
/* Start the scan thread */
hpsa_scan_thread = kthread_run(scan_thread, NULL, "hpsa_scan");
if (IS_ERR(hpsa_scan_thread)) {
err = PTR_ERR(hpsa_scan_thread);
return -ENODEV;
}
err = pci_register_driver(&hpsa_pci_driver);
if (err)
kthread_stop(hpsa_scan_thread);
return err;
}
static void __exit hpsa_cleanup(void)
{
pci_unregister_driver(&hpsa_pci_driver);
kthread_stop(hpsa_scan_thread);
}
module_init(hpsa_init);
......
......@@ -91,6 +91,11 @@ struct ctlr_info {
unsigned long *cmd_pool_bits;
int nr_allocs;
int nr_frees;
int busy_initializing;
int busy_scanning;
struct mutex busy_shutting_down;
struct list_head scan_list;
struct completion scan_wait;
struct Scsi_Host *scsi_host;
spinlock_t devlock; /* to protect hba[ctlr]->dev[]; */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment