Commit 54e90fb5ca8050156d3e748ddc690ed6ea9d71ac
Committed by
David S. Miller
1 parent
0e5a117441
Exists in
master
and in
4 other branches
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>
Showing 3 changed files with 62 additions and 44 deletions Side-by-side Diff
net/caif/caif_socket.c
... | ... | @@ -19,7 +19,7 @@ |
19 | 19 | #include <linux/uaccess.h> |
20 | 20 | #include <linux/debugfs.h> |
21 | 21 | #include <linux/caif/caif_socket.h> |
22 | -#include <asm/atomic.h> | |
22 | +#include <linux/atomic.h> | |
23 | 23 | #include <net/sock.h> |
24 | 24 | #include <net/tcp_states.h> |
25 | 25 | #include <net/caif/caif_layer.h> |
... | ... | @@ -816,6 +816,7 @@ |
816 | 816 | if (sk->sk_shutdown & SHUTDOWN_MASK) { |
817 | 817 | /* Allow re-connect after SHUTDOWN_IND */ |
818 | 818 | caif_disconnect_client(sock_net(sk), &cf_sk->layer); |
819 | + caif_free_client(&cf_sk->layer); | |
819 | 820 | break; |
820 | 821 | } |
821 | 822 | /* No reconnect on a seqpacket socket */ |
... | ... | @@ -926,7 +927,6 @@ |
926 | 927 | { |
927 | 928 | struct sock *sk = sock->sk; |
928 | 929 | struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); |
929 | - int res = 0; | |
930 | 930 | |
931 | 931 | if (!sk) |
932 | 932 | return 0; |
... | ... | @@ -953,10 +953,7 @@ |
953 | 953 | sk->sk_state = CAIF_DISCONNECTED; |
954 | 954 | sk->sk_shutdown = SHUTDOWN_MASK; |
955 | 955 | |
956 | - if (cf_sk->sk.sk_socket->state == SS_CONNECTED || | |
957 | - cf_sk->sk.sk_socket->state == SS_CONNECTING) | |
958 | - res = caif_disconnect_client(sock_net(sk), &cf_sk->layer); | |
959 | - | |
956 | + caif_disconnect_client(sock_net(sk), &cf_sk->layer); | |
960 | 957 | cf_sk->sk.sk_socket->state = SS_DISCONNECTING; |
961 | 958 | wake_up_interruptible_poll(sk_sleep(sk), POLLERR|POLLHUP); |
962 | 959 | |
... | ... | @@ -964,7 +961,7 @@ |
964 | 961 | sk_stream_kill_queues(&cf_sk->sk); |
965 | 962 | release_sock(sk); |
966 | 963 | sock_put(sk); |
967 | - return res; | |
964 | + return 0; | |
968 | 965 | } |
969 | 966 | |
970 | 967 | /* Copied from af_unix.c:unix_poll(), added CAIF tx_flow handling */ |
... | ... | @@ -1120,7 +1117,7 @@ |
1120 | 1117 | set_rx_flow_on(cf_sk); |
1121 | 1118 | |
1122 | 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 | 1121 | cf_sk->conn_req.link_selector = CAIF_LINK_LOW_LATENCY; |
1125 | 1122 | cf_sk->conn_req.protocol = protocol; |
1126 | 1123 | /* Increase the number of sockets created. */ |
net/caif/cfcnfg.c
... | ... | @@ -182,39 +182,26 @@ |
182 | 182 | |
183 | 183 | int caif_disconnect_client(struct net *net, struct cflayer *adap_layer) |
184 | 184 | { |
185 | - u8 channel_id = 0; | |
186 | - int ret = 0; | |
187 | - struct cflayer *servl = NULL; | |
185 | + u8 channel_id; | |
188 | 186 | struct cfcnfg *cfg = get_cfcnfg(net); |
189 | 187 | |
190 | 188 | caif_assert(adap_layer != NULL); |
191 | - | |
189 | + cfctrl_cancel_req(cfg->ctrl, adap_layer); | |
192 | 190 | 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 | - } | |
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); | |
198 | 199 | |
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); | |
212 | - | |
213 | 200 | /* Do RCU sync before initiating cleanup */ |
214 | 201 | synchronize_rcu(); |
215 | 202 | if (adap_layer->ctrlcmd != NULL) |
216 | 203 | adap_layer->ctrlcmd(adap_layer, CAIF_CTRLCMD_DEINIT_RSP, 0); |
217 | - return ret; | |
204 | + return 0; | |
218 | 205 | |
219 | 206 | } |
220 | 207 | EXPORT_SYMBOL(caif_disconnect_client); |
... | ... | @@ -400,6 +387,14 @@ |
400 | 387 | struct cfcnfg_phyinfo *phyinfo; |
401 | 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 | 398 | rcu_read_lock(); |
404 | 399 | |
405 | 400 | if (adapt_layer == NULL) { |
... | ... | @@ -523,7 +518,6 @@ |
523 | 518 | phyinfo->use_stx = stx; |
524 | 519 | phyinfo->use_fcs = fcs; |
525 | 520 | |
526 | - phy_layer->type = phy_type; | |
527 | 521 | frml = cffrml_create(phyid, fcs); |
528 | 522 | |
529 | 523 | if (!frml) { |
net/caif/cfmuxl.c
... | ... | @@ -62,16 +62,6 @@ |
62 | 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 | 65 | int cfmuxl_set_dnlayer(struct cflayer *layr, struct cflayer *dn, u8 phyid) |
76 | 66 | { |
77 | 67 | struct cfmuxl *muxl = (struct cfmuxl *) layr; |
... | ... | @@ -93,6 +83,24 @@ |
93 | 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 | 104 | struct cflayer *cfmuxl_remove_dnlayer(struct cflayer *layr, u8 phyid) |
97 | 105 | { |
98 | 106 | struct cfmuxl *muxl = container_obj(layr); |
... | ... | @@ -146,6 +154,11 @@ |
146 | 154 | struct cfmuxl *muxl = container_obj(layr); |
147 | 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 | 162 | spin_lock_bh(&muxl->receive_lock); |
150 | 163 | up = get_from_id(&muxl->srvl_list, id); |
151 | 164 | if (up == NULL) |
152 | 165 | |
153 | 166 | |
... | ... | @@ -235,12 +248,26 @@ |
235 | 248 | { |
236 | 249 | struct cfmuxl *muxl = container_obj(layr); |
237 | 250 | struct cflayer *layer; |
251 | + int idx; | |
238 | 252 | |
239 | 253 | rcu_read_lock(); |
240 | 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 | 268 | /* NOTE: ctrlcmd is not allowed to block */ |
243 | 269 | layer->ctrlcmd(layer, ctrl, phyid); |
270 | + } | |
244 | 271 | } |
245 | 272 | rcu_read_unlock(); |
246 | 273 | } |