diff options
author | Zhu Yi <yi.zhu@intel.com> | 2010-02-25 01:15:28 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2010-03-10 17:09:38 -0500 |
commit | c03c6aefdc2c1f5785a5b0d1a3f7e48eeaae3505 (patch) | |
tree | 5f00ed890b35708e04da459cfdd37ab9a7f46c23 /drivers | |
parent | 34dd5feb8b8b15654714731e1dbb34a6d37fb34e (diff) |
iwmc3200wifi: protect rx_tickets and rx_packets[] lists
Protect rx_tickets and rx_packets[] lists with spinlocks to fix the
race condition for concurrent list operations. In iwmc3200wifi both
sdio_isr_worker and rx_worker workqueues can access the rx ticket
and packets lists at the same time under high rx load.
Signed-off-by: Zhu Yi <yi.zhu@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/wireless/iwmc3200wifi/debugfs.c | 4 | ||||
-rw-r--r-- | drivers/net/wireless/iwmc3200wifi/iwm.h | 2 | ||||
-rw-r--r-- | drivers/net/wireless/iwmc3200wifi/main.c | 5 | ||||
-rw-r--r-- | drivers/net/wireless/iwmc3200wifi/rx.c | 24 |
4 files changed, 30 insertions, 5 deletions
diff --git a/drivers/net/wireless/iwmc3200wifi/debugfs.c b/drivers/net/wireless/iwmc3200wifi/debugfs.c index 6ac5c8dbe051..48930c1a0f76 100644 --- a/drivers/net/wireless/iwmc3200wifi/debugfs.c +++ b/drivers/net/wireless/iwmc3200wifi/debugfs.c | |||
@@ -280,6 +280,7 @@ static ssize_t iwm_debugfs_rx_ticket_read(struct file *filp, | |||
280 | if (!buf) | 280 | if (!buf) |
281 | return -ENOMEM; | 281 | return -ENOMEM; |
282 | 282 | ||
283 | spin_lock(&iwm->ticket_lock); | ||
283 | list_for_each_entry(ticket, &iwm->rx_tickets, node) { | 284 | list_for_each_entry(ticket, &iwm->rx_tickets, node) { |
284 | len += snprintf(buf + len, buf_len - len, "Ticket #%d\n", | 285 | len += snprintf(buf + len, buf_len - len, "Ticket #%d\n", |
285 | ticket->ticket->id); | 286 | ticket->ticket->id); |
@@ -288,6 +289,7 @@ static ssize_t iwm_debugfs_rx_ticket_read(struct file *filp, | |||
288 | len += snprintf(buf + len, buf_len - len, "\tflags: 0x%x\n", | 289 | len += snprintf(buf + len, buf_len - len, "\tflags: 0x%x\n", |
289 | ticket->ticket->flags); | 290 | ticket->ticket->flags); |
290 | } | 291 | } |
292 | spin_unlock(&iwm->ticket_lock); | ||
291 | 293 | ||
292 | for (i = 0; i < IWM_RX_ID_HASH; i++) { | 294 | for (i = 0; i < IWM_RX_ID_HASH; i++) { |
293 | struct iwm_rx_packet *packet; | 295 | struct iwm_rx_packet *packet; |
@@ -296,6 +298,7 @@ static ssize_t iwm_debugfs_rx_ticket_read(struct file *filp, | |||
296 | if (!list_empty(pkt_list)) { | 298 | if (!list_empty(pkt_list)) { |
297 | len += snprintf(buf + len, buf_len - len, | 299 | len += snprintf(buf + len, buf_len - len, |
298 | "Packet hash #%d\n", i); | 300 | "Packet hash #%d\n", i); |
301 | spin_lock(&iwm->packet_lock[i]); | ||
299 | list_for_each_entry(packet, pkt_list, node) { | 302 | list_for_each_entry(packet, pkt_list, node) { |
300 | len += snprintf(buf + len, buf_len - len, | 303 | len += snprintf(buf + len, buf_len - len, |
301 | "\tPacket id: %d\n", | 304 | "\tPacket id: %d\n", |
@@ -304,6 +307,7 @@ static ssize_t iwm_debugfs_rx_ticket_read(struct file *filp, | |||
304 | "\tPacket length: %lu\n", | 307 | "\tPacket length: %lu\n", |
305 | packet->pkt_size); | 308 | packet->pkt_size); |
306 | } | 309 | } |
310 | spin_unlock(&iwm->packet_lock[i]); | ||
307 | } | 311 | } |
308 | } | 312 | } |
309 | 313 | ||
diff --git a/drivers/net/wireless/iwmc3200wifi/iwm.h b/drivers/net/wireless/iwmc3200wifi/iwm.h index 9ad5b3c22eb2..13266c3842f8 100644 --- a/drivers/net/wireless/iwmc3200wifi/iwm.h +++ b/drivers/net/wireless/iwmc3200wifi/iwm.h | |||
@@ -269,7 +269,9 @@ struct iwm_priv { | |||
269 | 269 | ||
270 | struct sk_buff_head rx_list; | 270 | struct sk_buff_head rx_list; |
271 | struct list_head rx_tickets; | 271 | struct list_head rx_tickets; |
272 | spinlock_t ticket_lock; | ||
272 | struct list_head rx_packets[IWM_RX_ID_HASH]; | 273 | struct list_head rx_packets[IWM_RX_ID_HASH]; |
274 | spinlock_t packet_lock[IWM_RX_ID_HASH]; | ||
273 | struct workqueue_struct *rx_wq; | 275 | struct workqueue_struct *rx_wq; |
274 | struct work_struct rx_worker; | 276 | struct work_struct rx_worker; |
275 | 277 | ||
diff --git a/drivers/net/wireless/iwmc3200wifi/main.c b/drivers/net/wireless/iwmc3200wifi/main.c index 90519600a947..3a3510a6223a 100644 --- a/drivers/net/wireless/iwmc3200wifi/main.c +++ b/drivers/net/wireless/iwmc3200wifi/main.c | |||
@@ -276,8 +276,11 @@ int iwm_priv_init(struct iwm_priv *iwm) | |||
276 | 276 | ||
277 | skb_queue_head_init(&iwm->rx_list); | 277 | skb_queue_head_init(&iwm->rx_list); |
278 | INIT_LIST_HEAD(&iwm->rx_tickets); | 278 | INIT_LIST_HEAD(&iwm->rx_tickets); |
279 | for (i = 0; i < IWM_RX_ID_HASH; i++) | 279 | spin_lock_init(&iwm->ticket_lock); |
280 | for (i = 0; i < IWM_RX_ID_HASH; i++) { | ||
280 | INIT_LIST_HEAD(&iwm->rx_packets[i]); | 281 | INIT_LIST_HEAD(&iwm->rx_packets[i]); |
282 | spin_lock_init(&iwm->packet_lock[i]); | ||
283 | } | ||
281 | 284 | ||
282 | INIT_WORK(&iwm->rx_worker, iwm_rx_worker); | 285 | INIT_WORK(&iwm->rx_worker, iwm_rx_worker); |
283 | 286 | ||
diff --git a/drivers/net/wireless/iwmc3200wifi/rx.c b/drivers/net/wireless/iwmc3200wifi/rx.c index 38d950b0f24b..c8a31be16885 100644 --- a/drivers/net/wireless/iwmc3200wifi/rx.c +++ b/drivers/net/wireless/iwmc3200wifi/rx.c | |||
@@ -344,10 +344,15 @@ static struct iwm_rx_packet *iwm_rx_packet_get(struct iwm_priv *iwm, u16 id) | |||
344 | u8 id_hash = IWM_RX_ID_GET_HASH(id); | 344 | u8 id_hash = IWM_RX_ID_GET_HASH(id); |
345 | struct iwm_rx_packet *packet; | 345 | struct iwm_rx_packet *packet; |
346 | 346 | ||
347 | spin_lock(&iwm->packet_lock[id_hash]); | ||
347 | list_for_each_entry(packet, &iwm->rx_packets[id_hash], node) | 348 | list_for_each_entry(packet, &iwm->rx_packets[id_hash], node) |
348 | if (packet->id == id) | 349 | if (packet->id == id) { |
350 | list_del(&packet->node); | ||
351 | spin_unlock(&iwm->packet_lock[id_hash]); | ||
349 | return packet; | 352 | return packet; |
353 | } | ||
350 | 354 | ||
355 | spin_unlock(&iwm->packet_lock[id_hash]); | ||
351 | return NULL; | 356 | return NULL; |
352 | } | 357 | } |
353 | 358 | ||
@@ -385,18 +390,22 @@ void iwm_rx_free(struct iwm_priv *iwm) | |||
385 | struct iwm_rx_packet *packet, *np; | 390 | struct iwm_rx_packet *packet, *np; |
386 | int i; | 391 | int i; |
387 | 392 | ||
393 | spin_lock(&iwm->ticket_lock); | ||
388 | list_for_each_entry_safe(ticket, nt, &iwm->rx_tickets, node) { | 394 | list_for_each_entry_safe(ticket, nt, &iwm->rx_tickets, node) { |
389 | list_del(&ticket->node); | 395 | list_del(&ticket->node); |
390 | iwm_rx_ticket_node_free(ticket); | 396 | iwm_rx_ticket_node_free(ticket); |
391 | } | 397 | } |
398 | spin_unlock(&iwm->ticket_lock); | ||
392 | 399 | ||
393 | for (i = 0; i < IWM_RX_ID_HASH; i++) { | 400 | for (i = 0; i < IWM_RX_ID_HASH; i++) { |
401 | spin_lock(&iwm->packet_lock[i]); | ||
394 | list_for_each_entry_safe(packet, np, &iwm->rx_packets[i], | 402 | list_for_each_entry_safe(packet, np, &iwm->rx_packets[i], |
395 | node) { | 403 | node) { |
396 | list_del(&packet->node); | 404 | list_del(&packet->node); |
397 | kfree_skb(packet->skb); | 405 | kfree_skb(packet->skb); |
398 | kfree(packet); | 406 | kfree(packet); |
399 | } | 407 | } |
408 | spin_unlock(&iwm->packet_lock[i]); | ||
400 | } | 409 | } |
401 | } | 410 | } |
402 | 411 | ||
@@ -424,7 +433,9 @@ static int iwm_ntf_rx_ticket(struct iwm_priv *iwm, u8 *buf, | |||
424 | ticket->action == IWM_RX_TICKET_RELEASE ? | 433 | ticket->action == IWM_RX_TICKET_RELEASE ? |
425 | "RELEASE" : "DROP", | 434 | "RELEASE" : "DROP", |
426 | ticket->id); | 435 | ticket->id); |
436 | spin_lock(&iwm->ticket_lock); | ||
427 | list_add_tail(&ticket_node->node, &iwm->rx_tickets); | 437 | list_add_tail(&ticket_node->node, &iwm->rx_tickets); |
438 | spin_unlock(&iwm->ticket_lock); | ||
428 | 439 | ||
429 | /* | 440 | /* |
430 | * We received an Rx ticket, most likely there's | 441 | * We received an Rx ticket, most likely there's |
@@ -457,6 +468,7 @@ static int iwm_ntf_rx_packet(struct iwm_priv *iwm, u8 *buf, | |||
457 | struct iwm_rx_packet *packet; | 468 | struct iwm_rx_packet *packet; |
458 | u16 id, buf_offset; | 469 | u16 id, buf_offset; |
459 | u32 packet_size; | 470 | u32 packet_size; |
471 | u8 id_hash; | ||
460 | 472 | ||
461 | IWM_DBG_RX(iwm, DBG, "\n"); | 473 | IWM_DBG_RX(iwm, DBG, "\n"); |
462 | 474 | ||
@@ -474,7 +486,10 @@ static int iwm_ntf_rx_packet(struct iwm_priv *iwm, u8 *buf, | |||
474 | if (IS_ERR(packet)) | 486 | if (IS_ERR(packet)) |
475 | return PTR_ERR(packet); | 487 | return PTR_ERR(packet); |
476 | 488 | ||
477 | list_add_tail(&packet->node, &iwm->rx_packets[IWM_RX_ID_GET_HASH(id)]); | 489 | id_hash = IWM_RX_ID_GET_HASH(id); |
490 | spin_lock(&iwm->packet_lock[id_hash]); | ||
491 | list_add_tail(&packet->node, &iwm->rx_packets[id_hash]); | ||
492 | spin_unlock(&iwm->packet_lock[id_hash]); | ||
478 | 493 | ||
479 | /* We might (unlikely) have received the packet _after_ the ticket */ | 494 | /* We might (unlikely) have received the packet _after_ the ticket */ |
480 | queue_work(iwm->rx_wq, &iwm->rx_worker); | 495 | queue_work(iwm->rx_wq, &iwm->rx_worker); |
@@ -1664,6 +1679,7 @@ void iwm_rx_worker(struct work_struct *work) | |||
1664 | * We stop whenever a ticket is missing its packet, as we're | 1679 | * We stop whenever a ticket is missing its packet, as we're |
1665 | * supposed to send the packets in order. | 1680 | * supposed to send the packets in order. |
1666 | */ | 1681 | */ |
1682 | spin_lock(&iwm->ticket_lock); | ||
1667 | list_for_each_entry_safe(ticket, next, &iwm->rx_tickets, node) { | 1683 | list_for_each_entry_safe(ticket, next, &iwm->rx_tickets, node) { |
1668 | struct iwm_rx_packet *packet = | 1684 | struct iwm_rx_packet *packet = |
1669 | iwm_rx_packet_get(iwm, le16_to_cpu(ticket->ticket->id)); | 1685 | iwm_rx_packet_get(iwm, le16_to_cpu(ticket->ticket->id)); |
@@ -1672,12 +1688,12 @@ void iwm_rx_worker(struct work_struct *work) | |||
1672 | IWM_DBG_RX(iwm, DBG, "Skip rx_work: Wait for ticket %d " | 1688 | IWM_DBG_RX(iwm, DBG, "Skip rx_work: Wait for ticket %d " |
1673 | "to be handled first\n", | 1689 | "to be handled first\n", |
1674 | le16_to_cpu(ticket->ticket->id)); | 1690 | le16_to_cpu(ticket->ticket->id)); |
1675 | return; | 1691 | break; |
1676 | } | 1692 | } |
1677 | 1693 | ||
1678 | list_del(&ticket->node); | 1694 | list_del(&ticket->node); |
1679 | list_del(&packet->node); | ||
1680 | iwm_rx_process_packet(iwm, packet, ticket); | 1695 | iwm_rx_process_packet(iwm, packet, ticket); |
1681 | } | 1696 | } |
1697 | spin_unlock(&iwm->ticket_lock); | ||
1682 | } | 1698 | } |
1683 | 1699 | ||