aboutsummaryrefslogtreecommitdiffstats
path: root/net/bluetooth
diff options
context:
space:
mode:
authorRoger Quadros <ext-roger.quadros@nokia.com>2009-04-23 07:50:54 -0400
committerMarcel Holtmann <marcel@holtmann.org>2009-04-28 12:31:38 -0400
commitf3784d834c71689336fa272df420b45345cb6b84 (patch)
treebfed4c21b3b3360bab0eb8123595e911adfbfdcf /net/bluetooth
parentbf368e4e70cd4e0f880923c44e95a4273d725ab4 (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.c37
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 @@
9struct class *bt_class = NULL; 9struct class *bt_class = NULL;
10EXPORT_SYMBOL_GPL(bt_class); 10EXPORT_SYMBOL_GPL(bt_class);
11 11
12static struct workqueue_struct *btaddconn; 12static struct workqueue_struct *bluetooth;
13static struct workqueue_struct *btdelconn;
14 13
15static inline char *link_typetostr(int type) 14static inline char *link_typetostr(int type)
16{ 15{
@@ -88,9 +87,10 @@ static struct device_type bt_link = {
88 87
89static void add_conn(struct work_struct *work) 88static 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
132static void del_conn(struct work_struct *work) 132static 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
164static inline char *host_typetostr(int type) 167static inline char *host_typetostr(int type)
@@ -435,20 +438,13 @@ void hci_unregister_sysfs(struct hci_dev *hdev)
435 438
436int __init bt_sysfs_init(void) 439int __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
458void bt_sysfs_cleanup(void) 454void 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}