aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firewire
diff options
context:
space:
mode:
authorStefan Richter <stefanr@s5r6.in-berlin.de>2010-11-06 18:18:23 -0400
committerStefan Richter <stefanr@s5r6.in-berlin.de>2010-11-16 18:08:48 -0500
commit48553011cea504796e513350740781ac6745f556 (patch)
treef90a9e23ecd00c9c476e61e6e2c2fdd6cc552008 /drivers/firewire
parent7ee11fa8d0a84b05cefe12b0bebc05ab0ea89cd6 (diff)
firewire: net: replace lists by counters
The current transmit code does not at all make use of - fwnet_device.packet_list and only very limited use of - fwnet_device.broadcasted_list, - fwnet_device.queued_packets. Their current function is to track whether the TX soft-IRQ finished dealing with an skb when the AT-req tasklet takes over, and to discard pending tx datagrams (if there are any) when the local node is removed. The latter does actually contain a race condition bug with TX soft-IRQ and AT-req tasklet. Instead of these lists and the corresponding link in fwnet_packet_task, - a flag in fwnet_packet_task to track whether fwnet_tx is done, - a counter of queued datagrams in fwnet_device do the job as well. The above mentioned theoretic race condition is resolved by letting fwnet_remove sleep until all datagrams were flushed. It may sleep almost arbitrarily long since fwnet_remove is executed in the context of a multithreaded (concurrency managed) workqueue. The type of max_payload is changed to u16 here to avoid waste in struct fwnet_packet_task. This value cannot exceed 4096 per IEEE 1394:2008 table 16-18 (or 32678 per specification of packet headers, if there is ever going to be something else than beta mode). Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Diffstat (limited to 'drivers/firewire')
-rw-r--r--drivers/firewire/net.c73
1 files changed, 26 insertions, 47 deletions
diff --git a/drivers/firewire/net.c b/drivers/firewire/net.c
index 3a27cee5bf2..d2d488b9cd9 100644
--- a/drivers/firewire/net.c
+++ b/drivers/firewire/net.c
@@ -7,6 +7,7 @@
7 */ 7 */
8 8
9#include <linux/bug.h> 9#include <linux/bug.h>
10#include <linux/delay.h>
10#include <linux/device.h> 11#include <linux/device.h>
11#include <linux/firewire.h> 12#include <linux/firewire.h>
12#include <linux/firewire-constants.h> 13#include <linux/firewire-constants.h>
@@ -169,15 +170,8 @@ struct fwnet_device {
169 struct fw_address_handler handler; 170 struct fw_address_handler handler;
170 u64 local_fifo; 171 u64 local_fifo;
171 172
172 /* List of packets to be sent */ 173 /* Number of tx datagrams that have been queued but not yet acked */
173 struct list_head packet_list; 174 int queued_datagrams;
174 /*
175 * List of packets that were broadcasted. When we get an ISO interrupt
176 * one of them has been sent
177 */
178 struct list_head broadcasted_list;
179 /* List of packets that have been sent but not yet acked */
180 struct list_head sent_list;
181 175
182 struct list_head peer_list; 176 struct list_head peer_list;
183 struct fw_card *card; 177 struct fw_card *card;
@@ -195,7 +189,7 @@ struct fwnet_peer {
195 unsigned pdg_size; /* pd_list size */ 189 unsigned pdg_size; /* pd_list size */
196 190
197 u16 datagram_label; /* outgoing datagram label */ 191 u16 datagram_label; /* outgoing datagram label */
198 unsigned max_payload; /* includes RFC2374_FRAG_HDR_SIZE overhead */ 192 u16 max_payload; /* includes RFC2374_FRAG_HDR_SIZE overhead */
199 int node_id; 193 int node_id;
200 int generation; 194 int generation;
201 unsigned speed; 195 unsigned speed;
@@ -203,22 +197,18 @@ struct fwnet_peer {
203 197
204/* This is our task struct. It's used for the packet complete callback. */ 198/* This is our task struct. It's used for the packet complete callback. */
205struct fwnet_packet_task { 199struct fwnet_packet_task {
206 /*
207 * ptask can actually be on dev->packet_list, dev->broadcasted_list,
208 * or dev->sent_list depending on its current state.
209 */
210 struct list_head pt_link;
211 struct fw_transaction transaction; 200 struct fw_transaction transaction;
212 struct rfc2734_header hdr; 201 struct rfc2734_header hdr;
213 struct sk_buff *skb; 202 struct sk_buff *skb;
214 struct fwnet_device *dev; 203 struct fwnet_device *dev;
215 204
216 int outstanding_pkts; 205 int outstanding_pkts;
217 unsigned max_payload;
218 u64 fifo_addr; 206 u64 fifo_addr;
219 u16 dest_node; 207 u16 dest_node;
208 u16 max_payload;
220 u8 generation; 209 u8 generation;
221 u8 speed; 210 u8 speed;
211 u8 enqueued;
222}; 212};
223 213
224/* 214/*
@@ -915,9 +905,9 @@ static void fwnet_transmit_packet_done(struct fwnet_packet_task *ptask)
915 ptask->outstanding_pkts--; 905 ptask->outstanding_pkts--;
916 906
917 /* Check whether we or the networking TX soft-IRQ is last user. */ 907 /* Check whether we or the networking TX soft-IRQ is last user. */
918 free = (ptask->outstanding_pkts == 0 && !list_empty(&ptask->pt_link)); 908 free = (ptask->outstanding_pkts == 0 && ptask->enqueued);
919 if (free) 909 if (free)
920 list_del(&ptask->pt_link); 910 dev->queued_datagrams--;
921 911
922 if (ptask->outstanding_pkts == 0) { 912 if (ptask->outstanding_pkts == 0) {
923 dev->netdev->stats.tx_packets++; 913 dev->netdev->stats.tx_packets++;
@@ -986,9 +976,9 @@ static void fwnet_transmit_packet_failed(struct fwnet_packet_task *ptask)
986 ptask->outstanding_pkts = 0; 976 ptask->outstanding_pkts = 0;
987 977
988 /* Check whether we or the networking TX soft-IRQ is last user. */ 978 /* Check whether we or the networking TX soft-IRQ is last user. */
989 free = !list_empty(&ptask->pt_link); 979 free = ptask->enqueued;
990 if (free) 980 if (free)
991 list_del(&ptask->pt_link); 981 dev->queued_datagrams--;
992 982
993 dev->netdev->stats.tx_dropped++; 983 dev->netdev->stats.tx_dropped++;
994 dev->netdev->stats.tx_errors++; 984 dev->netdev->stats.tx_errors++;
@@ -1069,9 +1059,11 @@ static int fwnet_send_packet(struct fwnet_packet_task *ptask)
1069 spin_lock_irqsave(&dev->lock, flags); 1059 spin_lock_irqsave(&dev->lock, flags);
1070 1060
1071 /* If the AT tasklet already ran, we may be last user. */ 1061 /* If the AT tasklet already ran, we may be last user. */
1072 free = (ptask->outstanding_pkts == 0 && list_empty(&ptask->pt_link)); 1062 free = (ptask->outstanding_pkts == 0 && !ptask->enqueued);
1073 if (!free) 1063 if (!free)
1074 list_add_tail(&ptask->pt_link, &dev->broadcasted_list); 1064 ptask->enqueued = true;
1065 else
1066 dev->queued_datagrams--;
1075 1067
1076 spin_unlock_irqrestore(&dev->lock, flags); 1068 spin_unlock_irqrestore(&dev->lock, flags);
1077 1069
@@ -1086,9 +1078,11 @@ static int fwnet_send_packet(struct fwnet_packet_task *ptask)
1086 spin_lock_irqsave(&dev->lock, flags); 1078 spin_lock_irqsave(&dev->lock, flags);
1087 1079
1088 /* If the AT tasklet already ran, we may be last user. */ 1080 /* If the AT tasklet already ran, we may be last user. */
1089 free = (ptask->outstanding_pkts == 0 && list_empty(&ptask->pt_link)); 1081 free = (ptask->outstanding_pkts == 0 && !ptask->enqueued);
1090 if (!free) 1082 if (!free)
1091 list_add_tail(&ptask->pt_link, &dev->sent_list); 1083 ptask->enqueued = true;
1084 else
1085 dev->queued_datagrams--;
1092 1086
1093 spin_unlock_irqrestore(&dev->lock, flags); 1087 spin_unlock_irqrestore(&dev->lock, flags);
1094 1088
@@ -1350,10 +1344,12 @@ static netdev_tx_t fwnet_tx(struct sk_buff *skb, struct net_device *net)
1350 max_payload += RFC2374_FRAG_HDR_SIZE; 1344 max_payload += RFC2374_FRAG_HDR_SIZE;
1351 } 1345 }
1352 1346
1347 dev->queued_datagrams++;
1348
1353 spin_unlock_irqrestore(&dev->lock, flags); 1349 spin_unlock_irqrestore(&dev->lock, flags);
1354 1350
1355 ptask->max_payload = max_payload; 1351 ptask->max_payload = max_payload;
1356 INIT_LIST_HEAD(&ptask->pt_link); 1352 ptask->enqueued = 0;
1357 1353
1358 fwnet_send_packet(ptask); 1354 fwnet_send_packet(ptask);
1359 1355
@@ -1487,14 +1483,9 @@ static int fwnet_probe(struct device *_dev)
1487 dev->broadcast_rcv_context = NULL; 1483 dev->broadcast_rcv_context = NULL;
1488 dev->broadcast_xmt_max_payload = 0; 1484 dev->broadcast_xmt_max_payload = 0;
1489 dev->broadcast_xmt_datagramlabel = 0; 1485 dev->broadcast_xmt_datagramlabel = 0;
1490
1491 dev->local_fifo = FWNET_NO_FIFO_ADDR; 1486 dev->local_fifo = FWNET_NO_FIFO_ADDR;
1492 1487 dev->queued_datagrams = 0;
1493 INIT_LIST_HEAD(&dev->packet_list);
1494 INIT_LIST_HEAD(&dev->broadcasted_list);
1495 INIT_LIST_HEAD(&dev->sent_list);
1496 INIT_LIST_HEAD(&dev->peer_list); 1488 INIT_LIST_HEAD(&dev->peer_list);
1497
1498 dev->card = card; 1489 dev->card = card;
1499 dev->netdev = net; 1490 dev->netdev = net;
1500 1491
@@ -1552,7 +1543,7 @@ static int fwnet_remove(struct device *_dev)
1552 struct fwnet_peer *peer = dev_get_drvdata(_dev); 1543 struct fwnet_peer *peer = dev_get_drvdata(_dev);
1553 struct fwnet_device *dev = peer->dev; 1544 struct fwnet_device *dev = peer->dev;
1554 struct net_device *net; 1545 struct net_device *net;
1555 struct fwnet_packet_task *ptask, *pt_next; 1546 int i;
1556 1547
1557 mutex_lock(&fwnet_device_mutex); 1548 mutex_lock(&fwnet_device_mutex);
1558 1549
@@ -1570,21 +1561,9 @@ static int fwnet_remove(struct device *_dev)
1570 dev->card); 1561 dev->card);
1571 fw_iso_context_destroy(dev->broadcast_rcv_context); 1562 fw_iso_context_destroy(dev->broadcast_rcv_context);
1572 } 1563 }
1573 list_for_each_entry_safe(ptask, pt_next, 1564 for (i = 0; dev->queued_datagrams && i < 5; i++)
1574 &dev->packet_list, pt_link) { 1565 ssleep(1);
1575 dev_kfree_skb_any(ptask->skb); 1566 WARN_ON(dev->queued_datagrams);
1576 kmem_cache_free(fwnet_packet_task_cache, ptask);
1577 }
1578 list_for_each_entry_safe(ptask, pt_next,
1579 &dev->broadcasted_list, pt_link) {
1580 dev_kfree_skb_any(ptask->skb);
1581 kmem_cache_free(fwnet_packet_task_cache, ptask);
1582 }
1583 list_for_each_entry_safe(ptask, pt_next,
1584 &dev->sent_list, pt_link) {
1585 dev_kfree_skb_any(ptask->skb);
1586 kmem_cache_free(fwnet_packet_task_cache, ptask);
1587 }
1588 list_del(&dev->dev_link); 1567 list_del(&dev->dev_link);
1589 1568
1590 free_netdev(net); 1569 free_netdev(net);