Phonet: use atomic for packet TX window
Rémi Denis-Courmont [Wed, 17 Dec 2008 23:48:31 +0000 (15:48 -0800)]
GPRS TX flow control won't need to lock the underlying socket anymore.

Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

include/net/phonet/pep.h
net/phonet/pep.c
net/phonet/socket.c

index fcd7930..4c61cdc 100644 (file)
@@ -35,12 +35,12 @@ struct pep_sock {
        struct sock             *listener;
        struct sk_buff_head     ctrlreq_queue;
 #define PNPIPE_CTRLREQ_MAX     10
+       atomic_t                tx_credits;
        int                     ifindex;
        u16                     peer_type;      /* peer type/subtype */
        u8                      pipe_handle;
 
        u8                      rx_credits;
-       u8                      tx_credits;
        u8                      rx_fc;  /* RX flow control */
        u8                      tx_fc;  /* TX flow control */
        u8                      init_enable;    /* auto-enable at creation */
index bc6d50f..bb3e678 100644 (file)
@@ -225,6 +225,7 @@ static int pipe_rcv_status(struct sock *sk, struct sk_buff *skb)
 {
        struct pep_sock *pn = pep_sk(sk);
        struct pnpipehdr *hdr = pnp_hdr(skb);
+       int wake = 0;
 
        if (!pskb_may_pull(skb, sizeof(*hdr) + 4))
                return -EINVAL;
@@ -241,16 +242,16 @@ static int pipe_rcv_status(struct sock *sk, struct sk_buff *skb)
                case PN_LEGACY_FLOW_CONTROL:
                        switch (hdr->data[4]) {
                        case PEP_IND_BUSY:
-                               pn->tx_credits = 0;
+                               atomic_set(&pn->tx_credits, 0);
                                break;
                        case PEP_IND_READY:
-                               pn->tx_credits = 1;
+                               atomic_set(&pn->tx_credits, wake = 1);
                                break;
                        }
                        break;
                case PN_ONE_CREDIT_FLOW_CONTROL:
                        if (hdr->data[4] == PEP_IND_READY)
-                               pn->tx_credits = 1;
+                               atomic_set(&pn->tx_credits, wake = 1);
                        break;
                }
                break;
@@ -258,10 +259,7 @@ static int pipe_rcv_status(struct sock *sk, struct sk_buff *skb)
        case PN_PEP_IND_ID_MCFC_GRANT_CREDITS:
                if (pn->tx_fc != PN_MULTI_CREDIT_FLOW_CONTROL)
                        break;
-               if (pn->tx_credits + hdr->data[4] > 0xff)
-                       pn->tx_credits = 0xff;
-               else
-                       pn->tx_credits += hdr->data[4];
+               atomic_add(wake = hdr->data[4], &pn->tx_credits);
                break;
 
        default:
@@ -269,7 +267,7 @@ static int pipe_rcv_status(struct sock *sk, struct sk_buff *skb)
                                (unsigned)hdr->data[1]);
                return -EOPNOTSUPP;
        }
-       if (pn->tx_credits)
+       if (wake)
                sk->sk_write_space(sk);
        return 0;
 }
@@ -343,7 +341,7 @@ static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb)
                }
                /* fall through */
        case PNS_PEP_DISABLE_REQ:
-               pn->tx_credits = 0;
+               atomic_set(&pn->tx_credits, 0);
                pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC);
                break;
 
@@ -390,7 +388,7 @@ static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb)
                /* fall through */
        case PNS_PIPE_ENABLED_IND:
                if (!pn_flow_safe(pn->tx_fc)) {
-                       pn->tx_credits = 1;
+                       atomic_set(&pn->tx_credits, 1);
                        sk->sk_write_space(sk);
                }
                if (sk->sk_state == TCP_ESTABLISHED)
@@ -504,8 +502,9 @@ static int pep_connreq_rcv(struct sock *sk, struct sk_buff *skb)
        newpn->pn_sk.resource = pn->pn_sk.resource;
        skb_queue_head_init(&newpn->ctrlreq_queue);
        newpn->pipe_handle = pipe_handle;
+       atomic_set(&newpn->tx_credits, 0);
        newpn->peer_type = peer_type;
-       newpn->rx_credits = newpn->tx_credits = 0;
+       newpn->rx_credits = 0;
        newpn->rx_fc = newpn->tx_fc = PN_LEGACY_FLOW_CONTROL;
        newpn->init_enable = enabled;
 
@@ -821,14 +820,18 @@ static int pipe_skb_send(struct sock *sk, struct sk_buff *skb)
        struct pep_sock *pn = pep_sk(sk);
        struct pnpipehdr *ph;
 
+       if (pn_flow_safe(pn->tx_fc) &&
+           !atomic_add_unless(&pn->tx_credits, -1, 0)) {
+               kfree_skb(skb);
+               return -ENOBUFS;
+       }
+
        skb_push(skb, 3);
        skb_reset_transport_header(skb);
        ph = pnp_hdr(skb);
        ph->utid = 0;
        ph->message_id = PNS_PIPE_DATA;
        ph->pipe_handle = pn->pipe_handle;
-       if (pn_flow_safe(pn->tx_fc) && pn->tx_credits)
-               pn->tx_credits--;
 
        return pn_skb_send(sk, skb, &pipe_srv);
 }
@@ -866,7 +869,7 @@ disabled:
        BUG_ON(sk->sk_state != TCP_ESTABLISHED);
 
        /* Wait until flow control allows TX */
-       done = pn->tx_credits > 0;
+       done = atomic_read(&pn->tx_credits);
        while (!done) {
                DEFINE_WAIT(wait);
 
@@ -881,7 +884,7 @@ disabled:
 
                prepare_to_wait(&sk->sk_socket->wait, &wait,
                                TASK_INTERRUPTIBLE);
-               done = sk_wait_event(sk, &timeo, pn->tx_credits > 0);
+               done = sk_wait_event(sk, &timeo, atomic_read(&pn->tx_credits));
                finish_wait(&sk->sk_socket->wait, &wait);
 
                if (sk->sk_state != TCP_ESTABLISHED)
@@ -895,7 +898,8 @@ disabled:
                        goto out;
                skb_reserve(skb, MAX_PHONET_HEADER + 3);
 
-               if (sk->sk_state != TCP_ESTABLISHED || !pn->tx_credits)
+               if (sk->sk_state != TCP_ESTABLISHED ||
+                   !atomic_read(&pn->tx_credits))
                        goto disabled; /* sock_alloc_send_skb might sleep */
        }
 
@@ -917,7 +921,7 @@ int pep_writeable(struct sock *sk)
 {
        struct pep_sock *pn = pep_sk(sk);
 
-       return (sk->sk_state == TCP_ESTABLISHED) ? pn->tx_credits : 0;
+       return atomic_read(&pn->tx_credits);
 }
 
 int pep_write(struct sock *sk, struct sk_buff *skb)
index c75aa5c..ada2a35 100644 (file)
@@ -227,7 +227,7 @@ static unsigned int pn_socket_poll(struct file *file, struct socket *sock,
        if (!mask && sk->sk_state == TCP_CLOSE_WAIT)
                return POLLHUP;
 
-       if (sk->sk_state == TCP_ESTABLISHED && pn->tx_credits)
+       if (sk->sk_state == TCP_ESTABLISHED && atomic_read(&pn->tx_credits))
                mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
 
        return mask;