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", |
