diff options
author | sjur.brandeland@stericsson.com <sjur.brandeland@stericsson.com> | 2011-05-22 07:18:51 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-05-22 20:11:47 -0400 |
commit | 54e90fb5ca8050156d3e748ddc690ed6ea9d71ac (patch) | |
tree | 63a4d1a736a7c44cc88b69e41dec9c4885ef2f74 /net | |
parent | 0e5a117441ce245b87949cc7713627a293f37227 (diff) |
caif: Fixes freeze on Link layer removal.
CAIF Socket layer - caif_socket.c:
- Plug mem-leak at reconnect.
- Always call disconnect to cleanup CAIF stack.
- Disconnect will always report success.
CAIF configuration layer - cfcnfg.c
- Disconnect must dismantle the caif stack correctly
- Protect against faulty removals (check on id zero)
CAIF mux layer - cfmuxl.c
- When inserting new service layer in the MUX remove
any old entries with the same ID.
- When removing CAIF Link layer, remove the associated
service layers before notifying service layers.
Signed-off-by: Sjur Brændeland <sjur.brandeland@stericsson.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/caif/caif_socket.c | 13 | ||||
-rw-r--r-- | net/caif/cfcnfg.c | 44 | ||||
-rw-r--r-- | net/caif/cfmuxl.c | 49 |
3 files changed, 62 insertions, 44 deletions
diff --git a/net/caif/caif_socket.c b/net/caif/caif_socket.c index b840395ced1d..a98628086452 100644 --- a/net/caif/caif_socket.c +++ b/net/caif/caif_socket.c | |||
@@ -19,7 +19,7 @@ | |||
19 | #include <linux/uaccess.h> | 19 | #include <linux/uaccess.h> |
20 | #include <linux/debugfs.h> | 20 | #include <linux/debugfs.h> |
21 | #include <linux/caif/caif_socket.h> | 21 | #include <linux/caif/caif_socket.h> |
22 | #include <asm/atomic.h> | 22 | #include <linux/atomic.h> |
23 | #include <net/sock.h> | 23 | #include <net/sock.h> |
24 | #include <net/tcp_states.h> | 24 | #include <net/tcp_states.h> |
25 | #include <net/caif/caif_layer.h> | 25 | #include <net/caif/caif_layer.h> |
@@ -816,6 +816,7 @@ static int caif_connect(struct socket *sock, struct sockaddr *uaddr, | |||
816 | if (sk->sk_shutdown & SHUTDOWN_MASK) { | 816 | if (sk->sk_shutdown & SHUTDOWN_MASK) { |
817 | /* Allow re-connect after SHUTDOWN_IND */ | 817 | /* Allow re-connect after SHUTDOWN_IND */ |
818 | caif_disconnect_client(sock_net(sk), &cf_sk->layer); | 818 | caif_disconnect_client(sock_net(sk), &cf_sk->layer); |
819 | caif_free_client(&cf_sk->layer); | ||
819 | break; | 820 | break; |
820 | } | 821 | } |
821 | /* No reconnect on a seqpacket socket */ | 822 | /* No reconnect on a seqpacket socket */ |
@@ -926,7 +927,6 @@ static int caif_release(struct socket *sock) | |||
926 | { | 927 | { |
927 | struct sock *sk = sock->sk; | 928 | struct sock *sk = sock->sk; |
928 | struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); | 929 | struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); |
929 | int res = 0; | ||
930 | 930 | ||
931 | if (!sk) | 931 | if (!sk) |
932 | return 0; | 932 | return 0; |
@@ -953,10 +953,7 @@ static int caif_release(struct socket *sock) | |||
953 | sk->sk_state = CAIF_DISCONNECTED; | 953 | sk->sk_state = CAIF_DISCONNECTED; |
954 | sk->sk_shutdown = SHUTDOWN_MASK; | 954 | sk->sk_shutdown = SHUTDOWN_MASK; |
955 | 955 | ||
956 | if (cf_sk->sk.sk_socket->state == SS_CONNECTED || | 956 | caif_disconnect_client(sock_net(sk), &cf_sk->layer); |
957 | cf_sk->sk.sk_socket->state == SS_CONNECTING) | ||
958 | res = caif_disconnect_client(sock_net(sk), &cf_sk->layer); | ||
959 | |||
960 | cf_sk->sk.sk_socket->state = SS_DISCONNECTING; | 957 | cf_sk->sk.sk_socket->state = SS_DISCONNECTING; |
961 | wake_up_interruptible_poll(sk_sleep(sk), POLLERR|POLLHUP); | 958 | wake_up_interruptible_poll(sk_sleep(sk), POLLERR|POLLHUP); |
962 | 959 | ||
@@ -964,7 +961,7 @@ static int caif_release(struct socket *sock) | |||
964 | sk_stream_kill_queues(&cf_sk->sk); | 961 | sk_stream_kill_queues(&cf_sk->sk); |
965 | release_sock(sk); | 962 | release_sock(sk); |
966 | sock_put(sk); | 963 | sock_put(sk); |
967 | return res; | 964 | return 0; |
968 | } | 965 | } |
969 | 966 | ||
970 | /* Copied from af_unix.c:unix_poll(), added CAIF tx_flow handling */ | 967 | /* Copied from af_unix.c:unix_poll(), added CAIF tx_flow handling */ |
@@ -1120,7 +1117,7 @@ static int caif_create(struct net *net, struct socket *sock, int protocol, | |||
1120 | set_rx_flow_on(cf_sk); | 1117 | set_rx_flow_on(cf_sk); |
1121 | 1118 | ||
1122 | /* Set default options on configuration */ | 1119 | /* Set default options on configuration */ |
1123 | cf_sk->sk.sk_priority= CAIF_PRIO_NORMAL; | 1120 | cf_sk->sk.sk_priority = CAIF_PRIO_NORMAL; |
1124 | cf_sk->conn_req.link_selector = CAIF_LINK_LOW_LATENCY; | 1121 | cf_sk->conn_req.link_selector = CAIF_LINK_LOW_LATENCY; |
1125 | cf_sk->conn_req.protocol = protocol; | 1122 | cf_sk->conn_req.protocol = protocol; |
1126 | /* Increase the number of sockets created. */ | 1123 | /* Increase the number of sockets created. */ |
diff --git a/net/caif/cfcnfg.c b/net/caif/cfcnfg.c index 351c2ca7e7b9..52fe33bee029 100644 --- a/net/caif/cfcnfg.c +++ b/net/caif/cfcnfg.c | |||
@@ -182,39 +182,26 @@ static int cfcnfg_get_id_from_ifi(struct cfcnfg *cnfg, int ifi) | |||
182 | 182 | ||
183 | int caif_disconnect_client(struct net *net, struct cflayer *adap_layer) | 183 | int caif_disconnect_client(struct net *net, struct cflayer *adap_layer) |
184 | { | 184 | { |
185 | u8 channel_id = 0; | 185 | u8 channel_id; |
186 | int ret = 0; | ||
187 | struct cflayer *servl = NULL; | ||
188 | struct cfcnfg *cfg = get_cfcnfg(net); | 186 | struct cfcnfg *cfg = get_cfcnfg(net); |
189 | 187 | ||
190 | caif_assert(adap_layer != NULL); | 188 | caif_assert(adap_layer != NULL); |
191 | |||
192 | channel_id = adap_layer->id; | ||
193 | if (adap_layer->dn == NULL || channel_id == 0) { | ||
194 | pr_err("adap_layer->dn == NULL or adap_layer->id is 0\n"); | ||
195 | ret = -ENOTCONN; | ||
196 | goto end; | ||
197 | } | ||
198 | |||
199 | servl = cfmuxl_remove_uplayer(cfg->mux, channel_id); | ||
200 | if (servl == NULL) { | ||
201 | pr_err("PROTOCOL ERROR - " | ||
202 | "Error removing service_layer Channel_Id(%d)", | ||
203 | channel_id); | ||
204 | ret = -EINVAL; | ||
205 | goto end; | ||
206 | } | ||
207 | |||
208 | ret = cfctrl_linkdown_req(cfg->ctrl, channel_id, adap_layer); | ||
209 | |||
210 | end: | ||
211 | cfctrl_cancel_req(cfg->ctrl, adap_layer); | 189 | cfctrl_cancel_req(cfg->ctrl, adap_layer); |
190 | channel_id = adap_layer->id; | ||
191 | if (channel_id != 0) { | ||
192 | struct cflayer *servl; | ||
193 | servl = cfmuxl_remove_uplayer(cfg->mux, channel_id); | ||
194 | if (servl != NULL) | ||
195 | layer_set_up(servl, NULL); | ||
196 | } else | ||
197 | pr_debug("nothing to disconnect\n"); | ||
198 | cfctrl_linkdown_req(cfg->ctrl, channel_id, adap_layer); | ||
212 | 199 | ||
213 | /* Do RCU sync before initiating cleanup */ | 200 | /* Do RCU sync before initiating cleanup */ |
214 | synchronize_rcu(); | 201 | synchronize_rcu(); |
215 | if (adap_layer->ctrlcmd != NULL) | 202 | if (adap_layer->ctrlcmd != NULL) |
216 | adap_layer->ctrlcmd(adap_layer, CAIF_CTRLCMD_DEINIT_RSP, 0); | 203 | adap_layer->ctrlcmd(adap_layer, CAIF_CTRLCMD_DEINIT_RSP, 0); |
217 | return ret; | 204 | return 0; |
218 | 205 | ||
219 | } | 206 | } |
220 | EXPORT_SYMBOL(caif_disconnect_client); | 207 | EXPORT_SYMBOL(caif_disconnect_client); |
@@ -400,6 +387,14 @@ cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv, | |||
400 | struct cfcnfg_phyinfo *phyinfo; | 387 | struct cfcnfg_phyinfo *phyinfo; |
401 | struct net_device *netdev; | 388 | struct net_device *netdev; |
402 | 389 | ||
390 | if (channel_id == 0) { | ||
391 | pr_warn("received channel_id zero\n"); | ||
392 | if (adapt_layer != NULL && adapt_layer->ctrlcmd != NULL) | ||
393 | adapt_layer->ctrlcmd(adapt_layer, | ||
394 | CAIF_CTRLCMD_INIT_FAIL_RSP, 0); | ||
395 | return; | ||
396 | } | ||
397 | |||
403 | rcu_read_lock(); | 398 | rcu_read_lock(); |
404 | 399 | ||
405 | if (adapt_layer == NULL) { | 400 | if (adapt_layer == NULL) { |
@@ -523,7 +518,6 @@ got_phyid: | |||
523 | phyinfo->use_stx = stx; | 518 | phyinfo->use_stx = stx; |
524 | phyinfo->use_fcs = fcs; | 519 | phyinfo->use_fcs = fcs; |
525 | 520 | ||
526 | phy_layer->type = phy_type; | ||
527 | frml = cffrml_create(phyid, fcs); | 521 | frml = cffrml_create(phyid, fcs); |
528 | 522 | ||
529 | if (!frml) { | 523 | if (!frml) { |
diff --git a/net/caif/cfmuxl.c b/net/caif/cfmuxl.c index 2a56df7e0a4b..3a66b8c10e09 100644 --- a/net/caif/cfmuxl.c +++ b/net/caif/cfmuxl.c | |||
@@ -62,16 +62,6 @@ struct cflayer *cfmuxl_create(void) | |||
62 | return &this->layer; | 62 | return &this->layer; |
63 | } | 63 | } |
64 | 64 | ||
65 | int cfmuxl_set_uplayer(struct cflayer *layr, struct cflayer *up, u8 linkid) | ||
66 | { | ||
67 | struct cfmuxl *muxl = container_obj(layr); | ||
68 | |||
69 | spin_lock_bh(&muxl->receive_lock); | ||
70 | list_add_rcu(&up->node, &muxl->srvl_list); | ||
71 | spin_unlock_bh(&muxl->receive_lock); | ||
72 | return 0; | ||
73 | } | ||
74 | |||
75 | int cfmuxl_set_dnlayer(struct cflayer *layr, struct cflayer *dn, u8 phyid) | 65 | int cfmuxl_set_dnlayer(struct cflayer *layr, struct cflayer *dn, u8 phyid) |
76 | { | 66 | { |
77 | struct cfmuxl *muxl = (struct cfmuxl *) layr; | 67 | struct cfmuxl *muxl = (struct cfmuxl *) layr; |
@@ -93,6 +83,24 @@ static struct cflayer *get_from_id(struct list_head *list, u16 id) | |||
93 | return NULL; | 83 | return NULL; |
94 | } | 84 | } |
95 | 85 | ||
86 | int cfmuxl_set_uplayer(struct cflayer *layr, struct cflayer *up, u8 linkid) | ||
87 | { | ||
88 | struct cfmuxl *muxl = container_obj(layr); | ||
89 | struct cflayer *old; | ||
90 | |||
91 | spin_lock_bh(&muxl->receive_lock); | ||
92 | |||
93 | /* Two entries with same id is wrong, so remove old layer from mux */ | ||
94 | old = get_from_id(&muxl->srvl_list, linkid); | ||
95 | if (old != NULL) | ||
96 | list_del_rcu(&old->node); | ||
97 | |||
98 | list_add_rcu(&up->node, &muxl->srvl_list); | ||
99 | spin_unlock_bh(&muxl->receive_lock); | ||
100 | |||
101 | return 0; | ||
102 | } | ||
103 | |||
96 | struct cflayer *cfmuxl_remove_dnlayer(struct cflayer *layr, u8 phyid) | 104 | struct cflayer *cfmuxl_remove_dnlayer(struct cflayer *layr, u8 phyid) |
97 | { | 105 | { |
98 | struct cfmuxl *muxl = container_obj(layr); | 106 | struct cfmuxl *muxl = container_obj(layr); |
@@ -146,6 +154,11 @@ struct cflayer *cfmuxl_remove_uplayer(struct cflayer *layr, u8 id) | |||
146 | struct cfmuxl *muxl = container_obj(layr); | 154 | struct cfmuxl *muxl = container_obj(layr); |
147 | int idx = id % UP_CACHE_SIZE; | 155 | int idx = id % UP_CACHE_SIZE; |
148 | 156 | ||
157 | if (id == 0) { | ||
158 | pr_warn("Trying to remove control layer\n"); | ||
159 | return NULL; | ||
160 | } | ||
161 | |||
149 | spin_lock_bh(&muxl->receive_lock); | 162 | spin_lock_bh(&muxl->receive_lock); |
150 | up = get_from_id(&muxl->srvl_list, id); | 163 | up = get_from_id(&muxl->srvl_list, id); |
151 | if (up == NULL) | 164 | if (up == NULL) |
@@ -235,12 +248,26 @@ static void cfmuxl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, | |||
235 | { | 248 | { |
236 | struct cfmuxl *muxl = container_obj(layr); | 249 | struct cfmuxl *muxl = container_obj(layr); |
237 | struct cflayer *layer; | 250 | struct cflayer *layer; |
251 | int idx; | ||
238 | 252 | ||
239 | rcu_read_lock(); | 253 | rcu_read_lock(); |
240 | list_for_each_entry_rcu(layer, &muxl->srvl_list, node) { | 254 | list_for_each_entry_rcu(layer, &muxl->srvl_list, node) { |
241 | if (cfsrvl_phyid_match(layer, phyid) && layer->ctrlcmd) | 255 | |
256 | if (cfsrvl_phyid_match(layer, phyid) && layer->ctrlcmd) { | ||
257 | |||
258 | if ((ctrl == _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND || | ||
259 | ctrl == CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND) && | ||
260 | layer->id != 0) { | ||
261 | |||
262 | idx = layer->id % UP_CACHE_SIZE; | ||
263 | spin_lock_bh(&muxl->receive_lock); | ||
264 | rcu_assign_pointer(muxl->up_cache[idx], NULL); | ||
265 | list_del_rcu(&layer->node); | ||
266 | spin_unlock_bh(&muxl->receive_lock); | ||
267 | } | ||
242 | /* NOTE: ctrlcmd is not allowed to block */ | 268 | /* NOTE: ctrlcmd is not allowed to block */ |
243 | layer->ctrlcmd(layer, ctrl, phyid); | 269 | layer->ctrlcmd(layer, ctrl, phyid); |
270 | } | ||
244 | } | 271 | } |
245 | rcu_read_unlock(); | 272 | rcu_read_unlock(); |
246 | } | 273 | } |