diff options
author | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2010-11-06 18:18:23 -0400 |
---|---|---|
committer | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2010-11-16 18:08:48 -0500 |
commit | 48553011cea504796e513350740781ac6745f556 (patch) | |
tree | f90a9e23ecd00c9c476e61e6e2c2fdd6cc552008 /drivers/firewire | |
parent | 7ee11fa8d0a84b05cefe12b0bebc05ab0ea89cd6 (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.c | 73 |
1 files changed, 26 insertions, 47 deletions
diff --git a/drivers/firewire/net.c b/drivers/firewire/net.c index 3a27cee5bf26..d2d488b9cd96 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. */ |
205 | struct fwnet_packet_task { | 199 | struct 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); |