cciss rmmod/scan-thread fixes
Subsystems (2)
cover cciss rmmod/scan-thread fixes
The following series fixes several problems causing hangs while rmmoding the cciss driver. In the process of fixing these hangs, I also reworked the the logical drive scanning kernel thread to use one thread per driver rather than one thread per controller. I also added a sysfs attribute to kick off a controller scan.
.../ABI/testing/sysfs-bus-pci-devices-cciss | 7 + drivers/block/cciss.c | 198 ++++++++++++++++++-- drivers/block/cciss.h | 7 + 3 files changed, 188 insertions(+), 24 deletions(-)
Andrew Patterson (3):
cciss: remove logical drive sysfs entries during driver cleanup.
cciss: use only one scan thread
cciss: kick off logical drive topology rescan through sysfs
Signature · 1 line
Andrew Patterson
PATCH 1/3 cciss: remove logical drive sysfs entries during driver cleanup.
cciss: remove logical drive sysfs entries during driver cleanup.
Sysfs entries for logical drives need to be removed when a drive is deleted during driver cleanup.
---
drivers/block/cciss.c +4 -1
@@ -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
PATCH 2/3 cciss: use only one scan thread
In reply to Andrew Pattersoncciss: use only one scan thread
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.
---
drivers/block/cciss.c +140 -18
@@ -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 struct mutex scan_mutex; +static struct 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);
@@ -189,6 +194,8 @@ 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 void remove_from_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);
@@ -3232,20 +3239,121 @@ static irqreturn_t do_cciss_intr(int irq, void *dev_id)return IRQ_HANDLED ; } -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) { -ctlr_info_t *h = data; -int rc; -DECLARE_COMPLETION_ONSTACK (wait); - h->rescan_wait = &wait; +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 ; -for (;;) { - rc =wait_for_completion_interruptible (&wait); -if (kthread_should_stop ()) +mutex_lock (&scan_mutex); +list_for_each_entry (test_h, &scan_q, scan_list) { +if (test_h == h) { + found =1 ;break ; -if (!rc) + } + } +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) +{ +struct ctlr_info *h; + +set_current_state (TASK_INTERRUPTIBLE ); +while (!kthread_should_stop ()) { +mutex_lock (&scan_mutex); +if (list_empty (&scan_q)) { + h =NULL ; + }else { + 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) { +__set_current_state (TASK_RUNNING );rebuild_lun_table (h,0 ); +complete_all (&h->scan_wait); +mutex_lock (&scan_mutex); + h->busy_scanning =0 ; +mutex_unlock (&scan_mutex); +set_current_state (TASK_INTERRUPTIBLE ); + }else { +schedule (); +set_current_state (TASK_INTERRUPTIBLE ); +continue ; + } } + +__set_current_state (TASK_RUNNING );return 0 ; }
@@ -3268,8 +3376,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 +4026,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 +4035,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;
@@ -4037,10 +4148,6 @@ static int __devinit cciss_init_one(struct pci_dev *pdev, hba[i]->busy_initializing =0 ;rebuild_lun_table (hba[i],1 ); - hba[i]->cciss_scan_thread =kthread_run (scan_thread, hba[i], -"cciss_scan%02d" , i); -if (IS_ERR (hba[i]->cciss_scan_thread)) -return PTR_ERR (hba[i]->cciss_scan_thread);return 1 ;
@@ -4125,8 +4232,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 +4281,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 +4314,27 @@ static int __init cciss_init(void)if (err)return err; +/* Start the scan thread */ +mutex_init (&scan_mutex); +INIT_LIST_HEAD (&scan_q); + 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 +4351,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); }
drivers/block/cciss.h +5 -2
@@ -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_infoint 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; };
Re: [PATCH 2/3] cciss: use only one scan thread
Quoted 21 lines · On Tue, 14 Jul 2009 16:02:50 -0600 Andrew Patterson <[email protected]>…
cciss: use only one scan thread
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....
drivers/block/cciss.c +1
-0@@ -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>
drivers/block/cciss.c static struct board_type products[] = { · line 161
Quoted 6 lines
drivers/block/cciss.c +3
-0@@ -155,6 +156,10 @@ static struct board_type products[] = {static ctlr_info_t *hba[MAX_CTLR ]; +static struct task_struct *cciss_scan_thread; +static struct mutex scan_mutex; +static struct list_head scan_q;
Both of the above can be initialised at compile-time. This is a bit neater and prevents reviewers from having to run around checking that they got initialised early enough.
drivers/block/cciss.c static int sendcmd_withirq_core(ctlr_info_t *h, CommandList_struct *c, · line 198
Quoted 9 lines
drivers/block/cciss.c +2
-0static 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);@@ -189,6 +194,8 @@ 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 void remove_from_scan_list (struct ctlr_info *h);
These forward declarations are unneeded.
Quoted 20 lines
drivers/block/cciss.c +11
-1static int scan_thread (void *data);static int check_for_unit_attention (ctlr_info_t *h,CommandList_struct *c);@@ -3232,20 +3239,121 @@ static irqreturn_t do_cciss_intr(int irq, void *dev_id)return IRQ_HANDLED ; } -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) {
drivers/block/cciss.c static irqreturn_t do_cciss_intr(int irq, void *dev_id) · line 3259
Quoted 10 lines
drivers/block/cciss.c +6
-4-ctlr_info_t *h = data; -int rc; -DECLARE_COMPLETION_ONSTACK (wait); - h->rescan_wait = &wait; +struct ctlr_info *test_h; +int found =0 ; +int ret =0 ; + +if (h->busy_initializing) +return 0 ;
This looks pretty random and racy.
For example, what stops busy_initializing from becoming true one picosecond after the test?
Quoted 14 lines
drivers/block/cciss.c +8
-4+if (!mutex_trylock (&h->busy_shutting_down)) +return 0 ; -for (;;) { - rc =wait_for_completion_interruptible (&wait); -if (kthread_should_stop ()) +mutex_lock (&scan_mutex); +list_for_each_entry (test_h, &scan_q, scan_list) { +if (test_h == h) { + found =1 ;break ; -if (!rc) + } + }
drivers/block/cciss.c static irqreturn_t do_cciss_intr(int irq, void *dev_id) · line 3280
Quoted 10 lines
drivers/block/cciss.c +10
-0+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; +}
We already did init_completion(h->scan_wait) at startup time? Doing the INIT_COMPLETION() here looks unusual and is hopefully unnecessary.
Quoted 38 lines
drivers/block/cciss.c +37
-0+/** +* 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) +{ +struct ctlr_info *h; + +set_current_state (TASK_INTERRUPTIBLE ); +while (!kthread_should_stop ()) { +mutex_lock (&scan_mutex); +if (list_empty (&scan_q)) { + h =NULL ; + }else { + 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) { +__set_current_state (TASK_RUNNING );rebuild_lun_table (h,0 ); +complete_all (&h->scan_wait); +mutex_lock (&scan_mutex); + h->busy_scanning =0 ; +mutex_unlock (&scan_mutex); +set_current_state (TASK_INTERRUPTIBLE );
drivers/block/cciss.c static irqreturn_t do_cciss_intr(int irq, void *dev_id) · line 3358
Quoted 10 lines
drivers/block/cciss.c +7
-0+ }else { +schedule (); +set_current_state (TASK_INTERRUPTIBLE ); +continue ; + } } + +__set_current_state (TASK_RUNNING );return 0 ; }
This code is somewhat wrong. Look:
set_current_state(TASK_INTERRUPTIBLE); ... mutex_lock(&scan_mutex);
now, we don't know what state the task is in. If the mutex_lock() slept the we're in state TASK_RUNNING. If the mutex_lock() didn't sleep then we're in state <mumble>. Where <mumble>==TASK_INTERRUPTIBLE, but that's an implementation fluke.
So I'd say that some rethinking/restructuring is needed here.
Re: [PATCH 2/3] cciss: use only one scan thread
Quoted 27 lines · Andrew Morton
Quoted 24 lines · On Tue, 14 Jul 2009 16:02:50 -0600 Andrew Patterson <[email protected]>…
cciss: use only one scan thread
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....
drivers/block/cciss.c +1
-0@@ -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 ];
Quoted 7 lines
drivers/block/cciss.c static struct board_type products[] = { · line 161
Quoted 3 lines
drivers/block/cciss.c +3
-0+static struct task_struct *cciss_scan_thread; +static struct mutex scan_mutex; +static struct list_head scan_q;Both of the above can be initialised at compile-time. This is a bit neater and prevents reviewers from having to run around checking that they got initialised early enough.
Fixed.
Quoted 7 lines
Quoted 7 lines
drivers/block/cciss.c +0
-0static 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);@@ -189,6 +194,8 @@ static int sendcmd_withirq_core(ctlr_info_t *h, CommandList_struct *c,static int process_sendcmd_error (ctlr_info_t *h,CommandList_struct *c);
Quoted 6 lines
drivers/block/cciss.c static int sendcmd_withirq_core(ctlr_info_t *h, CommandList_struct *c, · line 198
Quoted 3 lines
drivers/block/cciss.c +2
-0static void fail_all_cmds (unsigned long ctlr); +static int add_to_scan_list (struct ctlr_info *h); +static void remove_from_scan_list (struct ctlr_info *h);These forward declarations are unneeded.
Fixed.
Quoted 24 lines
Quoted 24 lines
drivers/block/cciss.c +11
-5static int scan_thread (void *data);static int check_for_unit_attention (ctlr_info_t *h,CommandList_struct *c);@@ -3232,20 +3239,121 @@ static irqreturn_t do_cciss_intr(int irq, void *dev_id)return IRQ_HANDLED ; } -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) { -ctlr_info_t *h = data; -int rc; -DECLARE_COMPLETION_ONSTACK (wait); - h->rescan_wait = &wait;
Quoted 8 lines
drivers/block/cciss.c static irqreturn_t do_cciss_intr(int irq, void *dev_id) · line 3259
Quoted 6 lines
drivers/block/cciss.c +6
-0+struct ctlr_info *test_h; +int found =0 ; +int ret =0 ; + +if (h->busy_initializing) +return 0 ;This looks pretty random and racy.
Fixed. Converted busy_initializer to a mutex and will use:
if (mutex_is_locked(&h->busy_initializing))
return 0;
Quoted 23 lines
For example, what stops
busy_initializingfrom becoming true one picosecond after the test?Quoted 19 lines
drivers/block/cciss.c +13
-4+if (!mutex_trylock (&h->busy_shutting_down)) +return 0 ; -for (;;) { - rc =wait_for_completion_interruptible (&wait); -if (kthread_should_stop ()) +mutex_lock (&scan_mutex); +list_for_each_entry (test_h, &scan_q, scan_list) { +if (test_h == h) { + found =1 ;break ; -if (!rc) + } + } +if (!found && !h->busy_scanning) { +INIT_COMPLETION (h->scan_wait); +list_add_tail (&h->scan_list, &scan_q); + ret =1 ; + }
Quoted 9 lines
drivers/block/cciss.c static irqreturn_t do_cciss_intr(int irq, void *dev_id) · line 3280
Quoted 5 lines
drivers/block/cciss.c +5
-0+mutex_unlock (&scan_mutex); +mutex_unlock (&h->busy_shutting_down); + +return ret; +}We already did
init_completion(h->scan_wait)at startup time? Doing theINIT_COMPLETION()here looks unusual and is hopefully unnecessary.
I am following the text in the "Linux Device Drivers 3rd Edition section 5.4:
"A completion is normally a one-shot device; it is used once then discarded. It is possible, however, to reuse completion structures if proper care is taken. If complete_all is not used, a completion structure can be reused without any problems as long as there is no ambiguity about what event is being signalled. If you use complete_all, however, you must reinitialize the completion structure before reusing it. The macro:
INIT_COMPLETION(struct completion c);
can be used to quickly perform this reinitialization."
It there are better way to do this? INIT_COMPLETION() currently just sets done = 0.
Quoted 55 lines
Quoted 38 lines
drivers/block/cciss.c +37
-0+/** +* 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) +{ +struct ctlr_info *h; + +set_current_state (TASK_INTERRUPTIBLE ); +while (!kthread_should_stop ()) { +mutex_lock (&scan_mutex); +if (list_empty (&scan_q)) { + h =NULL ; + }else { + 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) { +__set_current_state (TASK_RUNNING );rebuild_lun_table (h,0 ); +complete_all (&h->scan_wait); +mutex_lock (&scan_mutex); + h->busy_scanning =0 ; +mutex_unlock (&scan_mutex); +set_current_state (TASK_INTERRUPTIBLE );drivers/block/cciss.c static irqreturn_t do_cciss_intr(int irq, void *dev_id) · line 3358
Quoted 10 lines
drivers/block/cciss.c +7
-0+ }else { +schedule (); +set_current_state (TASK_INTERRUPTIBLE ); +continue ; + } } + +__set_current_state (TASK_RUNNING );return 0 ; }This code is somewhat wrong. Look:
set_current_state(TASK_INTERRUPTIBLE); ... mutex_lock(&scan_mutex);
Quoted 6 lines
now, we don't know what state the task is in. If the
mutex_lock()slept the we're in stateTASK_RUNNING. If themutex_lock()didn't sleep then we're in state <mumble>. Where <mumble>==TASK_INTERRUPTIBLE, but that's an implementation fluke.So I'd say that some rethinking/restructuring is needed here.
Will look into this. I have had a hard time removing this possible race (even the mutex_lock() does not remove it completely, but at most you get multiple completions) without using some sort of nested locks.
Quoted 2 lines
Signature · 2 lines
Andrew Patterson Hewlett-Packard
Re: [PATCH 2/3] cciss: use only one scan thread
Quoted 17 lines · On Thu, 16 Jul 2009 04:15:59 +0000 Andrew Patterson <[email protected]>…
Quoted 5 lines
Quoted 1 line
+}
We already did
init_completion(h->scan_wait)at startup time? Doing theINIT_COMPLETION()here looks unusual and is hopefully unnecessary.I am following the text in the "Linux Device Drivers 3rd Edition section 5.4:
"A completion is normally a one-shot device; it is used once then discarded. It is possible, however, to reuse completion structures if proper care is taken. If
complete_allis not used, a completion structure can be reused without any problems as long as there is no ambiguity about what event is being signalled. If you usecomplete_all, however, you must reinitialize the completion structure before reusing it. The macro:
Quoted 6 lines
INIT_COMPLETION(struct completion c);can be used to quickly perform this reinitialization."
It there are better way to do this?
INIT_COMPLETION()currently just sets done = 0.
OK, I'd assumed that a full wait_for_completion()/complete() operation would leave the completion in ready-to-use-again state.
But wait_for_completion_interruptible() can indeed leave completion.done in a non-zero state if it was interrupted by a signal. hm. Perhaps so the caller can run wait_for_completion*() again.
PATCH 3/3 cciss: kick off logical drive topology rescan through sysfs
In reply to Andrew Pattersoncciss: kick off logical drive topology rescan through sysfs
Added /sys/bus/pci/devices/<dev>/ccissX/rescan sysfs entry used to kick off a rescan that discovers logical drive topology changes.
---
Documentation/ABI/testing/sysfs-bus-pci-devices-cciss +7 -0
@@ -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: July 2009 +Kernel Version: 2.6.31 +Contact: [email protected] +Description: Kicks of a rescan of the controller to discovery logical + drive topology changes.
drivers/block/cciss.c +32 -3
@@ -461,9 +461,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,
@@ -567,6 +577,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,
Quoted 11 lines · On Tue, 14 Jul 2009 16:02:40 -0600 Andrew Patterson <[email protected]>…
The following series fixes several problems causing hangs while rmmoding the cciss driver. In the process of fixing these hangs, I also reworked the the logical drive scanning kernel thread to use one thread per driver rather than one thread per controller. I also added a sysfs attribute to kick off a controller scan.
.../ABI/testing/sysfs-bus-pci-devices-cciss | 7 + drivers/block/cciss.c | 198 ++++++++++++++++++-- drivers/block/cciss.h | 7 + 3 files changed, 188 insertions(+), 24 deletions(-)
Quoted 5 lines
Andrew Patterson (3):
cciss: remove logical drive sysfs entries during driver cleanup.
cciss: use only one scan thread
cciss: kick off logical drive topology rescan through sysfs
So I assume that these are intended to fix the problem which cciss-fix-the-termination-of-the-scanning-thread.patch didn't fix?
Once we get this all sorted out, which kernel version(s) are we targetting and why?
Thanks.
Quoted 14 lines · Andrew Morton
Quoted 11 lines · On Tue, 14 Jul 2009 16:02:40 -0600 Andrew Patterson <[email protected]>…
The following series fixes several problems causing hangs while rmmoding the cciss driver. In the process of fixing these hangs, I also reworked the the logical drive scanning kernel thread to use one thread per driver rather than one thread per controller. I also added a sysfs attribute to kick off a controller scan.
.../ABI/testing/sysfs-bus-pci-devices-cciss | 7 + drivers/block/cciss.c | 198 ++++++++++++++++++-- drivers/block/cciss.h | 7 + 3 files changed, 188 insertions(+), 24 deletions(-)
Quoted 8 lines
Quoted 5 lines
Andrew Patterson (3):
cciss: remove logical drive sysfs entries during driver cleanup.
cciss: use only one scan thread
cciss: kick off logical drive topology rescan through sysfsSo I assume that these are intended to fix the problem which cciss-fix-the-termination-of-the-scanning-thread.patch didn't fix?
Correct. As well as a lot of the race conditions during cleanup.
Quoted 3 lines
Once we get this all sorted out, which kernel version(s) are we targetting and why?
The current problems this patchset tries to solve is that rmmod of the module hangs along with logical hot remove.
I was hoping to get these in as soon as possible. It looks like the sysfs additions and the scan_thread patches initially went into 2.6.30 (if my limited git-fu is telling me correctly), so I can't call this a regression. I guess 2.6.32 then.
Quoted 3 lines
Thanks.
Signature · 2 lines
Andrew Patterson Hewlett-Packard