caif: Fix for a race in socket transmit with flow control.
authorDmitry Tarnyagin <dmitry.tarnyagin@stericsson.com>
Sun, 11 Mar 2012 10:28:31 +0000 (10:28 +0000)
committerDavid S. Miller <davem@davemloft.net>
Sun, 11 Mar 2012 22:38:16 +0000 (15:38 -0700)
Kill faulty checks on flow-off leading to connection drop at race conditions.
caif_socket checks for flow-on before transmitting and goes to sleep or
return -EAGAIN upon flow stop. Remove faulty subsequent checks on flow-off
leading to connection drop. Also fix memory leaks on some of the errors paths.

Signed-off-by: Sjur Brændeland <sjur.brandeland@stericsson.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/caif/cfdbgl.c
net/caif/cfdgml.c
net/caif/cfrfml.c
net/caif/cfsrvl.c
net/caif/cfutill.c
net/caif/cfvidl.c

index 65d6ef3..2914659 100644 (file)
@@ -41,8 +41,10 @@ static int cfdbgl_transmit(struct cflayer *layr, struct cfpkt *pkt)
        struct caif_payload_info *info;
        int ret;
 
-       if (!cfsrvl_ready(service, &ret))
+       if (!cfsrvl_ready(service, &ret)) {
+               cfpkt_destroy(pkt);
                return ret;
+       }
 
        /* Add info for MUX-layer to route the packet out */
        info = cfpkt_info(pkt);
index 0f5ff27..a63f4a5 100644 (file)
@@ -86,12 +86,17 @@ static int cfdgml_transmit(struct cflayer *layr, struct cfpkt *pkt)
        struct caif_payload_info *info;
        struct cfsrvl *service = container_obj(layr);
        int ret;
-       if (!cfsrvl_ready(service, &ret))
+
+       if (!cfsrvl_ready(service, &ret)) {
+               cfpkt_destroy(pkt);
                return ret;
+       }
 
        /* STE Modem cannot handle more than 1500 bytes datagrams */
-       if (cfpkt_getlen(pkt) > DGM_MTU)
+       if (cfpkt_getlen(pkt) > DGM_MTU) {
+               cfpkt_destroy(pkt);
                return -EMSGSIZE;
+       }
 
        cfpkt_add_head(pkt, &zero, 3);
        packet_type = 0x08; /* B9 set - UNCLASSIFIED */
index 6dc75d4..2b563ad 100644 (file)
@@ -184,6 +184,11 @@ out:
                                        rfml->serv.dev_info.id);
        }
        spin_unlock(&rfml->sync);
+
+       if (unlikely(err == -EAGAIN))
+               /* It is not possible to recover after drop of a fragment */
+               err = -EIO;
+
        return err;
 }
 
@@ -218,7 +223,7 @@ static int cfrfml_transmit(struct cflayer *layr, struct cfpkt *pkt)
        caif_assert(layr->dn->transmit != NULL);
 
        if (!cfsrvl_ready(&rfml->serv, &err))
-               return err;
+               goto out;
 
        err = -EPROTO;
        if (cfpkt_getlen(pkt) <= RFM_HEAD_SIZE-1)
@@ -251,8 +256,11 @@ static int cfrfml_transmit(struct cflayer *layr, struct cfpkt *pkt)
 
                err = cfrfml_transmit_segment(rfml, frontpkt);
 
-               if (err != 0)
+               if (err != 0) {
+                       frontpkt = NULL;
                        goto out;
+               }
+
                frontpkt = rearpkt;
                rearpkt = NULL;
 
@@ -286,19 +294,8 @@ out:
                if (rearpkt)
                        cfpkt_destroy(rearpkt);
 
-               if (frontpkt && frontpkt != pkt) {
-
+               if (frontpkt)
                        cfpkt_destroy(frontpkt);
-                       /*
-                        * Socket layer will free the original packet,
-                        * but this packet may already be sent and
-                        * freed. So we have to return 0 in this case
-                        * to avoid socket layer to re-free this packet.
-                        * The return of shutdown indication will
-                        * cause connection to be invalidated anyhow.
-                        */
-                       err = 0;
-               }
        }
 
        return err;
index b99f5b2..4aa33d4 100644 (file)
@@ -174,15 +174,11 @@ void cfsrvl_init(struct cfsrvl *service,
 
 bool cfsrvl_ready(struct cfsrvl *service, int *err)
 {
-       if (service->open && service->modem_flow_on && service->phy_flow_on)
-               return true;
        if (!service->open) {
                *err = -ENOTCONN;
                return false;
        }
-       caif_assert(!(service->modem_flow_on && service->phy_flow_on));
-       *err = -EAGAIN;
-       return false;
+       return true;
 }
 
 u8 cfsrvl_getphyid(struct cflayer *layer)
index 53e49f3..86d2dad 100644 (file)
@@ -84,8 +84,11 @@ static int cfutill_transmit(struct cflayer *layr, struct cfpkt *pkt)
        caif_assert(layr != NULL);
        caif_assert(layr->dn != NULL);
        caif_assert(layr->dn->transmit != NULL);
-       if (!cfsrvl_ready(service, &ret))
+
+       if (!cfsrvl_ready(service, &ret)) {
+               cfpkt_destroy(pkt);
                return ret;
+       }
 
        cfpkt_add_head(pkt, &zero, 1);
        /* Add info for MUX-layer to route the packet out. */
index e3f37db..a8e2a2d 100644 (file)
@@ -50,8 +50,12 @@ static int cfvidl_transmit(struct cflayer *layr, struct cfpkt *pkt)
        struct caif_payload_info *info;
        u32 videoheader = 0;
        int ret;
-       if (!cfsrvl_ready(service, &ret))
+
+       if (!cfsrvl_ready(service, &ret)) {
+               cfpkt_destroy(pkt);
                return ret;
+       }
+
        cfpkt_add_head(pkt, &videoheader, 4);
        /* Add info for MUX-layer to route the packet out */
        info = cfpkt_info(pkt);