diff options
author | Andrew Patterson <andrew.patterson@hp.com> | 2009-09-17 14:46:58 -0400 |
---|---|---|
committer | Jens Axboe <jens.axboe@oracle.com> | 2009-10-01 15:15:41 -0400 |
commit | b368c9dd65984d1860b97bff77644c0e3e46df96 (patch) | |
tree | 003e31b2bb29eff630c3c156e08f4eaea7d7b6e2 /drivers/block | |
parent | c64bebcd7f33a6260b6d4c9999f797a633a3fa1c (diff) |
cciss: Use one scan thread per controller and fix hang during rmmod
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 <andrew.patterson@hp.com>
Signed-off-by: Stephen M. Cameron <scameron@beardog.cce.hp.com>
Acked-by: Mike Miller <mike.miller@hp.com>
Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
Diffstat (limited to 'drivers/block')
-rw-r--r-- | drivers/block/cciss.c | 156 | ||||
-rw-r--r-- | 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 f162f96c36e6..4fb63b898798 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c | |||
@@ -40,6 +40,7 @@ | |||
40 | #include <linux/hdreg.h> | 40 | #include <linux/hdreg.h> |
41 | #include <linux/spinlock.h> | 41 | #include <linux/spinlock.h> |
42 | #include <linux/compat.h> | 42 | #include <linux/compat.h> |
43 | #include <linux/mutex.h> | ||
43 | #include <asm/uaccess.h> | 44 | #include <asm/uaccess.h> |
44 | #include <asm/io.h> | 45 | #include <asm/io.h> |
45 | 46 | ||
@@ -156,6 +157,10 @@ static struct board_type products[] = { | |||
156 | 157 | ||
157 | static ctlr_info_t *hba[MAX_CTLR]; | 158 | static ctlr_info_t *hba[MAX_CTLR]; |
158 | 159 | ||
160 | static struct task_struct *cciss_scan_thread; | ||
161 | static DEFINE_MUTEX(scan_mutex); | ||
162 | static LIST_HEAD(scan_q); | ||
163 | |||
159 | static void do_cciss_request(struct request_queue *q); | 164 | static void do_cciss_request(struct request_queue *q); |
160 | static irqreturn_t do_cciss_intr(int irq, void *dev_id); | 165 | static irqreturn_t do_cciss_intr(int irq, void *dev_id); |
161 | static int cciss_open(struct block_device *bdev, fmode_t mode); | 166 | 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) | |||
3233 | return IRQ_HANDLED; | 3238 | return IRQ_HANDLED; |
3234 | } | 3239 | } |
3235 | 3240 | ||
3241 | /** | ||
3242 | * add_to_scan_list() - add controller to rescan queue | ||
3243 | * @h: Pointer to the controller. | ||
3244 | * | ||
3245 | * Adds the controller to the rescan queue if not already on the queue. | ||
3246 | * | ||
3247 | * returns 1 if added to the queue, 0 if skipped (could be on the | ||
3248 | * queue already, or the controller could be initializing or shutting | ||
3249 | * down). | ||
3250 | **/ | ||
3251 | static int add_to_scan_list(struct ctlr_info *h) | ||
3252 | { | ||
3253 | struct ctlr_info *test_h; | ||
3254 | int found = 0; | ||
3255 | int ret = 0; | ||
3256 | |||
3257 | if (h->busy_initializing) | ||
3258 | return 0; | ||
3259 | |||
3260 | if (!mutex_trylock(&h->busy_shutting_down)) | ||
3261 | return 0; | ||
3262 | |||
3263 | mutex_lock(&scan_mutex); | ||
3264 | list_for_each_entry(test_h, &scan_q, scan_list) { | ||
3265 | if (test_h == h) { | ||
3266 | found = 1; | ||
3267 | break; | ||
3268 | } | ||
3269 | } | ||
3270 | if (!found && !h->busy_scanning) { | ||
3271 | INIT_COMPLETION(h->scan_wait); | ||
3272 | list_add_tail(&h->scan_list, &scan_q); | ||
3273 | ret = 1; | ||
3274 | } | ||
3275 | mutex_unlock(&scan_mutex); | ||
3276 | mutex_unlock(&h->busy_shutting_down); | ||
3277 | |||
3278 | return ret; | ||
3279 | } | ||
3280 | |||
3281 | /** | ||
3282 | * remove_from_scan_list() - remove controller from rescan queue | ||
3283 | * @h: Pointer to the controller. | ||
3284 | * | ||
3285 | * Removes the controller from the rescan queue if present. Blocks if | ||
3286 | * the controller is currently conducting a rescan. | ||
3287 | **/ | ||
3288 | static void remove_from_scan_list(struct ctlr_info *h) | ||
3289 | { | ||
3290 | struct ctlr_info *test_h, *tmp_h; | ||
3291 | int scanning = 0; | ||
3292 | |||
3293 | mutex_lock(&scan_mutex); | ||
3294 | list_for_each_entry_safe(test_h, tmp_h, &scan_q, scan_list) { | ||
3295 | if (test_h == h) { | ||
3296 | list_del(&h->scan_list); | ||
3297 | complete_all(&h->scan_wait); | ||
3298 | mutex_unlock(&scan_mutex); | ||
3299 | return; | ||
3300 | } | ||
3301 | } | ||
3302 | if (&h->busy_scanning) | ||
3303 | scanning = 0; | ||
3304 | mutex_unlock(&scan_mutex); | ||
3305 | |||
3306 | if (scanning) | ||
3307 | wait_for_completion(&h->scan_wait); | ||
3308 | } | ||
3309 | |||
3310 | /** | ||
3311 | * scan_thread() - kernel thread used to rescan controllers | ||
3312 | * @data: Ignored. | ||
3313 | * | ||
3314 | * A kernel thread used scan for drive topology changes on | ||
3315 | * controllers. The thread processes only one controller at a time | ||
3316 | * using a queue. Controllers are added to the queue using | ||
3317 | * add_to_scan_list() and removed from the queue either after done | ||
3318 | * processing or using remove_from_scan_list(). | ||
3319 | * | ||
3320 | * returns 0. | ||
3321 | **/ | ||
3236 | static int scan_thread(void *data) | 3322 | static int scan_thread(void *data) |
3237 | { | 3323 | { |
3238 | ctlr_info_t *h = data; | 3324 | struct ctlr_info *h; |
3239 | int rc; | ||
3240 | DECLARE_COMPLETION_ONSTACK(wait); | ||
3241 | h->rescan_wait = &wait; | ||
3242 | 3325 | ||
3243 | for (;;) { | 3326 | while (1) { |
3244 | rc = wait_for_completion_interruptible(&wait); | 3327 | set_current_state(TASK_INTERRUPTIBLE); |
3328 | schedule(); | ||
3245 | if (kthread_should_stop()) | 3329 | if (kthread_should_stop()) |
3246 | break; | 3330 | break; |
3247 | if (!rc) | 3331 | |
3248 | rebuild_lun_table(h, 0); | 3332 | while (1) { |
3333 | mutex_lock(&scan_mutex); | ||
3334 | if (list_empty(&scan_q)) { | ||
3335 | mutex_unlock(&scan_mutex); | ||
3336 | break; | ||
3337 | } | ||
3338 | |||
3339 | h = list_entry(scan_q.next, | ||
3340 | struct ctlr_info, | ||
3341 | scan_list); | ||
3342 | list_del(&h->scan_list); | ||
3343 | h->busy_scanning = 1; | ||
3344 | mutex_unlock(&scan_mutex); | ||
3345 | |||
3346 | if (h) { | ||
3347 | rebuild_lun_table(h, 0); | ||
3348 | complete_all(&h->scan_wait); | ||
3349 | mutex_lock(&scan_mutex); | ||
3350 | h->busy_scanning = 0; | ||
3351 | mutex_unlock(&scan_mutex); | ||
3352 | } | ||
3353 | } | ||
3249 | } | 3354 | } |
3355 | |||
3250 | return 0; | 3356 | return 0; |
3251 | } | 3357 | } |
3252 | 3358 | ||
@@ -3269,8 +3375,8 @@ static int check_for_unit_attention(ctlr_info_t *h, CommandList_struct *c) | |||
3269 | case REPORT_LUNS_CHANGED: | 3375 | case REPORT_LUNS_CHANGED: |
3270 | printk(KERN_WARNING "cciss%d: report LUN data " | 3376 | printk(KERN_WARNING "cciss%d: report LUN data " |
3271 | "changed\n", h->ctlr); | 3377 | "changed\n", h->ctlr); |
3272 | if (h->rescan_wait) | 3378 | add_to_scan_list(h); |
3273 | complete(h->rescan_wait); | 3379 | wake_up_process(cciss_scan_thread); |
3274 | return 1; | 3380 | return 1; |
3275 | break; | 3381 | break; |
3276 | case POWER_OR_RESET: | 3382 | case POWER_OR_RESET: |
@@ -3919,6 +4025,7 @@ static int __devinit cciss_init_one(struct pci_dev *pdev, | |||
3919 | hba[i]->busy_initializing = 1; | 4025 | hba[i]->busy_initializing = 1; |
3920 | INIT_HLIST_HEAD(&hba[i]->cmpQ); | 4026 | INIT_HLIST_HEAD(&hba[i]->cmpQ); |
3921 | INIT_HLIST_HEAD(&hba[i]->reqQ); | 4027 | INIT_HLIST_HEAD(&hba[i]->reqQ); |
4028 | mutex_init(&hba[i]->busy_shutting_down); | ||
3922 | 4029 | ||
3923 | if (cciss_pci_init(hba[i], pdev) != 0) | 4030 | if (cciss_pci_init(hba[i], pdev) != 0) |
3924 | goto clean0; | 4031 | goto clean0; |
@@ -3927,6 +4034,8 @@ static int __devinit cciss_init_one(struct pci_dev *pdev, | |||
3927 | hba[i]->ctlr = i; | 4034 | hba[i]->ctlr = i; |
3928 | hba[i]->pdev = pdev; | 4035 | hba[i]->pdev = pdev; |
3929 | 4036 | ||
4037 | init_completion(&hba[i]->scan_wait); | ||
4038 | |||
3930 | if (cciss_create_hba_sysfs_entry(hba[i])) | 4039 | if (cciss_create_hba_sysfs_entry(hba[i])) |
3931 | goto clean0; | 4040 | goto clean0; |
3932 | 4041 | ||
@@ -4036,14 +4145,8 @@ static int __devinit cciss_init_one(struct pci_dev *pdev, | |||
4036 | 4145 | ||
4037 | hba[i]->cciss_max_sectors = 2048; | 4146 | hba[i]->cciss_max_sectors = 2048; |
4038 | 4147 | ||
4039 | hba[i]->busy_initializing = 0; | ||
4040 | |||
4041 | rebuild_lun_table(hba[i], 1); | 4148 | rebuild_lun_table(hba[i], 1); |
4042 | hba[i]->cciss_scan_thread = kthread_run(scan_thread, hba[i], | 4149 | hba[i]->busy_initializing = 0; |
4043 | "cciss_scan%02d", i); | ||
4044 | if (IS_ERR(hba[i]->cciss_scan_thread)) | ||
4045 | return PTR_ERR(hba[i]->cciss_scan_thread); | ||
4046 | |||
4047 | return 1; | 4150 | return 1; |
4048 | 4151 | ||
4049 | clean4: | 4152 | clean4: |
@@ -4126,8 +4229,9 @@ static void __devexit cciss_remove_one(struct pci_dev *pdev) | |||
4126 | return; | 4229 | return; |
4127 | } | 4230 | } |
4128 | 4231 | ||
4129 | kthread_stop(hba[i]->cciss_scan_thread); | 4232 | mutex_lock(&hba[i]->busy_shutting_down); |
4130 | 4233 | ||
4234 | remove_from_scan_list(hba[i]); | ||
4131 | remove_proc_entry(hba[i]->devname, proc_cciss); | 4235 | remove_proc_entry(hba[i]->devname, proc_cciss); |
4132 | unregister_blkdev(hba[i]->major, hba[i]->devname); | 4236 | unregister_blkdev(hba[i]->major, hba[i]->devname); |
4133 | 4237 | ||
@@ -4174,6 +4278,7 @@ static void __devexit cciss_remove_one(struct pci_dev *pdev) | |||
4174 | pci_release_regions(pdev); | 4278 | pci_release_regions(pdev); |
4175 | pci_set_drvdata(pdev, NULL); | 4279 | pci_set_drvdata(pdev, NULL); |
4176 | cciss_destroy_hba_sysfs_entry(hba[i]); | 4280 | cciss_destroy_hba_sysfs_entry(hba[i]); |
4281 | mutex_unlock(&hba[i]->busy_shutting_down); | ||
4177 | free_hba(i); | 4282 | free_hba(i); |
4178 | } | 4283 | } |
4179 | 4284 | ||
@@ -4206,15 +4311,25 @@ static int __init cciss_init(void) | |||
4206 | if (err) | 4311 | if (err) |
4207 | return err; | 4312 | return err; |
4208 | 4313 | ||
4314 | /* Start the scan thread */ | ||
4315 | cciss_scan_thread = kthread_run(scan_thread, NULL, "cciss_scan"); | ||
4316 | if (IS_ERR(cciss_scan_thread)) { | ||
4317 | err = PTR_ERR(cciss_scan_thread); | ||
4318 | goto err_bus_unregister; | ||
4319 | } | ||
4320 | |||
4209 | /* Register for our PCI devices */ | 4321 | /* Register for our PCI devices */ |
4210 | err = pci_register_driver(&cciss_pci_driver); | 4322 | err = pci_register_driver(&cciss_pci_driver); |
4211 | if (err) | 4323 | if (err) |
4212 | goto err_bus_register; | 4324 | goto err_thread_stop; |
4213 | 4325 | ||
4214 | return 0; | 4326 | return 0; |
4215 | 4327 | ||
4216 | err_bus_register: | 4328 | err_thread_stop: |
4329 | kthread_stop(cciss_scan_thread); | ||
4330 | err_bus_unregister: | ||
4217 | bus_unregister(&cciss_bus_type); | 4331 | bus_unregister(&cciss_bus_type); |
4332 | |||
4218 | return err; | 4333 | return err; |
4219 | } | 4334 | } |
4220 | 4335 | ||
@@ -4231,6 +4346,7 @@ static void __exit cciss_cleanup(void) | |||
4231 | cciss_remove_one(hba[i]->pdev); | 4346 | cciss_remove_one(hba[i]->pdev); |
4232 | } | 4347 | } |
4233 | } | 4348 | } |
4349 | kthread_stop(cciss_scan_thread); | ||
4234 | remove_proc_entry("driver/cciss", NULL); | 4350 | remove_proc_entry("driver/cciss", NULL); |
4235 | bus_unregister(&cciss_bus_type); | 4351 | bus_unregister(&cciss_bus_type); |
4236 | } | 4352 | } |
diff --git a/drivers/block/cciss.h b/drivers/block/cciss.h index 06a5db25b298..4fb3639b6cff 100644 --- a/drivers/block/cciss.h +++ b/drivers/block/cciss.h | |||
@@ -2,6 +2,7 @@ | |||
2 | #define CCISS_H | 2 | #define CCISS_H |
3 | 3 | ||
4 | #include <linux/genhd.h> | 4 | #include <linux/genhd.h> |
5 | #include <linux/mutex.h> | ||
5 | 6 | ||
6 | #include "cciss_cmd.h" | 7 | #include "cciss_cmd.h" |
7 | 8 | ||
@@ -108,6 +109,8 @@ struct ctlr_info | |||
108 | int nr_frees; | 109 | int nr_frees; |
109 | int busy_configuring; | 110 | int busy_configuring; |
110 | int busy_initializing; | 111 | int busy_initializing; |
112 | int busy_scanning; | ||
113 | struct mutex busy_shutting_down; | ||
111 | 114 | ||
112 | /* This element holds the zero based queue number of the last | 115 | /* This element holds the zero based queue number of the last |
113 | * queue to be started. It is used for fairness. | 116 | * queue to be started. It is used for fairness. |
@@ -122,8 +125,8 @@ struct ctlr_info | |||
122 | /* and saved for later processing */ | 125 | /* and saved for later processing */ |
123 | #endif | 126 | #endif |
124 | unsigned char alive; | 127 | unsigned char alive; |
125 | struct completion *rescan_wait; | 128 | struct list_head scan_list; |
126 | struct task_struct *cciss_scan_thread; | 129 | struct completion scan_wait; |
127 | struct device dev; | 130 | struct device dev; |
128 | }; | 131 | }; |
129 | 132 | ||