aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorZhu Yi <yi.zhu@intel.com>2010-02-25 01:15:28 -0500
committerJohn W. Linville <linville@tuxdriver.com>2010-03-10 17:09:38 -0500
commitc03c6aefdc2c1f5785a5b0d1a3f7e48eeaae3505 (patch)
tree5f00ed890b35708e04da459cfdd37ab9a7f46c23
parent34dd5feb8b8b15654714731e1dbb34a6d37fb34e (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>
-rw-r--r--drivers/net/wireless/iwmc3200wifi/debugfs.c4
-rw-r--r--drivers/net/wireless/iwmc3200wifi/iwm.h2
-rw-r--r--drivers/net/wireless/iwmc3200wifi/main.c5
-rw-r--r--drivers/net/wireless/iwmc3200wifi/rx.c24
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