2006-12-26 11:49:23

by Fabien Chevalier

[permalink] [raw]
Subject: [PATCH] New Christmas SCO flow control patch

diff --git a/include/net/bluetooth/sco.h b/include/net/bluetooth/sco.h
index e28a2a7..4ba976b 100644
--- a/include/net/bluetooth/sco.h
+++ b/include/net/bluetooth/sco.h
@@ -26,12 +26,7 @@
#define __SCO_H

/* SCO defaults */
-#define SCO_DEFAULT_MTU 500
-#define SCO_DEFAULT_FLUSH_TO 0xFFFF
-
#define SCO_CONN_TIMEOUT (HZ * 40)
-#define SCO_DISCONN_TIMEOUT (HZ * 2)
-#define SCO_CONN_IDLE_TIMEOUT (HZ * 60)

/* SCO socket address */
struct sockaddr_sco {
@@ -51,6 +46,9 @@ struct sco_conninfo {
__u8 dev_class[3];
};

+#define SCO_TXBUFS 0x03
+#define SCO_RXBUFS 0x04
+
/* ---- SCO connections ---- */
struct sco_conn {
struct hci_conn *hcon;

diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
index 5d13d4f..50f7590 100644
--- a/net/bluetooth/sco.c
+++ b/net/bluetooth/sco.c
@@ -53,7 +53,13 @@
#define BT_DBG(D...)
#endif

-#define VERSION "0.5"
+#define VERSION "0.6"
+
+#define MAX_SCO_TXBUFS 200
+#define MAX_SCO_RXBUFS 200
+
+#define DEFAULT_SCO_TXBUFS 5
+#define DEFAULT_SCO_RXBUFS 5

static const struct proto_ops sco_sock_ops;

@@ -61,6 +67,8 @@ static struct bt_sock_list sco_sk_list = {
.lock = RW_LOCK_UNLOCKED
};

+/* Local functions declaration */
+
static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent);
static void sco_chan_del(struct sock *sk, int err);

@@ -69,6 +77,35 @@ static int sco_conn_del(struct hci_conn *conn, int err);
static void sco_sock_close(struct sock *sk);
static void sco_sock_kill(struct sock *sk);

+static int sco_sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb);
+
+/*
+ * Write buffer destructor automatically called from kfree_skb.
+ */
+void sco_sock_wfree(struct sk_buff *skb)
+{
+ struct sock *sk = skb->sk;
+
+ atomic_dec(&sk->sk_wmem_alloc);
+ sk->sk_write_space(sk);
+ sock_put(sk);
+}
+
+static void sco_sock_write_space(struct sock *sk)
+{
+ read_lock(&sk->sk_callback_lock);
+
+ if(atomic_read(&sk->sk_wmem_alloc) < sk->sk_sndbuf) {
+ if (sk->sk_sleep && waitqueue_active(sk->sk_sleep))
+ wake_up_interruptible(sk->sk_sleep);
+
+ if (sock_writeable(sk))
+ sk_wake_async(sk, 2, POLL_OUT);
+ }
+
+ read_unlock(&sk->sk_callback_lock);
+}
+
/* ---- SCO timers ---- */
static void sco_sock_timeout(unsigned long arg)
{
@@ -234,27 +271,30 @@ static inline int sco_send_frame(struct sock *sk, struct msghdr *msg, int len)
{
struct sco_conn *conn = sco_pi(sk)->conn;
struct sk_buff *skb;
- int err, count;
-
- /* Check outgoing MTU */
- if (len > conn->mtu)
- return -EINVAL;
+ int err;

BT_DBG("sk %p len %d", sk, len);

- count = min_t(unsigned int, conn->mtu, len);
- if (!(skb = bt_skb_send_alloc(sk, count, msg->msg_flags & MSG_DONTWAIT, &err)))
+ if (!(skb = bt_skb_send_alloc(sk, len, msg->msg_flags & MSG_DONTWAIT, &err)))
return err;

- if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) {
+ /* fix sk_wmem_alloc value : by default it is increased by skb->truesize, but
+ we want it only increased by 1 */
+ atomic_sub(skb->truesize - 1, &sk->sk_wmem_alloc);
+ /* fix destructor */
+ skb->destructor = sco_sock_wfree;
+
+ if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) {
err = -EFAULT;
goto fail;
}

- if ((err = hci_send_sco(conn->hcon, skb)) < 0)
- return err;
+ err = hci_send_sco(conn->hcon, skb);

- return count;
+ if (err < 0)
+ goto fail;
+
+ return len;

fail:
kfree_skb(skb);
@@ -273,8 +313,9 @@ static inline void sco_recv_frame(struct sco_conn *conn, struct sk_buff *skb)
if (sk->sk_state != BT_CONNECTED)
goto drop;

- if (!sock_queue_rcv_skb(sk, skb))
+ if (sco_sock_queue_rcv_skb(sk, skb) == 0) {
return;
+ }

drop:
kfree_skb(skb);
@@ -328,7 +369,6 @@ static void sco_sock_destruct(struct sock *sk)
BT_DBG("sk %p", sk);

skb_queue_purge(&sk->sk_receive_queue);
- skb_queue_purge(&sk->sk_write_queue);
}

