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 | |
| 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>
| -rw-r--r-- | include/net/bluetooth/hci_core.h | 3 | ||||
| -rw-r--r-- | net/bluetooth/hci_sysfs.c | 37 |
2 files changed, 18 insertions, 22 deletions
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 01f9316b4c23..1224bba24bdd 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h | |||
| @@ -180,7 +180,8 @@ struct hci_conn { | |||
| 180 | struct timer_list disc_timer; | 180 | struct timer_list disc_timer; |
| 181 | struct timer_list idle_timer; | 181 | struct timer_list idle_timer; |
| 182 | 182 | ||
| 183 | struct work_struct work; | 183 | struct work_struct work_add; |
| 184 | struct work_struct work_del; | ||
| 184 | 185 | ||
| 185 | struct device dev; | 186 | struct device dev; |
| 186 | 187 | ||
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 | } |
