diff options
author | Jan Kiszka <jan.kiszka@web.de> | 2010-02-08 05:12:15 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-02-16 19:01:22 -0500 |
commit | 88c896ef87fd0dd4dbf36e8e86e019c74b1f6649 (patch) | |
tree | 727de518c111fc1d3d3a3bf49ea292ea414e17c6 /drivers/isdn/capi | |
parent | 0ca3a017a7373a4545dd7b345a8a0cecc16bc7e2 (diff) |
CAPI: Rework application locking
Drop the application rw-lock in favour of RCU. This synchronizes
capi20_release against capi_ctr_handle_message which may dereference an
application from (soft-)IRQ context. Any other access to the application
list is now protected by the capi_controller_lock as well. This also
allows to safely inspect applications for /proc dumping by holding
capi_controller_lock.
At this chance, drop some useless release_in_progress checks where we
obtained the application pointer from the list (which becomes NULL on
release_in_progress).
Signed-off-by: Jan Kiszka <jan.kiszka@web.de>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/isdn/capi')
-rw-r--r-- | drivers/isdn/capi/kcapi.c | 52 | ||||
-rw-r--r-- | drivers/isdn/capi/kcapi_proc.c | 11 |
2 files changed, 29 insertions, 34 deletions
diff --git a/drivers/isdn/capi/kcapi.c b/drivers/isdn/capi/kcapi.c index a99f7e3f8f51..0b4c8a7396bc 100644 --- a/drivers/isdn/capi/kcapi.c +++ b/drivers/isdn/capi/kcapi.c | |||
@@ -34,6 +34,7 @@ | |||
34 | #include <linux/b1lli.h> | 34 | #include <linux/b1lli.h> |
35 | #endif | 35 | #endif |
36 | #include <linux/mutex.h> | 36 | #include <linux/mutex.h> |
37 | #include <linux/rcupdate.h> | ||
37 | 38 | ||
38 | static int showcapimsgs = 0; | 39 | static int showcapimsgs = 0; |
39 | 40 | ||
@@ -64,8 +65,6 @@ DEFINE_MUTEX(capi_drivers_lock); | |||
64 | struct capi_ctr *capi_controller[CAPI_MAXCONTR]; | 65 | struct capi_ctr *capi_controller[CAPI_MAXCONTR]; |
65 | DEFINE_MUTEX(capi_controller_lock); | 66 | DEFINE_MUTEX(capi_controller_lock); |
66 | 67 | ||
67 | static DEFINE_RWLOCK(application_lock); | ||
68 | |||
69 | struct capi20_appl *capi_applications[CAPI_MAXAPPL]; | 68 | struct capi20_appl *capi_applications[CAPI_MAXAPPL]; |
70 | 69 | ||
71 | static int ncontrollers; | 70 | static int ncontrollers; |
@@ -103,7 +102,7 @@ static inline struct capi20_appl *get_capi_appl_by_nr(u16 applid) | |||
103 | if (applid - 1 >= CAPI_MAXAPPL) | 102 | if (applid - 1 >= CAPI_MAXAPPL) |
104 | return NULL; | 103 | return NULL; |
105 | 104 | ||
106 | return capi_applications[applid - 1]; | 105 | return rcu_dereference(capi_applications[applid - 1]); |
107 | } | 106 | } |
108 | 107 | ||
109 | /* -------- util functions ------------------------------------ */ | 108 | /* -------- util functions ------------------------------------ */ |
@@ -186,7 +185,7 @@ static void notify_up(u32 contr) | |||
186 | 185 | ||
187 | for (applid = 1; applid <= CAPI_MAXAPPL; applid++) { | 186 | for (applid = 1; applid <= CAPI_MAXAPPL; applid++) { |
188 | ap = get_capi_appl_by_nr(applid); | 187 | ap = get_capi_appl_by_nr(applid); |
189 | if (!ap || ap->release_in_progress) | 188 | if (!ap) |
190 | continue; | 189 | continue; |
191 | register_appl(ctr, applid, &ap->rparam); | 190 | register_appl(ctr, applid, &ap->rparam); |
192 | } | 191 | } |
@@ -216,7 +215,7 @@ static void ctr_down(struct capi_ctr *ctr, int new_state) | |||
216 | 215 | ||
217 | for (applid = 1; applid <= CAPI_MAXAPPL; applid++) { | 216 | for (applid = 1; applid <= CAPI_MAXAPPL; applid++) { |
218 | ap = get_capi_appl_by_nr(applid); | 217 | ap = get_capi_appl_by_nr(applid); |
219 | if (ap && !ap->release_in_progress) | 218 | if (ap) |
220 | capi_ctr_put(ctr); | 219 | capi_ctr_put(ctr); |
221 | } | 220 | } |
222 | 221 | ||
@@ -336,7 +335,6 @@ void capi_ctr_handle_message(struct capi_ctr *ctr, u16 appl, | |||
336 | struct capi20_appl *ap; | 335 | struct capi20_appl *ap; |
337 | int showctl = 0; | 336 | int showctl = 0; |
338 | u8 cmd, subcmd; | 337 | u8 cmd, subcmd; |
339 | unsigned long flags; | ||
340 | _cdebbuf *cdb; | 338 | _cdebbuf *cdb; |
341 | 339 | ||
342 | if (ctr->state != CAPI_CTR_RUNNING) { | 340 | if (ctr->state != CAPI_CTR_RUNNING) { |
@@ -384,10 +382,10 @@ void capi_ctr_handle_message(struct capi_ctr *ctr, u16 appl, | |||
384 | 382 | ||
385 | } | 383 | } |
386 | 384 | ||
387 | read_lock_irqsave(&application_lock, flags); | 385 | rcu_read_lock(); |
388 | ap = get_capi_appl_by_nr(CAPIMSG_APPID(skb->data)); | 386 | ap = get_capi_appl_by_nr(CAPIMSG_APPID(skb->data)); |
389 | if ((!ap) || (ap->release_in_progress)) { | 387 | if (!ap) { |
390 | read_unlock_irqrestore(&application_lock, flags); | 388 | rcu_read_unlock(); |
391 | cdb = capi_message2str(skb->data); | 389 | cdb = capi_message2str(skb->data); |
392 | if (cdb) { | 390 | if (cdb) { |
393 | printk(KERN_ERR "kcapi: handle_message: applid %d state released (%s)\n", | 391 | printk(KERN_ERR "kcapi: handle_message: applid %d state released (%s)\n", |
@@ -401,7 +399,7 @@ void capi_ctr_handle_message(struct capi_ctr *ctr, u16 appl, | |||
401 | } | 399 | } |
402 | skb_queue_tail(&ap->recv_queue, skb); | 400 | skb_queue_tail(&ap->recv_queue, skb); |
403 | schedule_work(&ap->recv_work); | 401 | schedule_work(&ap->recv_work); |
404 | read_unlock_irqrestore(&application_lock, flags); | 402 | rcu_read_unlock(); |
405 | 403 | ||
406 | return; | 404 | return; |
407 | 405 | ||
@@ -656,40 +654,35 @@ u16 capi20_register(struct capi20_appl *ap) | |||
656 | { | 654 | { |
657 | int i; | 655 | int i; |
658 | u16 applid; | 656 | u16 applid; |
659 | unsigned long flags; | ||
660 | 657 | ||
661 | DBG(""); | 658 | DBG(""); |
662 | 659 | ||
663 | if (ap->rparam.datablklen < 128) | 660 | if (ap->rparam.datablklen < 128) |
664 | return CAPI_LOGBLKSIZETOSMALL; | 661 | return CAPI_LOGBLKSIZETOSMALL; |
665 | 662 | ||
666 | write_lock_irqsave(&application_lock, flags); | 663 | ap->nrecvctlpkt = 0; |
664 | ap->nrecvdatapkt = 0; | ||
665 | ap->nsentctlpkt = 0; | ||
666 | ap->nsentdatapkt = 0; | ||
667 | mutex_init(&ap->recv_mtx); | ||
668 | skb_queue_head_init(&ap->recv_queue); | ||
669 | INIT_WORK(&ap->recv_work, recv_handler); | ||
670 | ap->release_in_progress = 0; | ||
671 | |||
672 | mutex_lock(&capi_controller_lock); | ||
667 | 673 | ||
668 | for (applid = 1; applid <= CAPI_MAXAPPL; applid++) { | 674 | for (applid = 1; applid <= CAPI_MAXAPPL; applid++) { |
669 | if (capi_applications[applid - 1] == NULL) | 675 | if (capi_applications[applid - 1] == NULL) |
670 | break; | 676 | break; |
671 | } | 677 | } |
672 | if (applid > CAPI_MAXAPPL) { | 678 | if (applid > CAPI_MAXAPPL) { |
673 | write_unlock_irqrestore(&application_lock, flags); | 679 | mutex_unlock(&capi_controller_lock); |
674 | return CAPI_TOOMANYAPPLS; | 680 | return CAPI_TOOMANYAPPLS; |
675 | } | 681 | } |
676 | 682 | ||
677 | ap->applid = applid; | 683 | ap->applid = applid; |
678 | capi_applications[applid - 1] = ap; | 684 | capi_applications[applid - 1] = ap; |
679 | 685 | ||
680 | ap->nrecvctlpkt = 0; | ||
681 | ap->nrecvdatapkt = 0; | ||
682 | ap->nsentctlpkt = 0; | ||
683 | ap->nsentdatapkt = 0; | ||
684 | mutex_init(&ap->recv_mtx); | ||
685 | skb_queue_head_init(&ap->recv_queue); | ||
686 | INIT_WORK(&ap->recv_work, recv_handler); | ||
687 | ap->release_in_progress = 0; | ||
688 | |||
689 | write_unlock_irqrestore(&application_lock, flags); | ||
690 | |||
691 | mutex_lock(&capi_controller_lock); | ||
692 | |||
693 | for (i = 0; i < CAPI_MAXCONTR; i++) { | 686 | for (i = 0; i < CAPI_MAXCONTR; i++) { |
694 | if (!capi_controller[i] || | 687 | if (!capi_controller[i] || |
695 | capi_controller[i]->state != CAPI_CTR_RUNNING) | 688 | capi_controller[i]->state != CAPI_CTR_RUNNING) |
@@ -721,16 +714,15 @@ EXPORT_SYMBOL(capi20_register); | |||
721 | u16 capi20_release(struct capi20_appl *ap) | 714 | u16 capi20_release(struct capi20_appl *ap) |
722 | { | 715 | { |
723 | int i; | 716 | int i; |
724 | unsigned long flags; | ||
725 | 717 | ||
726 | DBG("applid %#x", ap->applid); | 718 | DBG("applid %#x", ap->applid); |
727 | 719 | ||
728 | write_lock_irqsave(&application_lock, flags); | 720 | mutex_lock(&capi_controller_lock); |
721 | |||
729 | ap->release_in_progress = 1; | 722 | ap->release_in_progress = 1; |
730 | capi_applications[ap->applid - 1] = NULL; | 723 | capi_applications[ap->applid - 1] = NULL; |
731 | write_unlock_irqrestore(&application_lock, flags); | ||
732 | 724 | ||
733 | mutex_lock(&capi_controller_lock); | 725 | synchronize_rcu(); |
734 | 726 | ||
735 | for (i = 0; i < CAPI_MAXCONTR; i++) { | 727 | for (i = 0; i < CAPI_MAXCONTR; i++) { |
736 | if (!capi_controller[i] || | 728 | if (!capi_controller[i] || |
diff --git a/drivers/isdn/capi/kcapi_proc.c b/drivers/isdn/capi/kcapi_proc.c index 3e6e17a24389..ea2dff602e49 100644 --- a/drivers/isdn/capi/kcapi_proc.c +++ b/drivers/isdn/capi/kcapi_proc.c | |||
@@ -139,9 +139,11 @@ static const struct file_operations proc_contrstats_ops = { | |||
139 | // applid nrecvctlpkt nrecvdatapkt nsentctlpkt nsentdatapkt | 139 | // applid nrecvctlpkt nrecvdatapkt nsentctlpkt nsentdatapkt |
140 | // --------------------------------------------------------------------------- | 140 | // --------------------------------------------------------------------------- |
141 | 141 | ||
142 | static void * | 142 | static void *applications_start(struct seq_file *seq, loff_t *pos) |
143 | applications_start(struct seq_file *seq, loff_t *pos) | 143 | __acquires(capi_controller_lock) |
144 | { | 144 | { |
145 | mutex_lock(&capi_controller_lock); | ||
146 | |||
145 | if (*pos < CAPI_MAXAPPL) | 147 | if (*pos < CAPI_MAXAPPL) |
146 | return &capi_applications[*pos]; | 148 | return &capi_applications[*pos]; |
147 | 149 | ||
@@ -158,9 +160,10 @@ applications_next(struct seq_file *seq, void *v, loff_t *pos) | |||
158 | return NULL; | 160 | return NULL; |
159 | } | 161 | } |
160 | 162 | ||
161 | static void | 163 | static void applications_stop(struct seq_file *seq, void *v) |
162 | applications_stop(struct seq_file *seq, void *v) | 164 | __releases(capi_controller_lock) |
163 | { | 165 | { |
166 | mutex_unlock(&capi_controller_lock); | ||
164 | } | 167 | } |
165 | 168 | ||
166 | static int | 169 | static int |