diff options
author | Sjur Braendeland <sjur.brandeland@stericsson.com> | 2010-04-28 04:54:39 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-04-28 15:55:14 -0400 |
commit | 8391c4aab1aa4f47a9dab2c1ec3ebd2cbf09df1b (patch) | |
tree | b117d2d8afd3ce1711cc4206ddfd2bc02bb563ef /net/caif | |
parent | bece7b2398d073d11b2e352405a3ecd3a1e39c60 (diff) |
caif: Bugfixes in CAIF netdevice for close and flow control
Changes:
o Bugfix: Flow control was causing the device to be destroyed.
o Bugfix: Handle CAIF channel connect failures.
o If the underlying link layer is gone the net-device is no longer removed,
but closed.
Signed-off-by: Sjur Braendeland <sjur.brandeland@stericsson.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/caif')
-rw-r--r-- | net/caif/chnl_net.c | 130 |
1 files changed, 73 insertions, 57 deletions
diff --git a/net/caif/chnl_net.c b/net/caif/chnl_net.c index f622ff1d39ba..610966abe2dc 100644 --- a/net/caif/chnl_net.c +++ b/net/caif/chnl_net.c | |||
@@ -22,10 +22,10 @@ | |||
22 | #include <net/caif/cfpkt.h> | 22 | #include <net/caif/cfpkt.h> |
23 | #include <net/caif/caif_dev.h> | 23 | #include <net/caif/caif_dev.h> |
24 | 24 | ||
25 | #define CAIF_CONNECT_TIMEOUT 30 | 25 | /* GPRS PDP connection has MTU to 1500 */ |
26 | #define SIZE_MTU 1500 | 26 | #define SIZE_MTU 1500 |
27 | #define SIZE_MTU_MAX 4080 | 27 | /* 5 sec. connect timeout */ |
28 | #define SIZE_MTU_MIN 68 | 28 | #define CONNECT_TIMEOUT (5 * HZ) |
29 | #define CAIF_NET_DEFAULT_QUEUE_LEN 500 | 29 | #define CAIF_NET_DEFAULT_QUEUE_LEN 500 |
30 | 30 | ||
31 | #undef pr_debug | 31 | #undef pr_debug |
@@ -37,6 +37,13 @@ static LIST_HEAD(chnl_net_list); | |||
37 | MODULE_LICENSE("GPL"); | 37 | MODULE_LICENSE("GPL"); |
38 | MODULE_ALIAS_RTNL_LINK("caif"); | 38 | MODULE_ALIAS_RTNL_LINK("caif"); |
39 | 39 | ||
40 | enum caif_states { | ||
41 | CAIF_CONNECTED = 1, | ||
42 | CAIF_CONNECTING, | ||
43 | CAIF_DISCONNECTED, | ||
44 | CAIF_SHUTDOWN | ||
45 | }; | ||
46 | |||
40 | struct chnl_net { | 47 | struct chnl_net { |
41 | struct cflayer chnl; | 48 | struct cflayer chnl; |
42 | struct net_device_stats stats; | 49 | struct net_device_stats stats; |
@@ -47,7 +54,7 @@ struct chnl_net { | |||
47 | wait_queue_head_t netmgmt_wq; | 54 | wait_queue_head_t netmgmt_wq; |
48 | /* Flow status to remember and control the transmission. */ | 55 | /* Flow status to remember and control the transmission. */ |
49 | bool flowenabled; | 56 | bool flowenabled; |
50 | bool pending_close; | 57 | enum caif_states state; |
51 | }; | 58 | }; |
52 | 59 | ||
53 | static void robust_list_del(struct list_head *delete_node) | 60 | static void robust_list_del(struct list_head *delete_node) |
@@ -58,15 +65,16 @@ static void robust_list_del(struct list_head *delete_node) | |||
58 | list_for_each_safe(list_node, n, &chnl_net_list) { | 65 | list_for_each_safe(list_node, n, &chnl_net_list) { |
59 | if (list_node == delete_node) { | 66 | if (list_node == delete_node) { |
60 | list_del(list_node); | 67 | list_del(list_node); |
61 | break; | 68 | return; |
62 | } | 69 | } |
63 | } | 70 | } |
71 | WARN_ON(1); | ||
64 | } | 72 | } |
65 | 73 | ||
66 | static int chnl_recv_cb(struct cflayer *layr, struct cfpkt *pkt) | 74 | static int chnl_recv_cb(struct cflayer *layr, struct cfpkt *pkt) |
67 | { | 75 | { |
68 | struct sk_buff *skb; | 76 | struct sk_buff *skb; |
69 | struct chnl_net *priv = NULL; | 77 | struct chnl_net *priv = container_of(layr, struct chnl_net, chnl); |
70 | int pktlen; | 78 | int pktlen; |
71 | int err = 0; | 79 | int err = 0; |
72 | 80 | ||
@@ -91,7 +99,6 @@ static int chnl_recv_cb(struct cflayer *layr, struct cfpkt *pkt) | |||
91 | else | 99 | else |
92 | skb->ip_summed = CHECKSUM_NONE; | 100 | skb->ip_summed = CHECKSUM_NONE; |
93 | 101 | ||
94 | /* FIXME: Drivers should call this in tasklet context. */ | ||
95 | if (in_interrupt()) | 102 | if (in_interrupt()) |
96 | netif_rx(skb); | 103 | netif_rx(skb); |
97 | else | 104 | else |
@@ -117,23 +124,25 @@ static void close_work(struct work_struct *work) | |||
117 | struct chnl_net *dev = NULL; | 124 | struct chnl_net *dev = NULL; |
118 | struct list_head *list_node; | 125 | struct list_head *list_node; |
119 | struct list_head *_tmp; | 126 | struct list_head *_tmp; |
120 | rtnl_lock(); | 127 | /* May be called with or without RTNL lock held */ |
128 | int islocked = rtnl_is_locked(); | ||
129 | if (!islocked) | ||
130 | rtnl_lock(); | ||
121 | list_for_each_safe(list_node, _tmp, &chnl_net_list) { | 131 | list_for_each_safe(list_node, _tmp, &chnl_net_list) { |
122 | dev = list_entry(list_node, struct chnl_net, list_field); | 132 | dev = list_entry(list_node, struct chnl_net, list_field); |
123 | if (!dev->pending_close) | 133 | if (dev->state == CAIF_SHUTDOWN) |
124 | continue; | 134 | dev_close(dev->netdev); |
125 | list_del(list_node); | ||
126 | delete_device(dev); | ||
127 | } | 135 | } |
128 | rtnl_unlock(); | 136 | if (!islocked) |
137 | rtnl_unlock(); | ||
129 | } | 138 | } |
130 | static DECLARE_WORK(close_worker, close_work); | 139 | static DECLARE_WORK(close_worker, close_work); |
131 | 140 | ||
132 | static void chnl_flowctrl_cb(struct cflayer *layr, enum caif_ctrlcmd flow, | 141 | static void chnl_flowctrl_cb(struct cflayer *layr, enum caif_ctrlcmd flow, |
133 | int phyid) | 142 | int phyid) |
134 | { | 143 | { |
135 | struct chnl_net *priv; | 144 | struct chnl_net *priv = container_of(layr, struct chnl_net, chnl); |
136 | pr_debug("CAIF: %s(): NET flowctrl func called flow: %s.\n", | 145 | pr_debug("CAIF: %s(): NET flowctrl func called flow: %s\n", |
137 | __func__, | 146 | __func__, |
138 | flow == CAIF_CTRLCMD_FLOW_ON_IND ? "ON" : | 147 | flow == CAIF_CTRLCMD_FLOW_ON_IND ? "ON" : |
139 | flow == CAIF_CTRLCMD_INIT_RSP ? "INIT" : | 148 | flow == CAIF_CTRLCMD_INIT_RSP ? "INIT" : |
@@ -143,21 +152,31 @@ static void chnl_flowctrl_cb(struct cflayer *layr, enum caif_ctrlcmd flow, | |||
143 | flow == CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND ? | 152 | flow == CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND ? |
144 | "REMOTE_SHUTDOWN" : "UKNOWN CTRL COMMAND"); | 153 | "REMOTE_SHUTDOWN" : "UKNOWN CTRL COMMAND"); |
145 | 154 | ||
146 | priv = container_of(layr, struct chnl_net, chnl); | 155 | |
147 | 156 | ||
148 | switch (flow) { | 157 | switch (flow) { |
149 | case CAIF_CTRLCMD_FLOW_OFF_IND: | 158 | case CAIF_CTRLCMD_FLOW_OFF_IND: |
159 | priv->flowenabled = false; | ||
160 | netif_stop_queue(priv->netdev); | ||
161 | break; | ||
150 | case CAIF_CTRLCMD_DEINIT_RSP: | 162 | case CAIF_CTRLCMD_DEINIT_RSP: |
163 | priv->state = CAIF_DISCONNECTED; | ||
164 | break; | ||
151 | case CAIF_CTRLCMD_INIT_FAIL_RSP: | 165 | case CAIF_CTRLCMD_INIT_FAIL_RSP: |
166 | priv->state = CAIF_DISCONNECTED; | ||
167 | wake_up_interruptible(&priv->netmgmt_wq); | ||
168 | break; | ||
152 | case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND: | 169 | case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND: |
153 | priv->flowenabled = false; | 170 | priv->state = CAIF_SHUTDOWN; |
154 | netif_tx_disable(priv->netdev); | 171 | netif_tx_disable(priv->netdev); |
155 | pr_warning("CAIF: %s(): done\n", __func__); | ||
156 | priv->pending_close = 1; | ||
157 | schedule_work(&close_worker); | 172 | schedule_work(&close_worker); |
158 | break; | 173 | break; |
159 | case CAIF_CTRLCMD_FLOW_ON_IND: | 174 | case CAIF_CTRLCMD_FLOW_ON_IND: |
175 | priv->flowenabled = true; | ||
176 | netif_wake_queue(priv->netdev); | ||
177 | break; | ||
160 | case CAIF_CTRLCMD_INIT_RSP: | 178 | case CAIF_CTRLCMD_INIT_RSP: |
179 | priv->state = CAIF_CONNECTED; | ||
161 | priv->flowenabled = true; | 180 | priv->flowenabled = true; |
162 | netif_wake_queue(priv->netdev); | 181 | netif_wake_queue(priv->netdev); |
163 | wake_up_interruptible(&priv->netmgmt_wq); | 182 | wake_up_interruptible(&priv->netmgmt_wq); |
@@ -194,9 +213,6 @@ static int chnl_net_start_xmit(struct sk_buff *skb, struct net_device *dev) | |||
194 | 213 | ||
195 | pkt = cfpkt_fromnative(CAIF_DIR_OUT, (void *) skb); | 214 | pkt = cfpkt_fromnative(CAIF_DIR_OUT, (void *) skb); |
196 | 215 | ||
197 | pr_debug("CAIF: %s(): transmit inst %s %d,%p\n", | ||
198 | __func__, dev->name, priv->chnl.dn->id, &priv->chnl.dn); | ||
199 | |||
200 | /* Send the packet down the stack. */ | 216 | /* Send the packet down the stack. */ |
201 | result = priv->chnl.dn->transmit(priv->chnl.dn, pkt); | 217 | result = priv->chnl.dn->transmit(priv->chnl.dn, pkt); |
202 | if (result) { | 218 | if (result) { |
@@ -217,61 +233,59 @@ static int chnl_net_open(struct net_device *dev) | |||
217 | struct chnl_net *priv = NULL; | 233 | struct chnl_net *priv = NULL; |
218 | int result = -1; | 234 | int result = -1; |
219 | ASSERT_RTNL(); | 235 | ASSERT_RTNL(); |
220 | |||
221 | priv = netdev_priv(dev); | 236 | priv = netdev_priv(dev); |
222 | pr_debug("CAIF: %s(): dev name: %s\n", __func__, priv->name); | ||
223 | |||
224 | if (!priv) { | 237 | if (!priv) { |
225 | pr_debug("CAIF: %s(): chnl_net_open: no priv\n", __func__); | 238 | pr_debug("CAIF: %s(): chnl_net_open: no priv\n", __func__); |
226 | return -ENODEV; | 239 | return -ENODEV; |
227 | } | 240 | } |
228 | result = caif_connect_client(&priv->conn_req, &priv->chnl); | 241 | |
229 | if (result != 0) { | 242 | if (priv->state != CAIF_CONNECTING) { |
230 | pr_debug("CAIF: %s(): err: " | 243 | priv->state = CAIF_CONNECTING; |
231 | "Unable to register and open device, Err:%d\n", | 244 | result = caif_connect_client(&priv->conn_req, &priv->chnl); |
232 | __func__, | 245 | if (result != 0) { |
233 | result); | 246 | priv->state = CAIF_DISCONNECTED; |
234 | return -ENODEV; | 247 | pr_debug("CAIF: %s(): err: " |
248 | "Unable to register and open device," | ||
249 | " Err:%d\n", | ||
250 | __func__, | ||
251 | result); | ||
252 | return result; | ||
253 | } | ||
235 | } | 254 | } |
236 | result = wait_event_interruptible(priv->netmgmt_wq, priv->flowenabled); | 255 | |
256 | result = wait_event_interruptible_timeout(priv->netmgmt_wq, | ||
257 | priv->state != CAIF_CONNECTING, | ||
258 | CONNECT_TIMEOUT); | ||
237 | 259 | ||
238 | if (result == -ERESTARTSYS) { | 260 | if (result == -ERESTARTSYS) { |
239 | pr_debug("CAIF: %s(): wait_event_interruptible" | 261 | pr_debug("CAIF: %s(): wait_event_interruptible" |
240 | " woken by a signal\n", __func__); | 262 | " woken by a signal\n", __func__); |
241 | return -ERESTARTSYS; | 263 | return -ERESTARTSYS; |
242 | } else | 264 | } |
243 | pr_debug("CAIF: %s(): Flow on recieved\n", __func__); | 265 | if (result == 0) { |
266 | pr_debug("CAIF: %s(): connect timeout\n", __func__); | ||
267 | caif_disconnect_client(&priv->chnl); | ||
268 | priv->state = CAIF_DISCONNECTED; | ||
269 | pr_debug("CAIF: %s(): state disconnected\n", __func__); | ||
270 | return -ETIMEDOUT; | ||
271 | } | ||
244 | 272 | ||
273 | if (priv->state != CAIF_CONNECTED) { | ||
274 | pr_debug("CAIF: %s(): connect failed\n", __func__); | ||
275 | return -ECONNREFUSED; | ||
276 | } | ||
277 | pr_debug("CAIF: %s(): CAIF Netdevice connected\n", __func__); | ||
245 | return 0; | 278 | return 0; |
246 | } | 279 | } |
247 | 280 | ||
248 | static int chnl_net_stop(struct net_device *dev) | 281 | static int chnl_net_stop(struct net_device *dev) |
249 | { | 282 | { |
250 | struct chnl_net *priv; | 283 | struct chnl_net *priv; |
251 | int result = -1; | 284 | |
252 | ASSERT_RTNL(); | 285 | ASSERT_RTNL(); |
253 | priv = netdev_priv(dev); | 286 | priv = netdev_priv(dev); |
254 | 287 | priv->state = CAIF_DISCONNECTED; | |
255 | result = caif_disconnect_client(&priv->chnl); | 288 | caif_disconnect_client(&priv->chnl); |
256 | if (result != 0) { | ||
257 | pr_debug("CAIF: %s(): chnl_net_stop: err: " | ||
258 | "Unable to STOP device, Err:%d\n", | ||
259 | __func__, result); | ||
260 | return -EBUSY; | ||
261 | } | ||
262 | result = wait_event_interruptible(priv->netmgmt_wq, | ||
263 | !priv->flowenabled); | ||
264 | |||
265 | if (result == -ERESTARTSYS) { | ||
266 | pr_debug("CAIF: %s(): wait_event_interruptible woken by" | ||
267 | " signal, signal_pending(current) = %d\n", | ||
268 | __func__, | ||
269 | signal_pending(current)); | ||
270 | } else { | ||
271 | pr_debug("CAIF: %s(): disconnect received\n", __func__); | ||
272 | |||
273 | } | ||
274 | |||
275 | return 0; | 289 | return 0; |
276 | } | 290 | } |
277 | 291 | ||
@@ -377,6 +391,8 @@ static int ipcaif_newlink(struct net *src_net, struct net_device *dev, | |||
377 | ASSERT_RTNL(); | 391 | ASSERT_RTNL(); |
378 | caifdev = netdev_priv(dev); | 392 | caifdev = netdev_priv(dev); |
379 | caif_netlink_parms(data, &caifdev->conn_req); | 393 | caif_netlink_parms(data, &caifdev->conn_req); |
394 | dev_net_set(caifdev->netdev, src_net); | ||
395 | |||
380 | ret = register_netdevice(dev); | 396 | ret = register_netdevice(dev); |
381 | if (ret) | 397 | if (ret) |
382 | pr_warning("CAIF: %s(): device rtml registration failed\n", | 398 | pr_warning("CAIF: %s(): device rtml registration failed\n", |