static void sco_sock_cleanup_listen(struct sock *parent)
@@ -360,6 +400,8 @@ static void sco_sock_kill(struct sock *sk)
/* Kill poor orphan */
bt_sock_unlink(&sco_sk_list, sk);
sock_set_flag(sk, SOCK_DEAD);
+
+ /* release socket */
sock_put(sk);
}

@@ -376,7 +418,7 @@ static void sco_sock_close(struct sock *sk)

conn = sco_pi(sk)->conn;

- BT_DBG("sk %p state %d conn %p socket %p", sk, sk->sk_state, conn, sk->sk_socket);
+ BT_DBG("sk %p state %d conn %p socket %p refcnt %d", sk, sk->sk_state, conn, sk->sk_socket, atomic_read(&sk->sk_refcnt));

switch (sk->sk_state) {
case BT_LISTEN:
@@ -426,6 +468,15 @@ static struct sock *sco_sock_alloc(struct socket *sock, int proto, gfp_t prio)
INIT_LIST_HEAD(&bt_sk(sk)->accept_q);

sk->sk_destruct = sco_sock_destruct;
+ sk->sk_write_space = sco_sock_write_space;
+
+ /* Put sensible values for a voice link (i.e. not too big),
+ as sysctl_rmem_default & sysctl_wmem_default are
+ really not designed for that -- In our case we use sk_**buf to
+ store a count of SCO packets, not a number of bytes as most of other type of
+ sockets do */
+ sk->sk_sndbuf = DEFAULT_SCO_TXBUFS;
+ sk->sk_rcvbuf = DEFAULT_SCO_RXBUFS;
sk->sk_sndtimeo = SCO_CONN_TIMEOUT;

sock_reset_flag(sk, SOCK_ZAPPED);
@@ -656,6 +707,7 @@ static int sco_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen)
{
struct sock *sk = sock->sk;
+ u32 opt;
int err = 0;

BT_DBG("sk %p", sk);
@@ -663,6 +715,35 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char
lock_sock(sk);

switch (optname) {
+ case SCO_TXBUFS:
+ if (get_user(opt, (u32 __user *) optval)) {
+ err = -EFAULT;
+ break;
+ }
+ if(opt > MAX_SCO_TXBUFS) {
+ err = -EINVAL;
+ break;
+ }
+
+ sk->sk_sndbuf = opt;
+ /*
+ * Wake up sending tasks if we
+ * upped the value.
+ */
+ sk->sk_write_space(sk);
+ break;
+ case SCO_RXBUFS:
+ if (get_user(opt, (u32 __user *) optval)) {
+ err = -EFAULT;
+ break;
+ }
+ if(opt > MAX_SCO_RXBUFS) {
+ err = -EINVAL;
+ break;
+ }
+
+ sk->sk_rcvbuf = opt;
+ break;
default:
err = -ENOPROTOOPT;
break;
@@ -677,7 +758,8 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char
struct sock *sk = sock->sk;
struct sco_options opts;
struct sco_conninfo cinfo;
- int len, err = 0;
+ int len, err = 0;
+ int val;

BT_DBG("sk %p", sk);

@@ -687,6 +769,24 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char
lock_sock(sk);

switch (optname) {
+ case SCO_RXBUFS:
+ val = sk->sk_rcvbuf;
+
+ len = min_t(unsigned int, len, sizeof(val));
+ if (copy_to_user(optval, (char *)&val, len))
+ err = -EFAULT;
+
+ break;
+
+ case SCO_TXBUFS:
+ val = sk->sk_sndbuf;
+
+ len = min_t(unsigned int, len, sizeof(val));
+ if (copy_to_user(optval, (char *)&val, len))
+ err = -EFAULT;
+
+ break;
+
case SCO_OPTIONS:
if (sk->sk_state != BT_CONNECTED) {
err = -ENOTCONN;
@@ -891,6 +991,34 @@ drop:
return 0;
}

+static int sco_sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
+{
+ int err = 0;
+
+ BT_DBG("sock %p, sk_rcvbuf %d, qlen %d", sk, sk->sk_rcvbuf, skb_queue_len(&sk->sk_receive_queue));
+ /* Cast skb->rcvbuf to unsigned... It's pointless, but reduces
+ number of warnings when compiling with -W --ANK
+ */
+ if (skb_queue_len(&sk->sk_receive_queue) + 1 >
+ (unsigned)sk->sk_rcvbuf) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ skb->dev = NULL;
+ skb->sk = sk;
+ skb->destructor = NULL;
+
+ skb_queue_tail(&sk->sk_receive_queue, skb);
+
+ if (!sock_flag(sk, SOCK_DEAD))
+ sk->sk_data_ready(sk, 1);
+out:
+ return err;
+}
+
+/* ------- Others ------- */
+
static ssize_t sco_sysfs_show(struct class *dev, char *buf)
{
struct sock *sk;


Attachments:
sco-flowcontrol-v3.0-1of2.diff (13.99 kB)
sco-flowcontrol-v3.0-2of2.diff (7.69 kB)
fchevalier.vcf (241.00 B)
Download all attachments