mISDN: Fix refcounting bug
authorKarsten Keil <isdn@linux-pingi.de>
Fri, 4 May 2012 04:15:31 +0000 (04:15 +0000)
committerDavid S. Miller <davem@davemloft.net>
Fri, 4 May 2012 15:53:59 +0000 (11:53 -0400)
Under some configs it was still not possible to unload the driver,
because the module use count was srewed up.

Signed-off-by: Karsten Keil <keil@b1-systems.de>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/isdn/mISDN/tei.c

index 969766f..109276a 100644 (file)
@@ -790,18 +790,23 @@ tei_ph_data_ind(struct teimgr *tm, u_int mt, u_char *dp, int len)
 static struct layer2 *
 create_new_tei(struct manager *mgr, int tei, int sapi)
 {
-       u_long          opt = 0;
-       u_long          flags;
-       int             id;
-       struct layer2   *l2;
+       unsigned long           opt = 0;
+       unsigned long           flags;
+       int                     id;
+       struct layer2           *l2;
+       struct channel_req      rq;
 
        if (!mgr->up)
                return NULL;
        if ((tei >= 0) && (tei < 64))
                test_and_set_bit(OPTION_L2_FIXEDTEI, &opt);
-       if (mgr->ch.st->dev->Dprotocols
-           & ((1 << ISDN_P_TE_E1) | (1 << ISDN_P_NT_E1)))
+       if (mgr->ch.st->dev->Dprotocols & ((1 << ISDN_P_TE_E1) |
+           (1 << ISDN_P_NT_E1))) {
                test_and_set_bit(OPTION_L2_PMX, &opt);
+               rq.protocol = ISDN_P_NT_E1;
+       } else {
+               rq.protocol = ISDN_P_NT_S0;
+       }
        l2 = create_l2(mgr->up, ISDN_P_LAPD_NT, opt, tei, sapi);
        if (!l2) {
                printk(KERN_WARNING "%s:no memory for layer2\n", __func__);
@@ -836,6 +841,14 @@ create_new_tei(struct manager *mgr, int tei, int sapi)
                l2->ch.recv = mgr->ch.recv;
                l2->ch.peer = mgr->ch.peer;
                l2->ch.ctrl(&l2->ch, OPEN_CHANNEL, NULL);
+               /* We need open here L1 for the manager as well (refcounting) */
+               rq.adr.dev = mgr->ch.st->dev->id;
+               id = mgr->ch.st->own.ctrl(&mgr->ch.st->own, OPEN_CHANNEL, &rq);
+               if (id < 0) {
+                       printk(KERN_WARNING "%s: cannot open L1\n", __func__);
+                       l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL);
+                       l2 = NULL;
+               }
        }
        return l2;
 }
@@ -978,10 +991,11 @@ TEIrelease(struct layer2 *l2)
 static int
 create_teimgr(struct manager *mgr, struct channel_req *crq)
 {
-       struct layer2   *l2;
-       u_long          opt = 0;
-       u_long          flags;
-       int             id;
+       struct layer2           *l2;
+       unsigned long           opt = 0;
+       unsigned long           flags;
+       int                     id;
+       struct channel_req      l1rq;
 
        if (*debug & DEBUG_L2_TEI)
                printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n",
@@ -1016,6 +1030,7 @@ create_teimgr(struct manager *mgr, struct channel_req *crq)
                if (crq->protocol == ISDN_P_LAPD_TE)
                        test_and_set_bit(MGR_OPT_USER, &mgr->options);
        }
+       l1rq.adr = crq->adr;
        if (mgr->ch.st->dev->Dprotocols
            & ((1 << ISDN_P_TE_E1) | (1 << ISDN_P_NT_E1)))
                test_and_set_bit(OPTION_L2_PMX, &opt);
@@ -1055,24 +1070,34 @@ create_teimgr(struct manager *mgr, struct channel_req *crq)
                l2->tm->tei_m.fsm = &teifsmu;
                l2->tm->tei_m.state = ST_TEI_NOP;
                l2->tm->tval = 1000; /* T201  1 sec */
+               if (test_bit(OPTION_L2_PMX, &opt))
+                       l1rq.protocol = ISDN_P_TE_E1;
+               else
+                       l1rq.protocol = ISDN_P_TE_S0;
        } else {
                l2->tm->tei_m.fsm = &teifsmn;
                l2->tm->tei_m.state = ST_TEI_NOP;
                l2->tm->tval = 2000; /* T202  2 sec */
+               if (test_bit(OPTION_L2_PMX, &opt))
+                       l1rq.protocol = ISDN_P_NT_E1;
+               else
+                       l1rq.protocol = ISDN_P_NT_S0;
        }
        mISDN_FsmInitTimer(&l2->tm->tei_m, &l2->tm->timer);
        write_lock_irqsave(&mgr->lock, flags);
        id = get_free_id(mgr);
        list_add_tail(&l2->list, &mgr->layer2);
        write_unlock_irqrestore(&mgr->lock, flags);
-       if (id < 0) {
-               l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL);
-       } else {
+       if (id >= 0) {
                l2->ch.nr = id;
                l2->up->nr = id;
                crq->ch = &l2->ch;
-               id = 0;
+               /* We need open here L1 for the manager as well (refcounting) */
+               id = mgr->ch.st->own.ctrl(&mgr->ch.st->own, OPEN_CHANNEL,
+                                         &l1rq);
        }
+       if (id < 0)
+               l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL);
        return id;
 }