diff options
author | David Woodhouse <dwmw2@infradead.org> | 2007-12-09 23:54:27 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-01-28 18:06:30 -0500 |
commit | 2eb188a1c57ae79283cee951c317bd191cf1ca56 (patch) | |
tree | 6fa459e2d171f035c97af88bfa5957dd544b4413 /drivers/net/wireless/libertas | |
parent | b8d40bc9c9099943cbcf18d285bf241f1f080a44 (diff) |
libertas: Move actual transmission to main thread
The locking issues with TX, especially TX from multiple netdevs, get
_so_ much easier if you do it like this.
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/libertas')
-rw-r--r-- | drivers/net/wireless/libertas/dev.h | 5 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/main.c | 24 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/tx.c | 103 |
3 files changed, 71 insertions, 61 deletions
diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h index 04cf33da589..468140512a6 100644 --- a/drivers/net/wireless/libertas/dev.h +++ b/drivers/net/wireless/libertas/dev.h | |||
@@ -164,7 +164,10 @@ struct lbs_private { | |||
164 | 164 | ||
165 | struct mutex lock; | 165 | struct mutex lock; |
166 | 166 | ||
167 | u8 tmptxbuf[LBS_UPLD_SIZE]; | 167 | /* TX packet ready to be sent... */ |
168 | int tx_pending_len; /* -1 while building packet */ | ||
169 | |||
170 | u8 tx_pending_buf[LBS_UPLD_SIZE]; | ||
168 | /* protected by hard_start_xmit serialization */ | 171 | /* protected by hard_start_xmit serialization */ |
169 | 172 | ||
170 | /** command-related variables */ | 173 | /** command-related variables */ |
diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c index 42b64b5ad08..f16c93ba6ef 100644 --- a/drivers/net/wireless/libertas/main.c +++ b/drivers/net/wireless/libertas/main.c | |||
@@ -739,6 +739,8 @@ static int lbs_thread(void *data) | |||
739 | shouldsleep = 0; /* Interrupt pending. Deal with it now */ | 739 | shouldsleep = 0; /* Interrupt pending. Deal with it now */ |
740 | else if (priv->dnld_sent) | 740 | else if (priv->dnld_sent) |
741 | shouldsleep = 1; /* Something is en route to the device already */ | 741 | shouldsleep = 1; /* Something is en route to the device already */ |
742 | else if (priv->tx_pending_len > 0) | ||
743 | shouldsleep = 0; /* We've a packet to send */ | ||
742 | else if (priv->cur_cmd) | 744 | else if (priv->cur_cmd) |
743 | shouldsleep = 1; /* Can't send a command; one already running */ | 745 | shouldsleep = 1; /* Can't send a command; one already running */ |
744 | else if (!list_empty(&priv->cmdpendingq)) | 746 | else if (!list_empty(&priv->cmdpendingq)) |
@@ -852,6 +854,28 @@ static int lbs_thread(void *data) | |||
852 | */ | 854 | */ |
853 | if (!list_empty(&priv->cmdpendingq)) | 855 | if (!list_empty(&priv->cmdpendingq)) |
854 | wake_up_all(&priv->cmd_pending); | 856 | wake_up_all(&priv->cmd_pending); |
857 | |||
858 | spin_lock_irq(&priv->driver_lock); | ||
859 | if (!priv->dnld_sent && priv->tx_pending_len > 0) { | ||
860 | int ret = priv->hw_host_to_card(priv, MVMS_DAT, | ||
861 | priv->tx_pending_buf, | ||
862 | priv->tx_pending_len); | ||
863 | if (ret) { | ||
864 | lbs_deb_tx("host_to_card failed %d\n", ret); | ||
865 | priv->dnld_sent = DNLD_RES_RECEIVED; | ||
866 | } | ||
867 | priv->tx_pending_len = 0; | ||
868 | if (!priv->currenttxskb) { | ||
869 | /* We can wake the queues immediately if we aren't | ||
870 | waiting for TX feedback */ | ||
871 | if (priv->connect_status == LBS_CONNECTED) | ||
872 | netif_wake_queue(priv->dev); | ||
873 | if (priv->mesh_dev && | ||
874 | priv->mesh_connect_status == LBS_CONNECTED) | ||
875 | netif_wake_queue(priv->mesh_dev); | ||
876 | } | ||
877 | } | ||
878 | spin_unlock_irq(&priv->driver_lock); | ||
855 | } | 879 | } |
856 | 880 | ||
857 | del_timer(&priv->command_timer); | 881 | del_timer(&priv->command_timer); |
diff --git a/drivers/net/wireless/libertas/tx.c b/drivers/net/wireless/libertas/tx.c index 300aa05edeb..e2141f0a67d 100644 --- a/drivers/net/wireless/libertas/tx.c +++ b/drivers/net/wireless/libertas/tx.c | |||
@@ -67,39 +67,45 @@ int lbs_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) | |||
67 | 67 | ||
68 | lbs_deb_enter(LBS_DEB_TX); | 68 | lbs_deb_enter(LBS_DEB_TX); |
69 | 69 | ||
70 | ret = NETDEV_TX_BUSY; | 70 | ret = NETDEV_TX_OK; |
71 | |||
72 | if (priv->dnld_sent) { | ||
73 | lbs_pr_alert( "TX error: dnld_sent = %d, not sending\n", | ||
74 | priv->dnld_sent); | ||
75 | goto done; | ||
76 | } | ||
77 | |||
78 | if (priv->currenttxskb) { | ||
79 | lbs_pr_err("%s while TX skb pending\n", __func__); | ||
80 | goto done; | ||
81 | } | ||
82 | 71 | ||
83 | if ((priv->psstate == PS_STATE_SLEEP) || | 72 | /* We need to protect against the queues being restarted before |
84 | (priv->psstate == PS_STATE_PRE_SLEEP)) { | 73 | we get round to stopping them */ |
85 | lbs_pr_alert("TX error: packet xmit in %ssleep mode\n", | 74 | spin_lock_irqsave(&priv->driver_lock, flags); |
86 | priv->psstate == PS_STATE_SLEEP?"":"pre-"); | ||
87 | goto done; | ||
88 | } | ||
89 | 75 | ||
90 | if (priv->surpriseremoved) | 76 | if (priv->surpriseremoved) |
91 | goto drop; | 77 | goto free; |
92 | 78 | ||
93 | if (!skb->len || (skb->len > MRVDRV_ETH_TX_PACKET_BUFFER_SIZE)) { | 79 | if (!skb->len || (skb->len > MRVDRV_ETH_TX_PACKET_BUFFER_SIZE)) { |
94 | lbs_deb_tx("tx err: skb length %d 0 or > %zd\n", | 80 | lbs_deb_tx("tx err: skb length %d 0 or > %zd\n", |
95 | skb->len, MRVDRV_ETH_TX_PACKET_BUFFER_SIZE); | 81 | skb->len, MRVDRV_ETH_TX_PACKET_BUFFER_SIZE); |
96 | /* We'll never manage to send this one; drop it and return 'OK' */ | 82 | /* We'll never manage to send this one; drop it and return 'OK' */ |
97 | goto drop; | 83 | |
84 | priv->stats.tx_dropped++; | ||
85 | priv->stats.tx_errors++; | ||
86 | goto free; | ||
87 | } | ||
88 | |||
89 | |||
90 | netif_stop_queue(priv->dev); | ||
91 | if (priv->mesh_dev) | ||
92 | netif_stop_queue(priv->mesh_dev); | ||
93 | |||
94 | if (priv->tx_pending_len) { | ||
95 | /* This can happen if packets come in on the mesh and eth | ||
96 | device simultaneously -- there's no mutual exclusion on | ||
97 | hard_start_xmit() calls between devices. */ | ||
98 | lbs_deb_tx("Packet on %s while busy\n", dev->name); | ||
99 | ret = NETDEV_TX_BUSY; | ||
100 | goto unlock; | ||
98 | } | 101 | } |
99 | 102 | ||
103 | priv->tx_pending_len = -1; | ||
104 | spin_unlock_irqrestore(&priv->driver_lock, flags); | ||
105 | |||
100 | lbs_deb_hex(LBS_DEB_TX, "TX Data", skb->data, min_t(unsigned int, skb->len, 100)); | 106 | lbs_deb_hex(LBS_DEB_TX, "TX Data", skb->data, min_t(unsigned int, skb->len, 100)); |
101 | 107 | ||
102 | txpd = (void *)priv->tmptxbuf; | 108 | txpd = (void *)priv->tx_pending_buf; |
103 | memset(txpd, 0, sizeof(struct txpd)); | 109 | memset(txpd, 0, sizeof(struct txpd)); |
104 | 110 | ||
105 | p802x_hdr = skb->data; | 111 | p802x_hdr = skb->data; |
@@ -134,54 +140,31 @@ int lbs_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) | |||
134 | 140 | ||
135 | memcpy(&txpd[1], p802x_hdr, le16_to_cpu(txpd->tx_packet_length)); | 141 | memcpy(&txpd[1], p802x_hdr, le16_to_cpu(txpd->tx_packet_length)); |
136 | 142 | ||
137 | /* We need to protect against the queues being restarted before | ||
138 | we get round to stopping them */ | ||
139 | spin_lock_irqsave(&priv->driver_lock, flags); | 143 | spin_lock_irqsave(&priv->driver_lock, flags); |
144 | priv->tx_pending_len = pkt_len + sizeof(struct txpd); | ||
140 | 145 | ||
141 | ret = priv->hw_host_to_card(priv, MVMS_DAT, priv->tmptxbuf, | 146 | lbs_deb_tx("%s lined up packet\n", __func__); |
142 | pkt_len + sizeof(struct txpd)); | ||
143 | |||
144 | if (!ret) { | ||
145 | lbs_deb_tx("%s succeeds\n", __func__); | ||
146 | |||
147 | /* Stop processing outgoing pkts before submitting */ | ||
148 | netif_stop_queue(priv->dev); | ||
149 | if (priv->mesh_dev) | ||
150 | netif_stop_queue(priv->mesh_dev); | ||
151 | |||
152 | priv->stats.tx_packets++; | ||
153 | priv->stats.tx_bytes += skb->len; | ||
154 | |||
155 | dev->trans_start = jiffies; | ||
156 | 147 | ||
157 | if (priv->monitormode != LBS_MONITOR_OFF) { | 148 | priv->stats.tx_packets++; |
158 | /* Keep the skb to echo it back once Tx feedback is | 149 | priv->stats.tx_bytes += skb->len; |
159 | received from FW */ | ||
160 | skb_orphan(skb); | ||
161 | 150 | ||
162 | /* Keep the skb around for when we get feedback */ | 151 | dev->trans_start = jiffies; |
163 | priv->currenttxskb = skb; | ||
164 | } else | ||
165 | dev_kfree_skb_any(skb); | ||
166 | |||
167 | } | ||
168 | |||
169 | spin_unlock_irqrestore(&priv->driver_lock, flags); | ||
170 | 152 | ||
171 | if (ret) { | 153 | if (priv->monitormode != LBS_MONITOR_OFF) { |
172 | lbs_deb_tx("tx err: hw_host_to_card returned 0x%X\n", ret); | 154 | /* Keep the skb to echo it back once Tx feedback is |
173 | drop: | 155 | received from FW */ |
174 | priv->stats.tx_dropped++; | 156 | skb_orphan(skb); |
175 | priv->stats.tx_errors++; | ||
176 | 157 | ||
158 | /* Keep the skb around for when we get feedback */ | ||
159 | priv->currenttxskb = skb; | ||
160 | } else { | ||
161 | free: | ||
177 | dev_kfree_skb_any(skb); | 162 | dev_kfree_skb_any(skb); |
178 | } | 163 | } |
164 | unlock: | ||
165 | spin_unlock_irqrestore(&priv->driver_lock, flags); | ||
166 | wake_up(&priv->waitq); | ||
179 | 167 | ||
180 | /* Even if we dropped the packet, return OK. Otherwise the | ||
181 | packet gets requeued. */ | ||
182 | ret = NETDEV_TX_OK; | ||
183 | |||
184 | done: | ||
185 | lbs_deb_leave_args(LBS_DEB_TX, "ret %d", ret); | 168 | lbs_deb_leave_args(LBS_DEB_TX, "ret %d", ret); |
186 | return ret; | 169 | return ret; |
187 | } | 170 | } |