Bluetooth: Fix waiting for clearing of BT_SK_SUSPEND flag
authorJohan Hedberg <johan.hedberg@intel.com>
Mon, 16 Sep 2013 10:05:19 +0000 (13:05 +0300)
committerGustavo Padovan <gustavo.padovan@collabora.co.uk>
Wed, 18 Sep 2013 22:02:59 +0000 (17:02 -0500)
In the case of blocking sockets we should not proceed with sendmsg() if
the socket has the BT_SK_SUSPEND flag set. So far the code was only
ensuring that POLLOUT doesn't get set for non-blocking sockets using
poll() but there was no code in place to ensure that blocking sockets do
the right thing when writing to them.

This patch adds a new bt_sock_wait_ready helper function to sleep in the
sendmsg call if the BT_SK_SUSPEND flag is set, and wake up as soon as it
is unset. It also updates the L2CAP and RFCOMM sendmsg callbacks to take
advantage of this new helper function.

Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
Acked-by: Marcel Holtmann <marcel@holtmann.org>
Signed-off-by: Gustavo Padovan <gustavo.padovan@collabora.co.uk>
include/net/bluetooth/bluetooth.h
net/bluetooth/af_bluetooth.c
net/bluetooth/l2cap_sock.c
net/bluetooth/rfcomm/sock.c

index 10d43d8c7037ac58bea44788af5c7086dbf40f90..afbc711ba37a432119833ae23a0ecf853de59fec 100644 (file)
@@ -249,6 +249,7 @@ int  bt_sock_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
 uint bt_sock_poll(struct file *file, struct socket *sock, poll_table *wait);
 int  bt_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg);
 int  bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo);
+int  bt_sock_wait_ready(struct sock *sk, unsigned long flags);
 
 void bt_accept_enqueue(struct sock *parent, struct sock *sk);
 void bt_accept_unlink(struct sock *sk);
index 9096137c889c4857507fcb60828b8d7ad581f424..c600631cd19e244c5ca73811bea163d31eaca72b 100644 (file)
@@ -525,6 +525,46 @@ int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo)
 }
 EXPORT_SYMBOL(bt_sock_wait_state);
 
+/* This function expects the sk lock to be held when called */
+int bt_sock_wait_ready(struct sock *sk, unsigned long flags)
+{
+       DECLARE_WAITQUEUE(wait, current);
+       unsigned long timeo;
+       int err = 0;
+
+       BT_DBG("sk %p", sk);
+
+       timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
+
+       add_wait_queue(sk_sleep(sk), &wait);
+       set_current_state(TASK_INTERRUPTIBLE);
+       while (test_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags)) {
+               if (!timeo) {
+                       err = -EAGAIN;
+                       break;
+               }
+
+               if (signal_pending(current)) {
+                       err = sock_intr_errno(timeo);
+                       break;
+               }
+
+               release_sock(sk);
+               timeo = schedule_timeout(timeo);
+               lock_sock(sk);
+               set_current_state(TASK_INTERRUPTIBLE);
+
+               err = sock_error(sk);
+               if (err)
+                       break;
+       }
+       __set_current_state(TASK_RUNNING);
+       remove_wait_queue(sk_sleep(sk), &wait);
+
+       return err;
+}
+EXPORT_SYMBOL(bt_sock_wait_ready);
+
 #ifdef CONFIG_PROC_FS
 struct bt_seq_state {
        struct bt_sock_list *l;
index 0098af80b21327045c99a38771ed8c95da2544c2..ad95b426b09cd3576a41dfbcaec1a0aac4bad3ec 100644 (file)
@@ -777,6 +777,12 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
        if (sk->sk_state != BT_CONNECTED)
                return -ENOTCONN;
 
+       lock_sock(sk);
+       err = bt_sock_wait_ready(sk, msg->msg_flags);
+       release_sock(sk);
+       if (err)
+               return err;
+
        l2cap_chan_lock(chan);
        err = l2cap_chan_send(chan, msg, len, sk->sk_priority);
        l2cap_chan_unlock(chan);
index 30b3721dc6d77d96be4bdb412ee90c49b8976dc9..072938dc527d5800af3cf90b9da970585b277c6b 100644 (file)
@@ -544,7 +544,7 @@ static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
        struct sock *sk = sock->sk;
        struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc;
        struct sk_buff *skb;
-       int sent = 0;
+       int sent;
 
        if (test_bit(RFCOMM_DEFER_SETUP, &d->flags))
                return -ENOTCONN;
@@ -559,6 +559,10 @@ static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
 
        lock_sock(sk);
 
+       sent = bt_sock_wait_ready(sk, msg->msg_flags);
+       if (sent)
+               goto done;
+
        while (len) {
                size_t size = min_t(size_t, len, d->mtu);
                int err;
@@ -594,6 +598,7 @@ static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
                len  -= size;
        }
 
+done:
        release_sock(sk);
 
        return sent;