diff options
author | sjur.brandeland@stericsson.com <sjur.brandeland@stericsson.com> | 2011-05-12 22:44:06 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-05-15 17:45:56 -0400 |
commit | c85c2951d4da1236e32f1858db418221e624aba5 (patch) | |
tree | 8f70d7ab3dbe05ea6f812f9bfb8d341425a29193 /net/caif/cfctrl.c | |
parent | bee925db9a77a5736596dcf6f91d0879f5ee915b (diff) |
caif: Handle dev_queue_xmit errors.
Do proper handling of dev_queue_xmit errors in order to
avoid double free of skb and leaks in error conditions.
In cfctrl pending requests are removed when CAIF Link layer goes down.
Signed-off-by: Sjur Brændeland <sjur.brandeland@stericsson.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/caif/cfctrl.c')
-rw-r--r-- | net/caif/cfctrl.c | 121 |
1 files changed, 85 insertions, 36 deletions
diff --git a/net/caif/cfctrl.c b/net/caif/cfctrl.c index 397a2c099e2c..0c00a6015dda 100644 --- a/net/caif/cfctrl.c +++ b/net/caif/cfctrl.c | |||
@@ -17,7 +17,6 @@ | |||
17 | #define UTILITY_NAME_LENGTH 16 | 17 | #define UTILITY_NAME_LENGTH 16 |
18 | #define CFPKT_CTRL_PKT_LEN 20 | 18 | #define CFPKT_CTRL_PKT_LEN 20 |
19 | 19 | ||
20 | |||
21 | #ifdef CAIF_NO_LOOP | 20 | #ifdef CAIF_NO_LOOP |
22 | static int handle_loop(struct cfctrl *ctrl, | 21 | static int handle_loop(struct cfctrl *ctrl, |
23 | int cmd, struct cfpkt *pkt){ | 22 | int cmd, struct cfpkt *pkt){ |
@@ -51,13 +50,29 @@ struct cflayer *cfctrl_create(void) | |||
51 | this->serv.layer.receive = cfctrl_recv; | 50 | this->serv.layer.receive = cfctrl_recv; |
52 | sprintf(this->serv.layer.name, "ctrl"); | 51 | sprintf(this->serv.layer.name, "ctrl"); |
53 | this->serv.layer.ctrlcmd = cfctrl_ctrlcmd; | 52 | this->serv.layer.ctrlcmd = cfctrl_ctrlcmd; |
53 | #ifndef CAIF_NO_LOOP | ||
54 | spin_lock_init(&this->loop_linkid_lock); | 54 | spin_lock_init(&this->loop_linkid_lock); |
55 | this->loop_linkid = 1; | ||
56 | #endif | ||
55 | spin_lock_init(&this->info_list_lock); | 57 | spin_lock_init(&this->info_list_lock); |
56 | INIT_LIST_HEAD(&this->list); | 58 | INIT_LIST_HEAD(&this->list); |
57 | this->loop_linkid = 1; | ||
58 | return &this->serv.layer; | 59 | return &this->serv.layer; |
59 | } | 60 | } |
60 | 61 | ||
62 | void cfctrl_remove(struct cflayer *layer) | ||
63 | { | ||
64 | struct cfctrl_request_info *p, *tmp; | ||
65 | struct cfctrl *ctrl = container_obj(layer); | ||
66 | |||
67 | spin_lock_bh(&ctrl->info_list_lock); | ||
68 | list_for_each_entry_safe(p, tmp, &ctrl->list, list) { | ||
69 | list_del(&p->list); | ||
70 | kfree(p); | ||
71 | } | ||
72 | spin_unlock_bh(&ctrl->info_list_lock); | ||
73 | kfree(layer); | ||
74 | } | ||
75 | |||
61 | static bool param_eq(const struct cfctrl_link_param *p1, | 76 | static bool param_eq(const struct cfctrl_link_param *p1, |
62 | const struct cfctrl_link_param *p2) | 77 | const struct cfctrl_link_param *p2) |
63 | { | 78 | { |
@@ -116,11 +131,11 @@ static bool cfctrl_req_eq(const struct cfctrl_request_info *r1, | |||
116 | static void cfctrl_insert_req(struct cfctrl *ctrl, | 131 | static void cfctrl_insert_req(struct cfctrl *ctrl, |
117 | struct cfctrl_request_info *req) | 132 | struct cfctrl_request_info *req) |
118 | { | 133 | { |
119 | spin_lock(&ctrl->info_list_lock); | 134 | spin_lock_bh(&ctrl->info_list_lock); |
120 | atomic_inc(&ctrl->req_seq_no); | 135 | atomic_inc(&ctrl->req_seq_no); |
121 | req->sequence_no = atomic_read(&ctrl->req_seq_no); | 136 | req->sequence_no = atomic_read(&ctrl->req_seq_no); |
122 | list_add_tail(&req->list, &ctrl->list); | 137 | list_add_tail(&req->list, &ctrl->list); |
123 | spin_unlock(&ctrl->info_list_lock); | 138 | spin_unlock_bh(&ctrl->info_list_lock); |
124 | } | 139 | } |
125 | 140 | ||
126 | /* Compare and remove request */ | 141 | /* Compare and remove request */ |
@@ -129,7 +144,6 @@ static struct cfctrl_request_info *cfctrl_remove_req(struct cfctrl *ctrl, | |||
129 | { | 144 | { |
130 | struct cfctrl_request_info *p, *tmp, *first; | 145 | struct cfctrl_request_info *p, *tmp, *first; |
131 | 146 | ||
132 | spin_lock(&ctrl->info_list_lock); | ||
133 | first = list_first_entry(&ctrl->list, struct cfctrl_request_info, list); | 147 | first = list_first_entry(&ctrl->list, struct cfctrl_request_info, list); |
134 | 148 | ||
135 | list_for_each_entry_safe(p, tmp, &ctrl->list, list) { | 149 | list_for_each_entry_safe(p, tmp, &ctrl->list, list) { |
@@ -145,7 +159,6 @@ static struct cfctrl_request_info *cfctrl_remove_req(struct cfctrl *ctrl, | |||
145 | } | 159 | } |
146 | p = NULL; | 160 | p = NULL; |
147 | out: | 161 | out: |
148 | spin_unlock(&ctrl->info_list_lock); | ||
149 | return p; | 162 | return p; |
150 | } | 163 | } |
151 | 164 | ||
@@ -179,10 +192,6 @@ void cfctrl_enum_req(struct cflayer *layer, u8 physlinkid) | |||
179 | cfpkt_addbdy(pkt, physlinkid); | 192 | cfpkt_addbdy(pkt, physlinkid); |
180 | ret = | 193 | ret = |
181 | cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt); | 194 | cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt); |
182 | if (ret < 0) { | ||
183 | pr_err("Could not transmit enum message\n"); | ||
184 | cfpkt_destroy(pkt); | ||
185 | } | ||
186 | } | 195 | } |
187 | 196 | ||
188 | int cfctrl_linkup_request(struct cflayer *layer, | 197 | int cfctrl_linkup_request(struct cflayer *layer, |
@@ -196,14 +205,23 @@ int cfctrl_linkup_request(struct cflayer *layer, | |||
196 | struct cfctrl_request_info *req; | 205 | struct cfctrl_request_info *req; |
197 | int ret; | 206 | int ret; |
198 | char utility_name[16]; | 207 | char utility_name[16]; |
199 | struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN); | 208 | struct cfpkt *pkt; |
209 | |||
210 | if (cfctrl_cancel_req(layer, user_layer) > 0) { | ||
211 | /* Slight Paranoia, check if already connecting */ | ||
212 | pr_err("Duplicate connect request for same client\n"); | ||
213 | WARN_ON(1); | ||
214 | return -EALREADY; | ||
215 | } | ||
216 | |||
217 | pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN); | ||
200 | if (!pkt) { | 218 | if (!pkt) { |
201 | pr_warn("Out of memory\n"); | 219 | pr_warn("Out of memory\n"); |
202 | return -ENOMEM; | 220 | return -ENOMEM; |
203 | } | 221 | } |
204 | cfpkt_addbdy(pkt, CFCTRL_CMD_LINK_SETUP); | 222 | cfpkt_addbdy(pkt, CFCTRL_CMD_LINK_SETUP); |
205 | cfpkt_addbdy(pkt, (param->chtype << 4) + param->linktype); | 223 | cfpkt_addbdy(pkt, (param->chtype << 4) | param->linktype); |
206 | cfpkt_addbdy(pkt, (param->priority << 3) + param->phyid); | 224 | cfpkt_addbdy(pkt, (param->priority << 3) | param->phyid); |
207 | cfpkt_addbdy(pkt, param->endpoint & 0x03); | 225 | cfpkt_addbdy(pkt, param->endpoint & 0x03); |
208 | 226 | ||
209 | switch (param->linktype) { | 227 | switch (param->linktype) { |
@@ -266,9 +284,13 @@ int cfctrl_linkup_request(struct cflayer *layer, | |||
266 | ret = | 284 | ret = |
267 | cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt); | 285 | cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt); |
268 | if (ret < 0) { | 286 | if (ret < 0) { |
269 | pr_err("Could not transmit linksetup request\n"); | 287 | int count; |
270 | cfpkt_destroy(pkt); | 288 | |
271 | return -ENODEV; | 289 | count = cfctrl_cancel_req(&cfctrl->serv.layer, |
290 | user_layer); | ||
291 | if (count != 1) | ||
292 | pr_err("Could not remove request (%d)", count); | ||
293 | return -ENODEV; | ||
272 | } | 294 | } |
273 | return 0; | 295 | return 0; |
274 | } | 296 | } |
@@ -288,28 +310,29 @@ int cfctrl_linkdown_req(struct cflayer *layer, u8 channelid, | |||
288 | init_info(cfpkt_info(pkt), cfctrl); | 310 | init_info(cfpkt_info(pkt), cfctrl); |
289 | ret = | 311 | ret = |
290 | cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt); | 312 | cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt); |
291 | if (ret < 0) { | 313 | #ifndef CAIF_NO_LOOP |
292 | pr_err("Could not transmit link-down request\n"); | 314 | cfctrl->loop_linkused[channelid] = 0; |
293 | cfpkt_destroy(pkt); | 315 | #endif |
294 | } | ||
295 | return ret; | 316 | return ret; |
296 | } | 317 | } |
297 | 318 | ||
298 | void cfctrl_cancel_req(struct cflayer *layr, struct cflayer *adap_layer) | 319 | int cfctrl_cancel_req(struct cflayer *layr, struct cflayer *adap_layer) |
299 | { | 320 | { |
300 | struct cfctrl_request_info *p, *tmp; | 321 | struct cfctrl_request_info *p, *tmp; |
301 | struct cfctrl *ctrl = container_obj(layr); | 322 | struct cfctrl *ctrl = container_obj(layr); |
302 | spin_lock(&ctrl->info_list_lock); | 323 | int found = 0; |
324 | spin_lock_bh(&ctrl->info_list_lock); | ||
303 | 325 | ||
304 | list_for_each_entry_safe(p, tmp, &ctrl->list, list) { | 326 | list_for_each_entry_safe(p, tmp, &ctrl->list, list) { |
305 | if (p->client_layer == adap_layer) { | 327 | if (p->client_layer == adap_layer) { |
306 | pr_debug("cancel req :%d\n", p->sequence_no); | ||
307 | list_del(&p->list); | 328 | list_del(&p->list); |
308 | kfree(p); | 329 | kfree(p); |
330 | found++; | ||
309 | } | 331 | } |
310 | } | 332 | } |
311 | 333 | ||
312 | spin_unlock(&ctrl->info_list_lock); | 334 | spin_unlock_bh(&ctrl->info_list_lock); |
335 | return found; | ||
313 | } | 336 | } |
314 | 337 | ||
315 | static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt) | 338 | static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt) |
@@ -461,6 +484,7 @@ static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt) | |||
461 | 484 | ||
462 | rsp.cmd = cmd; | 485 | rsp.cmd = cmd; |
463 | rsp.param = linkparam; | 486 | rsp.param = linkparam; |
487 | spin_lock_bh(&cfctrl->info_list_lock); | ||
464 | req = cfctrl_remove_req(cfctrl, &rsp); | 488 | req = cfctrl_remove_req(cfctrl, &rsp); |
465 | 489 | ||
466 | if (CFCTRL_ERR_BIT == (CFCTRL_ERR_BIT & cmdrsp) || | 490 | if (CFCTRL_ERR_BIT == (CFCTRL_ERR_BIT & cmdrsp) || |
@@ -480,6 +504,8 @@ static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt) | |||
480 | 504 | ||
481 | if (req != NULL) | 505 | if (req != NULL) |
482 | kfree(req); | 506 | kfree(req); |
507 | |||
508 | spin_unlock_bh(&cfctrl->info_list_lock); | ||
483 | } | 509 | } |
484 | break; | 510 | break; |
485 | case CFCTRL_CMD_LINK_DESTROY: | 511 | case CFCTRL_CMD_LINK_DESTROY: |
@@ -523,12 +549,29 @@ static void cfctrl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, | |||
523 | switch (ctrl) { | 549 | switch (ctrl) { |
524 | case _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND: | 550 | case _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND: |
525 | case CAIF_CTRLCMD_FLOW_OFF_IND: | 551 | case CAIF_CTRLCMD_FLOW_OFF_IND: |
526 | spin_lock(&this->info_list_lock); | 552 | spin_lock_bh(&this->info_list_lock); |
527 | if (!list_empty(&this->list)) { | 553 | if (!list_empty(&this->list)) { |
528 | pr_debug("Received flow off in control layer\n"); | 554 | pr_debug("Received flow off in control layer\n"); |
529 | } | 555 | } |
530 | spin_unlock(&this->info_list_lock); | 556 | spin_unlock_bh(&this->info_list_lock); |
531 | break; | 557 | break; |
558 | case _CAIF_CTRLCMD_PHYIF_DOWN_IND: { | ||
559 | struct cfctrl_request_info *p, *tmp; | ||
560 | |||
561 | /* Find all connect request and report failure */ | ||
562 | spin_lock_bh(&this->info_list_lock); | ||
563 | list_for_each_entry_safe(p, tmp, &this->list, list) { | ||
564 | if (p->param.phyid == phyid) { | ||
565 | list_del(&p->list); | ||
566 | p->client_layer->ctrlcmd(p->client_layer, | ||
567 | CAIF_CTRLCMD_INIT_FAIL_RSP, | ||
568 | phyid); | ||
569 | kfree(p); | ||
570 | } | ||
571 | } | ||
572 | spin_unlock_bh(&this->info_list_lock); | ||
573 | break; | ||
574 | } | ||
532 | default: | 575 | default: |
533 | break; | 576 | break; |
534 | } | 577 | } |
@@ -538,27 +581,33 @@ static void cfctrl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, | |||
538 | static int handle_loop(struct cfctrl *ctrl, int cmd, struct cfpkt *pkt) | 581 | static int handle_loop(struct cfctrl *ctrl, int cmd, struct cfpkt *pkt) |
539 | { | 582 | { |
540 | static int last_linkid; | 583 | static int last_linkid; |
584 | static int dec; | ||
541 | u8 linkid, linktype, tmp; | 585 | u8 linkid, linktype, tmp; |
542 | switch (cmd) { | 586 | switch (cmd) { |
543 | case CFCTRL_CMD_LINK_SETUP: | 587 | case CFCTRL_CMD_LINK_SETUP: |
544 | spin_lock(&ctrl->loop_linkid_lock); | 588 | spin_lock_bh(&ctrl->loop_linkid_lock); |
545 | for (linkid = last_linkid + 1; linkid < 255; linkid++) | 589 | if (!dec) { |
546 | if (!ctrl->loop_linkused[linkid]) | 590 | for (linkid = last_linkid + 1; linkid < 255; linkid++) |
547 | goto found; | 591 | if (!ctrl->loop_linkused[linkid]) |
592 | goto found; | ||
593 | } | ||
594 | dec = 1; | ||
548 | for (linkid = last_linkid - 1; linkid > 0; linkid--) | 595 | for (linkid = last_linkid - 1; linkid > 0; linkid--) |
549 | if (!ctrl->loop_linkused[linkid]) | 596 | if (!ctrl->loop_linkused[linkid]) |
550 | goto found; | 597 | goto found; |
551 | spin_unlock(&ctrl->loop_linkid_lock); | 598 | spin_unlock_bh(&ctrl->loop_linkid_lock); |
552 | pr_err("Out of link-ids\n"); | 599 | |
553 | return -EINVAL; | ||
554 | found: | 600 | found: |
601 | if (linkid < 10) | ||
602 | dec = 0; | ||
603 | |||
555 | if (!ctrl->loop_linkused[linkid]) | 604 | if (!ctrl->loop_linkused[linkid]) |
556 | ctrl->loop_linkused[linkid] = 1; | 605 | ctrl->loop_linkused[linkid] = 1; |
557 | 606 | ||
558 | last_linkid = linkid; | 607 | last_linkid = linkid; |
559 | 608 | ||
560 | cfpkt_add_trail(pkt, &linkid, 1); | 609 | cfpkt_add_trail(pkt, &linkid, 1); |
561 | spin_unlock(&ctrl->loop_linkid_lock); | 610 | spin_unlock_bh(&ctrl->loop_linkid_lock); |
562 | cfpkt_peek_head(pkt, &linktype, 1); | 611 | cfpkt_peek_head(pkt, &linktype, 1); |
563 | if (linktype == CFCTRL_SRV_UTIL) { | 612 | if (linktype == CFCTRL_SRV_UTIL) { |
564 | tmp = 0x01; | 613 | tmp = 0x01; |
@@ -568,10 +617,10 @@ found: | |||
568 | break; | 617 | break; |
569 | 618 | ||
570 | case CFCTRL_CMD_LINK_DESTROY: | 619 | case CFCTRL_CMD_LINK_DESTROY: |
571 | spin_lock(&ctrl->loop_linkid_lock); | 620 | spin_lock_bh(&ctrl->loop_linkid_lock); |
572 | cfpkt_peek_head(pkt, &linkid, 1); | 621 | cfpkt_peek_head(pkt, &linkid, 1); |
573 | ctrl->loop_linkused[linkid] = 0; | 622 | ctrl->loop_linkused[linkid] = 0; |
574 | spin_unlock(&ctrl->loop_linkid_lock); | 623 | spin_unlock_bh(&ctrl->loop_linkid_lock); |
575 | break; | 624 | break; |
576 | default: | 625 | default: |
577 | break; | 626 | break; |