aboutsummaryrefslogtreecommitdiffstats
path: root/net/caif/chnl_net.c
diff options
context:
space:
mode:
authorSjur Braendeland <sjur.brandeland@stericsson.com>2010-04-28 04:54:39 -0400
committerDavid S. Miller <davem@davemloft.net>2010-04-28 15:55:14 -0400
commit8391c4aab1aa4f47a9dab2c1ec3ebd2cbf09df1b (patch)
treeb117d2d8afd3ce1711cc4206ddfd2bc02bb563ef /net/caif/chnl_net.c
parentbece7b2398d073d11b2e352405a3ecd3a1e39c60 (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/chnl_net.c')
-rw-r--r--net/caif/chnl_net.c130
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);
37MODULE_LICENSE("GPL"); 37MODULE_LICENSE("GPL");
38MODULE_ALIAS_RTNL_LINK("caif"); 38MODULE_ALIAS_RTNL_LINK("caif");
39 39
40enum caif_states {
41 CAIF_CONNECTED = 1,
42 CAIF_CONNECTING,
43 CAIF_DISCONNECTED,
44 CAIF_SHUTDOWN
45};
46
40struct chnl_net { 47struct 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
53static void robust_list_del(struct list_head *delete_node) 60static 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
66static int chnl_recv_cb(struct cflayer *layr, struct cfpkt *pkt) 74static 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}
130static DECLARE_WORK(close_worker, close_work); 139static DECLARE_WORK(close_worker, close_work);
131 140
132static void chnl_flowctrl_cb(struct cflayer *layr, enum caif_ctrlcmd flow, 141static 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
248static int chnl_net_stop(struct net_device *dev) 281static 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",