diff options
author | Roger Quadros <ext-roger.quadros@nokia.com> | 2009-04-23 07:50:54 -0400 |
---|---|---|
committer | Marcel Holtmann <marcel@holtmann.org> | 2009-04-28 12:31:38 -0400 |
commit | f3784d834c71689336fa272df420b45345cb6b84 (patch) | |
tree | bfed4c21b3b3360bab0eb8123595e911adfbfdcf /net/bluetooth | |
parent | bf368e4e70cd4e0f880923c44e95a4273d725ab4 (diff) |
Bluetooth: Ensure that HCI sysfs add/del is preempt safe
Use a different work_struct variables for add_conn() and del_conn() and
use single work queue instead of two for adding and deleting connections.
It eliminates the following error on a preemptible kernel:
[ 204.358032] Unable to handle kernel NULL pointer dereference at virtual address 0000000c
[ 204.370697] pgd = c0004000
[ 204.373443] [0000000c] *pgd=00000000
[ 204.378601] Internal error: Oops: 17 [#1] PREEMPT
[ 204.383361] Modules linked in: vfat fat rfcomm sco l2cap sd_mod scsi_mod iphb pvr2d drm omaplfb ps
[ 204.438537] CPU: 0 Not tainted (2.6.28-maemo2 #1)
[ 204.443664] PC is at klist_put+0x2c/0xb4
[ 204.447601] LR is at klist_put+0x18/0xb4
[ 204.451568] pc : [<c0270f08>] lr : [<c0270ef4>] psr: a0000113
[ 204.451568] sp : cf1b3f10 ip : cf1b3f10 fp : cf1b3f2c
[ 204.463104] r10: 00000000 r9 : 00000000 r8 : bf08029c
[ 204.468353] r7 : c7869200 r6 : cfbe2690 r5 : c78692c8 r4 : 00000001
[ 204.474945] r3 : 00000001 r2 : cf1b2000 r1 : 00000001 r0 : 00000000
[ 204.481506] Flags: NzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment kernel
[ 204.488861] Control: 10c5387d Table: 887fc018 DAC: 00000017
[ 204.494628] Process btdelconn (pid: 515, stack limit = 0xcf1b22e0)
Signed-off-by: Roger Quadros <ext-roger.quadros@nokia.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'net/bluetooth')
-rw-r--r-- | net/bluetooth/hci_sysfs.c | 37 |
1 files changed, 16 insertions, 21 deletions
diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c index ed82796d4a0f..b7c51082ddeb 100644 --- a/net/bluetooth/hci_sysfs.c +++ b/net/bluetooth/hci_sysfs.c | |||
@@ -9,8 +9,7 @@ | |||
9 | struct class *bt_class = NULL; | 9 | struct class *bt_class = NULL; |
10 | EXPORT_SYMBOL_GPL(bt_class); | 10 | EXPORT_SYMBOL_GPL(bt_class); |
11 | 11 | ||
12 | static struct workqueue_struct *btaddconn; | 12 | static struct workqueue_struct *bluetooth; |
13 | static struct workqueue_struct *btdelconn; | ||
14 | 13 | ||
15 | static inline char *link_typetostr(int type) | 14 | static inline char *link_typetostr(int type) |
16 | { | 15 | { |
@@ -88,9 +87,10 @@ static struct device_type bt_link = { | |||
88 | 87 | ||
89 | static void add_conn(struct work_struct *work) | 88 | static void add_conn(struct work_struct *work) |
90 | { | 89 | { |
91 | struct hci_conn *conn = container_of(work, struct hci_conn, work); | 90 | struct hci_conn *conn = container_of(work, struct hci_conn, work_add); |
92 | 91 | ||
93 | flush_workqueue(btdelconn); | 92 | /* ensure previous add/del is complete */ |
93 | flush_workqueue(bluetooth); | ||
94 | 94 | ||
95 | if (device_add(&conn->dev) < 0) { | 95 | if (device_add(&conn->dev) < 0) { |
96 | BT_ERR("Failed to register connection device"); | 96 | BT_ERR("Failed to register connection device"); |
@@ -114,9 +114,9 @@ void hci_conn_add_sysfs(struct hci_conn *conn) | |||
114 | 114 | ||
115 | device_initialize(&conn->dev); | 115 | device_initialize(&conn->dev); |
116 | 116 | ||
117 | INIT_WORK(&conn->work, add_conn); | 117 | INIT_WORK(&conn->work_add, add_conn); |
118 | 118 | ||
119 | queue_work(btaddconn, &conn->work); | 119 | queue_work(bluetooth, &conn->work_add); |
120 | } | 120 | } |
121 | 121 | ||
122 | /* | 122 | /* |
@@ -131,9 +131,12 @@ static int __match_tty(struct device *dev, void *data) | |||
131 | 131 | ||
132 | static void del_conn(struct work_struct *work) | 132 | static void del_conn(struct work_struct *work) |
133 | { | 133 | { |
134 | struct hci_conn *conn = container_of(work, struct hci_conn, work); | 134 | struct hci_conn *conn = container_of(work, struct hci_conn, work_del); |
135 | struct hci_dev *hdev = conn->hdev; | 135 | struct hci_dev *hdev = conn->hdev; |
136 | 136 | ||
137 | /* ensure previous add/del is complete */ | ||
138 | flush_workqueue(bluetooth); | ||
139 | |||
137 | while (1) { | 140 | while (1) { |
138 | struct device *dev; | 141 | struct device *dev; |
139 | 142 | ||
@@ -156,9 +159,9 @@ void hci_conn_del_sysfs(struct hci_conn *conn) | |||
156 | if (!device_is_registered(&conn->dev)) | 159 | if (!device_is_registered(&conn->dev)) |
157 | return; | 160 | return; |
158 | 161 | ||
159 | INIT_WORK(&conn->work, del_conn); | 162 | INIT_WORK(&conn->work_del, del_conn); |
160 | 163 | ||
161 | queue_work(btdelconn, &conn->work); | 164 | queue_work(bluetooth, &conn->work_del); |
162 | } | 165 | } |
163 | 166 | ||
164 | static inline char *host_typetostr(int type) | 167 | static inline char *host_typetostr(int type) |
@@ -435,20 +438,13 @@ void hci_unregister_sysfs(struct hci_dev *hdev) | |||
435 | 438 | ||
436 | int __init bt_sysfs_init(void) | 439 | int __init bt_sysfs_init(void) |
437 | { | 440 | { |
438 | btaddconn = create_singlethread_workqueue("btaddconn"); | 441 | bluetooth = create_singlethread_workqueue("bluetooth"); |
439 | if (!btaddconn) | 442 | if (!bluetooth) |
440 | return -ENOMEM; | ||
441 | |||
442 | btdelconn = create_singlethread_workqueue("btdelconn"); | ||
443 | if (!btdelconn) { | ||
444 | destroy_workqueue(btaddconn); | ||
445 | return -ENOMEM; | 443 | return -ENOMEM; |
446 | } | ||
447 | 444 | ||
448 | bt_class = class_create(THIS_MODULE, "bluetooth"); | 445 | bt_class = class_create(THIS_MODULE, "bluetooth"); |
449 | if (IS_ERR(bt_class)) { | 446 | if (IS_ERR(bt_class)) { |
450 | destroy_workqueue(btdelconn); | 447 | destroy_workqueue(bluetooth); |
451 | destroy_workqueue(btaddconn); | ||
452 | return PTR_ERR(bt_class); | 448 | return PTR_ERR(bt_class); |
453 | } | 449 | } |
454 | 450 | ||
@@ -457,8 +453,7 @@ int __init bt_sysfs_init(void) | |||
457 | 453 | ||
458 | void bt_sysfs_cleanup(void) | 454 | void bt_sysfs_cleanup(void) |
459 | { | 455 | { |
460 | destroy_workqueue(btaddconn); | 456 | destroy_workqueue(bluetooth); |
461 | destroy_workqueue(btdelconn); | ||
462 | 457 | ||
463 | class_destroy(bt_class); | 458 | class_destroy(bt_class); |
464 | } | 459 | } |