2012-02-03 14:36:19

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv2 00/10] Bluetooth: Change socket lock to l2cap_chan lock

From: Andrei Emeltchenko <[email protected]>

Changes:
* RFCv2: Convert l2cap channel list back to mutex from RCU list.

This is DRAFT RFC introducing l2cap channel lock. Please make suggestion how to
make it better.

Andrei Emeltchenko (10):
Bluetooth: Change chan_ready param from sk to chan
Bluetooth: Clean up l2cap_chan_add
Bluetooth: Remove unneeded sk variable
Bluetooth: Revert to mutexes from RCU list
Bluetooth: Add l2cap_chan_lock
Bluetooth: Add locked and unlocked state_change
Bluetooth: Add socket error function
Bluetooth: Change socket lock to chan lock
Bluetooth: Use l2cap chan lock in socket connect
Bluetooth: Remove socket lock check

include/net/bluetooth/l2cap.h | 11 ++
net/bluetooth/l2cap_core.c | 379 +++++++++++++++++++++++++----------------
net/bluetooth/l2cap_sock.c | 23 ++-
3 files changed, 258 insertions(+), 155 deletions(-)

--
1.7.8.3



2012-02-06 10:44:52

by Andrei Emeltchenko

[permalink] [raw]
Subject: Re: [RFCv2 04/10] Bluetooth: Revert to mutexes from RCU list

Hi Ulisses,

Thanks for the review, please see my comment below:

On Fri, Feb 03, 2012 at 07:14:24PM -0200, Ulisses Furquim wrote:
> On Fri, Feb 3, 2012 at 12:36 PM, Emeltchenko Andrei
> <[email protected]> wrote:
> > From: Andrei Emeltchenko <[email protected]>
> >
> > Usage of RCU list looks not reasonalbe for a number of reasons:
> > our code sleep and we have to use socket spinlocks, some parts
> > of code are updaters thus we need to use mutexes anyway.
>
> Just a comment. RCU updater side should be locked in most cases so
> that's not really a big reason for removing RCU usage here.

Yes, the main reason was sleeping inside RCU.

...
> > ?static struct l2cap_chan *__l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 cid)
> > ?{
> > - ? ? ? struct l2cap_chan *c, *r = NULL;
> > -
> > - ? ? ? rcu_read_lock();
> > + ? ? ? struct l2cap_chan *c;
> >
> > - ? ? ? list_for_each_entry_rcu(c, &conn->chan_l, list) {
> > - ? ? ? ? ? ? ? if (c->scid == cid) {
> > - ? ? ? ? ? ? ? ? ? ? ? r = c;
> > - ? ? ? ? ? ? ? ? ? ? ? break;
> > - ? ? ? ? ? ? ? }
> > + ? ? ? list_for_each_entry(c, &conn->chan_l, list) {
> > + ? ? ? ? ? ? ? if (c->scid == cid)
> > + ? ? ? ? ? ? ? ? ? ? ? return c;
> > ? ? ? ?}
> > -
> > - ? ? ? rcu_read_unlock();
> > - ? ? ? return r;
> > + ? ? ? return NULL;
> > ?}
> >
> > ?/* Find channel with given SCID.
> > @@ -115,36 +103,34 @@ static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 ci
> > ?{
> > ? ? ? ?struct l2cap_chan *c;
> >
> > + ? ? ? mutex_lock(&conn->chan_lock);
> > ? ? ? ?c = __l2cap_get_chan_by_scid(conn, cid);
> > ? ? ? ?if (c)
> > ? ? ? ? ? ? ? ?lock_sock(c->sk);
> > + ? ? ? mutex_unlock(&conn->chan_lock);
> > ? ? ? ?return c;
> > ?}
>
> Ok. We have now chan_lock around the unlocked versions and we still
> return a locked sock. However, we do have to be careful not to open
> windows where we can deadlock. See my other comments below about that.

The function above is not used anywhere and should be removed. I think I
need to put chan_lock to __* versions of get channel.

> > ?static struct l2cap_chan *__l2cap_get_chan_by_ident(struct l2cap_conn *conn, u8 ident)
> > ?{
> > - ? ? ? struct l2cap_chan *c, *r = NULL;
> > -
> > - ? ? ? rcu_read_lock();
> > + ? ? ? struct l2cap_chan *c;
> >
> > - ? ? ? list_for_each_entry_rcu(c, &conn->chan_l, list) {
> > - ? ? ? ? ? ? ? if (c->ident == ident) {
> > - ? ? ? ? ? ? ? ? ? ? ? r = c;
> > - ? ? ? ? ? ? ? ? ? ? ? break;
> > - ? ? ? ? ? ? ? }
> > + ? ? ? list_for_each_entry(c, &conn->chan_l, list) {
> > + ? ? ? ? ? ? ? if (c->ident == ident)
> > + ? ? ? ? ? ? ? ? ? ? ? return c;
> > ? ? ? ?}
> > -
> > - ? ? ? rcu_read_unlock();
> > - ? ? ? return r;
> > + ? ? ? return NULL;
> > ?}
> >
> > ?static inline struct l2cap_chan *l2cap_get_chan_by_ident(struct l2cap_conn *conn, u8 ident)
> > ?{
> > ? ? ? ?struct l2cap_chan *c;
> >
> > + ? ? ? mutex_lock(&conn->chan_lock);
> > ? ? ? ?c = __l2cap_get_chan_by_ident(conn, ident);
> > ? ? ? ?if (c)
> > ? ? ? ? ? ? ? ?lock_sock(c->sk);
> > + ? ? ? mutex_unlock(&conn->chan_lock);
> > ? ? ? ?return c;
> > ?}
> >
> > @@ -259,6 +245,7 @@ static void l2cap_chan_timeout(struct work_struct *work)
> >
> > ? ? ? ?BT_DBG("chan %p state %d", chan, chan->state);
> >
> > + ? ? ? mutex_lock(&chan->conn->chan_lock);
> > ? ? ? ?lock_sock(sk);
> >
> > ? ? ? ?if (chan->state == BT_CONNECTED || chan->state == BT_CONFIG)
> > @@ -272,6 +259,7 @@ static void l2cap_chan_timeout(struct work_struct *work)
> > ? ? ? ?l2cap_chan_close(chan, reason);
> >
> > ? ? ? ?release_sock(sk);
> > + ? ? ? mutex_unlock(&chan->conn->chan_lock);
> >
> > ? ? ? ?chan->ops->close(chan->data);
> > ? ? ? ?l2cap_chan_put(chan);
> > @@ -357,7 +345,9 @@ static void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
> >
> > ? ? ? ?l2cap_chan_hold(chan);
> >
> > - ? ? ? list_add_rcu(&chan->list, &conn->chan_l);
> > + ? ? ? mutex_lock(&conn->chan_lock);
> > + ? ? ? list_add(&chan->list, &conn->chan_l);
> > + ? ? ? mutex_unlock(&conn->chan_lock);
> > ?}
>
> Hmm, I'm not sure this is correct. Have you seen l2cap_connect_req()
> calls __l2cap_get_chan_by_dcid() without the chan_lock? It's like that
> because it was relying on RCU but now we do need to lock chan_lock
> there as well. I'd recommend we turn l2cap_chan_add() into
> __l2cap_chan_add() and lock chan_lock in the callers. Do you agree?

Yes, this sounds reasonable. We can lock with chan_lock l2cap_connect_req
so that socket lock remains inside chan_lock and lockdep is happy.

> > ?/* Delete channel.
> > @@ -374,8 +364,7 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
> >
> > ? ? ? ?if (conn) {
> > ? ? ? ? ? ? ? ?/* Delete from channel list */
> > - ? ? ? ? ? ? ? list_del_rcu(&chan->list);
> > - ? ? ? ? ? ? ? synchronize_rcu();
> > + ? ? ? ? ? ? ? list_del(&chan->list);
> >
> > ? ? ? ? ? ? ? ?l2cap_chan_put(chan);
> >
> > @@ -426,10 +415,14 @@ static void l2cap_chan_cleanup_listen(struct sock *parent)
> > ? ? ? ?/* Close not yet accepted channels */
> > ? ? ? ?while ((sk = bt_accept_dequeue(parent, NULL))) {
> > ? ? ? ? ? ? ? ?struct l2cap_chan *chan = l2cap_pi(sk)->chan;
> > +
> > + ? ? ? ? ? ? ? mutex_lock(&chan->conn->chan_lock);
> > ? ? ? ? ? ? ? ?__clear_chan_timer(chan);
> > ? ? ? ? ? ? ? ?lock_sock(sk);
> > ? ? ? ? ? ? ? ?l2cap_chan_close(chan, ECONNRESET);
> > ? ? ? ? ? ? ? ?release_sock(sk);
> > + ? ? ? ? ? ? ? mutex_unlock(&chan->conn->chan_lock);
> > +
> > ? ? ? ? ? ? ? ?chan->ops->close(chan->data);
> > ? ? ? ?}
> > ?}
>
> I see you're adding locks around l2cap_chan_close() instead of just
> locking list_del() which might be a good approach. Is that what you
> want? Before we had to drop the lock to call l2cap_chan_close()

Yes, the idea is to minimize locks/unlocks count.

> because it would be locked again to change the list (which I thought
> wasn't good). And could you please remove the leftover comment in
> l2cap_conn_start() about releasing the lock before calling
> l2cap_chan_close()?

Yes.

> Have you seen l2cap_sock_shutdown() in l2cap_sock.c also calls
> l2cap_chan_close()? Why don't we need to lock chan_lock there?

I think chan_lock is missing there.

> If that's what you wanted then please describe this kind of change in
> the commit message (at least). This way we can refer to it if anything
> happens or if we need to understand the change. I do think you write
> short commit messages so please be more verbose.

OK, I 'll try to write longer commit messages and split this commit to
several chunks so that it would be easier to understand them.

>
> > ?/* ---- L2CAP signalling commands ---- */
> > @@ -2755,6 +2753,7 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd
> > ? ? ? ? ? ? ? ? ? ? ? ?return -EFAULT;
> > ? ? ? ?}
> >
> > + ? ? ? mutex_lock(&conn->chan_lock);
> > ? ? ? ?sk = chan->sk;
> >
> > ? ? ? ?switch (result) {
> > @@ -2782,6 +2781,8 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd
> > ? ? ? ?}
> >
> > ? ? ? ?release_sock(sk);
> > + ? ? ? mutex_unlock(&conn->chan_lock);
> > +
> > ? ? ? ?return 0;
> > ?}
> > @@ -3032,6 +3033,7 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd
> > ? ? ? ?if (!chan)
> > ? ? ? ? ? ? ? ?return 0;
> >
> > + ? ? ? mutex_lock(&conn->chan_lock);
> > ? ? ? ?sk = chan->sk;
> >
> > ? ? ? ?rsp.dcid = cpu_to_le16(chan->scid);
> > @@ -3042,6 +3044,7 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd
> >
> > ? ? ? ?l2cap_chan_del(chan, ECONNRESET);
> > ? ? ? ?release_sock(sk);
> > + ? ? ? mutex_unlock(&conn->chan_lock);
> >
> > ? ? ? ?chan->ops->close(chan->data);
> > ? ? ? ?return 0;
>
> Here in l2cap_connect_rsp() and l2cap_disconnect_req() it seems we
> have a window where we can deadlock chan_lock and sock lock. Have you
> seen that? It can be that it may never happen but it'd be good to
> avoid that IMO. We receive a locked sock from either
> l2cap_get_chan_by_scid() or l2cap_get_chan_by_ident() and then we try
> to lock chan_lock. That's a different order you've been locking them.
> Maybe we can lock chan_lock then use the unlocked versions and
> explicitly lock sock after receiving chan?

Actually I am using unlocked versions of get_chan functions. But I will
move chan_lock before those functions since them have to be locked.

> > @@ -3063,6 +3066,7 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd
> > ? ? ? ?if (!chan)
> > ? ? ? ? ? ? ? ?return 0;
> >
> > + ? ? ? mutex_lock(&conn->chan_lock);
> > ? ? ? ?sk = chan->sk;
> >
> > ? ? ? ?l2cap_chan_del(chan, 0);
>
> The last comment also applies here. Besides, aren't we missing an unlock?

Yes, apparently. See comment above.

> How much have you been testing these changes? I'd like to avoid
> introducing more bugs with changes are supposed to fix things so
> please do test this as much as you can.

I was testing it with my test scripts and I am going to test with PTS. But
still some bugs may be introduced since this is quite sensitive area.

Best regards
Andrei Emeltchenko


2012-02-03 21:14:24

by Ulisses Furquim

[permalink] [raw]
Subject: Re: [RFCv2 04/10] Bluetooth: Revert to mutexes from RCU list

Hi Andrei,

On Fri, Feb 3, 2012 at 12:36 PM, Emeltchenko Andrei
<[email protected]> wrote:
> From: Andrei Emeltchenko <[email protected]>
>
> Usage of RCU list looks not reasonalbe for a number of reasons:
> our code sleep and we have to use socket spinlocks, some parts
> of code are updaters thus we need to use mutexes anyway.

Just a comment. RCU updater side should be locked in most cases so
that's not really a big reason for removing RCU usage here.

> Signed-off-by: Andrei Emeltchenko <[email protected]>
> ---
> ?net/bluetooth/l2cap_core.c | ?110 ++++++++++++++++++++++---------------------
> ?1 files changed, 56 insertions(+), 54 deletions(-)
>
> diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
> index f1a6b3c..010ef75 100644
> --- a/net/bluetooth/l2cap_core.c
> +++ b/net/bluetooth/l2cap_core.c
> @@ -77,36 +77,24 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn,
>
> ?static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn, u16 cid)
> ?{
> - ? ? ? struct l2cap_chan *c, *r = NULL;
> -
> - ? ? ? rcu_read_lock();
> + ? ? ? struct l2cap_chan *c;
>
> - ? ? ? list_for_each_entry_rcu(c, &conn->chan_l, list) {
> - ? ? ? ? ? ? ? if (c->dcid == cid) {
> - ? ? ? ? ? ? ? ? ? ? ? r = c;
> - ? ? ? ? ? ? ? ? ? ? ? break;
> - ? ? ? ? ? ? ? }
> + ? ? ? list_for_each_entry(c, &conn->chan_l, list) {
> + ? ? ? ? ? ? ? if (c->dcid == cid)
> + ? ? ? ? ? ? ? ? ? ? ? return c;
> ? ? ? ?}
> -
> - ? ? ? rcu_read_unlock();
> - ? ? ? return r;
> + ? ? ? return NULL;
> ?}
>
> ?static struct l2cap_chan *__l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 cid)
> ?{
> - ? ? ? struct l2cap_chan *c, *r = NULL;
> -
> - ? ? ? rcu_read_lock();
> + ? ? ? struct l2cap_chan *c;
>
> - ? ? ? list_for_each_entry_rcu(c, &conn->chan_l, list) {
> - ? ? ? ? ? ? ? if (c->scid == cid) {
> - ? ? ? ? ? ? ? ? ? ? ? r = c;
> - ? ? ? ? ? ? ? ? ? ? ? break;
> - ? ? ? ? ? ? ? }
> + ? ? ? list_for_each_entry(c, &conn->chan_l, list) {
> + ? ? ? ? ? ? ? if (c->scid == cid)
> + ? ? ? ? ? ? ? ? ? ? ? return c;
> ? ? ? ?}
> -
> - ? ? ? rcu_read_unlock();
> - ? ? ? return r;
> + ? ? ? return NULL;
> ?}
>
> ?/* Find channel with given SCID.
> @@ -115,36 +103,34 @@ static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 ci
> ?{
> ? ? ? ?struct l2cap_chan *c;
>
> + ? ? ? mutex_lock(&conn->chan_lock);
> ? ? ? ?c = __l2cap_get_chan_by_scid(conn, cid);
> ? ? ? ?if (c)
> ? ? ? ? ? ? ? ?lock_sock(c->sk);
> + ? ? ? mutex_unlock(&conn->chan_lock);
> ? ? ? ?return c;
> ?}

Ok. We have now chan_lock around the unlocked versions and we still
return a locked sock. However, we do have to be careful not to open
windows where we can deadlock. See my other comments below about that.

> ?static struct l2cap_chan *__l2cap_get_chan_by_ident(struct l2cap_conn *conn, u8 ident)
> ?{
> - ? ? ? struct l2cap_chan *c, *r = NULL;
> -
> - ? ? ? rcu_read_lock();
> + ? ? ? struct l2cap_chan *c;
>
> - ? ? ? list_for_each_entry_rcu(c, &conn->chan_l, list) {
> - ? ? ? ? ? ? ? if (c->ident == ident) {
> - ? ? ? ? ? ? ? ? ? ? ? r = c;
> - ? ? ? ? ? ? ? ? ? ? ? break;
> - ? ? ? ? ? ? ? }
> + ? ? ? list_for_each_entry(c, &conn->chan_l, list) {
> + ? ? ? ? ? ? ? if (c->ident == ident)
> + ? ? ? ? ? ? ? ? ? ? ? return c;
> ? ? ? ?}
> -
> - ? ? ? rcu_read_unlock();
> - ? ? ? return r;
> + ? ? ? return NULL;
> ?}
>
> ?static inline struct l2cap_chan *l2cap_get_chan_by_ident(struct l2cap_conn *conn, u8 ident)
> ?{
> ? ? ? ?struct l2cap_chan *c;
>
> + ? ? ? mutex_lock(&conn->chan_lock);
> ? ? ? ?c = __l2cap_get_chan_by_ident(conn, ident);
> ? ? ? ?if (c)
> ? ? ? ? ? ? ? ?lock_sock(c->sk);
> + ? ? ? mutex_unlock(&conn->chan_lock);
> ? ? ? ?return c;
> ?}
>
> @@ -259,6 +245,7 @@ static void l2cap_chan_timeout(struct work_struct *work)
>
> ? ? ? ?BT_DBG("chan %p state %d", chan, chan->state);
>
> + ? ? ? mutex_lock(&chan->conn->chan_lock);
> ? ? ? ?lock_sock(sk);
>
> ? ? ? ?if (chan->state == BT_CONNECTED || chan->state == BT_CONFIG)
> @@ -272,6 +259,7 @@ static void l2cap_chan_timeout(struct work_struct *work)
> ? ? ? ?l2cap_chan_close(chan, reason);
>
> ? ? ? ?release_sock(sk);
> + ? ? ? mutex_unlock(&chan->conn->chan_lock);
>
> ? ? ? ?chan->ops->close(chan->data);
> ? ? ? ?l2cap_chan_put(chan);
> @@ -357,7 +345,9 @@ static void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
>
> ? ? ? ?l2cap_chan_hold(chan);
>
> - ? ? ? list_add_rcu(&chan->list, &conn->chan_l);
> + ? ? ? mutex_lock(&conn->chan_lock);
> + ? ? ? list_add(&chan->list, &conn->chan_l);
> + ? ? ? mutex_unlock(&conn->chan_lock);
> ?}

Hmm, I'm not sure this is correct. Have you seen l2cap_connect_req()
calls __l2cap_get_chan_by_dcid() without the chan_lock? It's like that
because it was relying on RCU but now we do need to lock chan_lock
there as well. I'd recommend we turn l2cap_chan_add() into
__l2cap_chan_add() and lock chan_lock in the callers. Do you agree?

> ?/* Delete channel.
> @@ -374,8 +364,7 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
>
> ? ? ? ?if (conn) {
> ? ? ? ? ? ? ? ?/* Delete from channel list */
> - ? ? ? ? ? ? ? list_del_rcu(&chan->list);
> - ? ? ? ? ? ? ? synchronize_rcu();
> + ? ? ? ? ? ? ? list_del(&chan->list);
>
> ? ? ? ? ? ? ? ?l2cap_chan_put(chan);
>
> @@ -426,10 +415,14 @@ static void l2cap_chan_cleanup_listen(struct sock *parent)
> ? ? ? ?/* Close not yet accepted channels */
> ? ? ? ?while ((sk = bt_accept_dequeue(parent, NULL))) {
> ? ? ? ? ? ? ? ?struct l2cap_chan *chan = l2cap_pi(sk)->chan;
> +
> + ? ? ? ? ? ? ? mutex_lock(&chan->conn->chan_lock);
> ? ? ? ? ? ? ? ?__clear_chan_timer(chan);
> ? ? ? ? ? ? ? ?lock_sock(sk);
> ? ? ? ? ? ? ? ?l2cap_chan_close(chan, ECONNRESET);
> ? ? ? ? ? ? ? ?release_sock(sk);
> + ? ? ? ? ? ? ? mutex_unlock(&chan->conn->chan_lock);
> +
> ? ? ? ? ? ? ? ?chan->ops->close(chan->data);
> ? ? ? ?}
> ?}

I see you're adding locks around l2cap_chan_close() instead of just
locking list_del() which might be a good approach. Is that what you
want? Before we had to drop the lock to call l2cap_chan_close()
because it would be locked again to change the list (which I thought
wasn't good). And could you please remove the leftover comment in
l2cap_conn_start() about releasing the lock before calling
l2cap_chan_close()?

Have you seen l2cap_sock_shutdown() in l2cap_sock.c also calls
l2cap_chan_close()? Why don't we need to lock chan_lock there?

If that's what you wanted then please describe this kind of change in
the commit message (at least). This way we can refer to it if anything
happens or if we need to understand the change. I do think you write
short commit messages so please be more verbose.

> ?/* ---- L2CAP signalling commands ---- */
> @@ -2755,6 +2753,7 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd
> ? ? ? ? ? ? ? ? ? ? ? ?return -EFAULT;
> ? ? ? ?}
>
> + ? ? ? mutex_lock(&conn->chan_lock);
> ? ? ? ?sk = chan->sk;
>
> ? ? ? ?switch (result) {
> @@ -2782,6 +2781,8 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd
> ? ? ? ?}
>
> ? ? ? ?release_sock(sk);
> + ? ? ? mutex_unlock(&conn->chan_lock);
> +
> ? ? ? ?return 0;
> ?}
> @@ -3032,6 +3033,7 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd
> ? ? ? ?if (!chan)
> ? ? ? ? ? ? ? ?return 0;
>
> + ? ? ? mutex_lock(&conn->chan_lock);
> ? ? ? ?sk = chan->sk;
>
> ? ? ? ?rsp.dcid = cpu_to_le16(chan->scid);
> @@ -3042,6 +3044,7 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd
>
> ? ? ? ?l2cap_chan_del(chan, ECONNRESET);
> ? ? ? ?release_sock(sk);
> + ? ? ? mutex_unlock(&conn->chan_lock);
>
> ? ? ? ?chan->ops->close(chan->data);
> ? ? ? ?return 0;

Here in l2cap_connect_rsp() and l2cap_disconnect_req() it seems we
have a window where we can deadlock chan_lock and sock lock. Have you
seen that? It can be that it may never happen but it'd be good to
avoid that IMO. We receive a locked sock from either
l2cap_get_chan_by_scid() or l2cap_get_chan_by_ident() and then we try
to lock chan_lock. That's a different order you've been locking them.
Maybe we can lock chan_lock then use the unlocked versions and
explicitly lock sock after receiving chan?

> @@ -3063,6 +3066,7 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd
> ? ? ? ?if (!chan)
> ? ? ? ? ? ? ? ?return 0;
>
> + ? ? ? mutex_lock(&conn->chan_lock);
> ? ? ? ?sk = chan->sk;
>
> ? ? ? ?l2cap_chan_del(chan, 0);

The last comment also applies here. Besides, aren't we missing an unlock?

<snip>

How much have you been testing these changes? I'd like to avoid
introducing more bugs with changes are supposed to fix things so
please do test this as much as you can.

Regards,

--
Ulisses Furquim
ProFUSION embedded systems
http://profusion.mobi
Mobile: +55 19 9250 0942
Skype: ulissesffs

2012-02-03 16:09:57

by Marcel Holtmann

[permalink] [raw]
Subject: Re: [RFCv2 07/10] Bluetooth: Add socket error function

Hi Andrei,

> Use locked and unlocked versions to help removing socket
> locks from l2cap.
>
> Signed-off-by: Andrei Emeltchenko <[email protected]>
> ---
> net/bluetooth/l2cap_core.c | 23 +++++++++++++++++++----
> 1 files changed, 19 insertions(+), 4 deletions(-)
>
> diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
> index cdc9d0c..79d6996 100644
> --- a/net/bluetooth/l2cap_core.c
> +++ b/net/bluetooth/l2cap_core.c
> @@ -243,6 +243,20 @@ static void l2cap_state_change(struct l2cap_chan *chan, int state)
> release_sock(chan->sk);
> }
>
> +static inline void __l2cap_set_sock_err(struct sock *sk, int err)
> +{
> + sk->sk_err = err;
> +}
> +
> +static inline void l2cap_set_sock_err(struct l2cap_chan *chan, int err)
> +{
> + struct sock *sk = chan->sk;
> +
> + lock_sock(sk);
> + __l2cap_set_sock_err(sk, err);
> + release_sock(sk);
> +}
> +

actually this is a bad idea. The parameters are different.

Regards

Marcel



2012-02-03 16:08:33

by Marcel Holtmann

[permalink] [raw]
Subject: Re: [RFCv2 06/10] Bluetooth: Add locked and unlocked state_change

Hi Andrei,

> Split to locked and unlocked versions of l2cap_state_change helping
> to remove socket locks from l2cap code.
>
> Signed-off-by: Andrei Emeltchenko <[email protected]>
> ---
> net/bluetooth/l2cap_core.c | 39 +++++++++++++++++++++++----------------
> 1 files changed, 23 insertions(+), 16 deletions(-)

Acked-by: Marcel Holtmann <[email protected]>

Regards

Marcel



2012-02-03 16:07:52

by Marcel Holtmann

[permalink] [raw]
Subject: Re: [RFCv2 05/10] Bluetooth: Add l2cap_chan_lock

Hi Andrei,

> Channel lock will be used to lock L2CAP channels which are locked
> currently by socket locks.
>
> Signed-off-by: Andrei Emeltchenko <[email protected]>
> ---
> include/net/bluetooth/l2cap.h | 11 +++++++++++
> net/bluetooth/l2cap_core.c | 2 ++
> 2 files changed, 13 insertions(+), 0 deletions(-)

Acked-by: Marcel Holtmann <[email protected]>

Regards

Marcel



2012-02-03 16:06:29

by Marcel Holtmann

[permalink] [raw]
Subject: Re: [RFCv2 04/10] Bluetooth: Revert to mutexes from RCU list

Hi Andrei,

> Usage of RCU list looks not reasonalbe for a number of reasons:
> our code sleep and we have to use socket spinlocks, some parts
> of code are updaters thus we need to use mutexes anyway.
>
> Signed-off-by: Andrei Emeltchenko <[email protected]>
> ---
> net/bluetooth/l2cap_core.c | 110 ++++++++++++++++++++++---------------------
> 1 files changed, 56 insertions(+), 54 deletions(-)

I don't see anything wrong with this patch, but I am waiting for Ulisses
to take a look as well.

Regards

Marcel



2012-02-03 16:05:26

by Marcel Holtmann

[permalink] [raw]
Subject: Re: [RFCv2 03/10] Bluetooth: Remove unneeded sk variable

Hi Andrei,

> In debug use chan %p instead of sk.
>
> Signed-off-by: Andrei Emeltchenko <[email protected]>
> ---
> net/bluetooth/l2cap_core.c | 9 +++------
> 1 files changed, 3 insertions(+), 6 deletions(-)

Acked-by: Marcel Holtmann <[email protected]>

Regards

Marcel



2012-02-03 16:04:55

by Marcel Holtmann

[permalink] [raw]
Subject: Re: [RFCv2 02/10] Bluetooth: Clean up l2cap_chan_add

Hi Andrei,

> Change elseif to switch. This make sense even more with following
> patches which otherwise have to add elseifs statements.
>
> Signed-off-by: Andrei Emeltchenko <[email protected]>
> ---
> net/bluetooth/l2cap_core.c | 11 ++++++++---
> 1 files changed, 8 insertions(+), 3 deletions(-)

Acked-by: Marcel Holtmann <[email protected]>

Regards

Marcel



2012-02-03 16:04:16

by Marcel Holtmann

[permalink] [raw]
Subject: Re: [RFCv2 01/10] Bluetooth: Change chan_ready param from sk to chan

Hi Andrei,

> Change is needed to remove dependency on sk when possible
> before introducing l2cap channel lock.
>
> Signed-off-by: Andrei Emeltchenko <[email protected]>
> ---
> net/bluetooth/l2cap_core.c | 12 ++++++------
> 1 files changed, 6 insertions(+), 6 deletions(-)

Acked-by: Marcel Holtmann <[email protected]>

Regards

Marcel



2012-02-03 14:36:29

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv2 10/10] Bluetooth: Remove socket lock check

From: Andrei Emeltchenko <[email protected]>

Simplify code so that we do not need to check whether socket is locked.

Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/l2cap_sock.c | 8 ++++----
1 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index ddac4cb..358975a 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -125,15 +125,15 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al

err = l2cap_chan_connect(chan, la.l2_psm, la.l2_cid, &la.l2_bdaddr);
if (err)
- goto done;
+ return err;

lock_sock(sk);

err = bt_sock_wait_state(sk, BT_CONNECTED,
sock_sndtimeo(sk, flags & O_NONBLOCK));
-done:
- if (sock_owned_by_user(sk))
- release_sock(sk);
+
+ release_sock(sk);
+
return err;
}

--
1.7.8.3


2012-02-03 14:36:24

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv2 05/10] Bluetooth: Add l2cap_chan_lock

From: Andrei Emeltchenko <[email protected]>

Channel lock will be used to lock L2CAP channels which are locked
currently by socket locks.

Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/l2cap.h | 11 +++++++++++
net/bluetooth/l2cap_core.c | 2 ++
2 files changed, 13 insertions(+), 0 deletions(-)

diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index 42fdbb8..f9fe348 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -497,6 +497,7 @@ struct l2cap_chan {

void *data;
struct l2cap_ops *ops;
+ struct mutex lock;
};

struct l2cap_ops {
@@ -609,6 +610,16 @@ static inline void l2cap_chan_put(struct l2cap_chan *c)
kfree(c);
}

+static inline void l2cap_chan_lock(struct l2cap_chan *chan)
+{
+ mutex_lock(&chan->lock);
+}
+
+static inline void l2cap_chan_unlock(struct l2cap_chan *chan)
+{
+ mutex_unlock(&chan->lock);
+}
+
static inline void l2cap_set_timer(struct l2cap_chan *chan,
struct delayed_work *work, long timeout)
{
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 010ef75..bb828ca 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -273,6 +273,8 @@ struct l2cap_chan *l2cap_chan_create(struct sock *sk)
if (!chan)
return NULL;

+ mutex_init(&chan->lock);
+
chan->sk = sk;

write_lock(&chan_list_lock);
--
1.7.8.3


2012-02-03 14:36:27

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv2 08/10] Bluetooth: Change socket lock to chan lock

From: Andrei Emeltchenko <[email protected]>


Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/l2cap_core.c | 155 +++++++++++++++++++++++++++----------------
net/bluetooth/l2cap_sock.c | 13 +++-
2 files changed, 108 insertions(+), 60 deletions(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 79d6996..d70d0f5 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -261,13 +261,12 @@ static void l2cap_chan_timeout(struct work_struct *work)
{
struct l2cap_chan *chan = container_of(work, struct l2cap_chan,
chan_timer.work);
- struct sock *sk = chan->sk;
int reason;

BT_DBG("chan %p state %d", chan, chan->state);

mutex_lock(&chan->conn->chan_lock);
- lock_sock(sk);
+ l2cap_chan_lock(chan);

if (chan->state == BT_CONNECTED || chan->state == BT_CONFIG)
reason = ECONNREFUSED;
@@ -279,7 +278,7 @@ static void l2cap_chan_timeout(struct work_struct *work)

l2cap_chan_close(chan, reason);

- release_sock(sk);
+ l2cap_chan_unlock(chan);
mutex_unlock(&chan->conn->chan_lock);

chan->ops->close(chan->data);
@@ -395,6 +394,8 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
hci_conn_put(conn->hcon);
}

+ lock_sock(sk);
+
__l2cap_state_change(chan, BT_CLOSED);
sock_set_flag(sk, SOCK_ZAPPED);

@@ -407,6 +408,8 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
} else
sk->sk_state_change(sk);

+ release_sock(sk);
+
if (!(test_bit(CONF_OUTPUT_DONE, &chan->conf_state) &&
test_bit(CONF_INPUT_DONE, &chan->conf_state)))
return;
@@ -440,10 +443,10 @@ static void l2cap_chan_cleanup_listen(struct sock *parent)
struct l2cap_chan *chan = l2cap_pi(sk)->chan;

mutex_lock(&chan->conn->chan_lock);
+ l2cap_chan_lock(chan);
__clear_chan_timer(chan);
- lock_sock(sk);
l2cap_chan_close(chan, ECONNRESET);
- release_sock(sk);
+ l2cap_chan_unlock(chan);
mutex_unlock(&chan->conn->chan_lock);

chan->ops->close(chan->data);
@@ -459,10 +462,12 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason)

switch (chan->state) {
case BT_LISTEN:
+ lock_sock(sk);
l2cap_chan_cleanup_listen(sk);

__l2cap_state_change(chan, BT_CLOSED);
sock_set_flag(sk, SOCK_ZAPPED);
+ release_sock(sk);
break;

case BT_CONNECTED:
@@ -505,7 +510,9 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason)
break;

default:
+ lock_sock(sk);
sock_set_flag(sk, SOCK_ZAPPED);
+ release_sock(sk);
break;
}
}
@@ -733,14 +740,12 @@ static inline int l2cap_mode_supported(__u8 mode, __u32 feat_mask)

static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *chan, int err)
{
- struct sock *sk;
+ struct sock *sk = chan->sk;
struct l2cap_disconn_req req;

if (!conn)
return;

- sk = chan->sk;
-
if (chan->mode == L2CAP_MODE_ERTM) {
__clear_retrans_timer(chan);
__clear_monitor_timer(chan);
@@ -752,8 +757,10 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *c
l2cap_send_cmd(conn, l2cap_get_ident(conn),
L2CAP_DISCONN_REQ, sizeof(req), &req);

+ lock_sock(sk);
__l2cap_state_change(chan, BT_DISCONN);
__l2cap_set_sock_err(sk, err);
+ release_sock(sk);
}

/* ---- L2CAP connections ---- */
@@ -768,10 +775,10 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
list_for_each_entry_safe(chan, tmp, &conn->chan_l, list) {
struct sock *sk = chan->sk;

- bh_lock_sock(sk);
+ l2cap_chan_lock(chan);

if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
- bh_unlock_sock(sk);
+ l2cap_chan_unlock(chan);
continue;
}

@@ -780,7 +787,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)

if (!l2cap_chan_check_security(chan) ||
!__l2cap_no_conn_pending(chan)) {
- bh_unlock_sock(sk);
+ l2cap_chan_unlock(chan);
continue;
}

@@ -790,7 +797,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
/* l2cap_chan_close() calls list_del(chan)
* so release the lock */
l2cap_chan_close(chan, ECONNRESET);
- bh_unlock_sock(sk);
+ l2cap_chan_unlock(chan);
continue;
}

@@ -810,6 +817,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
rsp.dcid = cpu_to_le16(chan->scid);

if (l2cap_chan_check_security(chan)) {
+ lock_sock(sk);
if (bt_sk(sk)->defer_setup) {
struct sock *parent = bt_sk(sk)->parent;
rsp.result = cpu_to_le16(L2CAP_CR_PEND);
@@ -822,6 +830,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS);
rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
}
+ release_sock(sk);
} else {
rsp.result = cpu_to_le16(L2CAP_CR_PEND);
rsp.status = cpu_to_le16(L2CAP_CS_AUTHEN_PEND);
@@ -832,7 +841,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)

if (test_bit(CONF_REQ_SENT, &chan->conf_state) ||
rsp.result != L2CAP_CR_SUCCESS) {
- bh_unlock_sock(sk);
+ l2cap_chan_unlock(chan);
continue;
}

@@ -842,7 +851,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
chan->num_conf_req++;
}

- bh_unlock_sock(sk);
+ l2cap_chan_unlock(chan);
}

mutex_unlock(&conn->chan_lock);
@@ -931,7 +940,11 @@ clean:
static void l2cap_chan_ready(struct l2cap_chan *chan)
{
struct sock *sk = chan->sk;
- struct sock *parent = bt_sk(sk)->parent;
+ struct sock *parent;
+
+ lock_sock(sk);
+
+ parent = bt_sk(sk)->parent;

BT_DBG("sk %p, parent %p", sk, parent);

@@ -943,6 +956,8 @@ static void l2cap_chan_ready(struct l2cap_chan *chan)

if (parent)
parent->sk_data_ready(parent, 0);
+
+ release_sock(sk);
}

static void l2cap_conn_ready(struct l2cap_conn *conn)
@@ -960,23 +975,25 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
mutex_lock(&conn->chan_lock);

list_for_each_entry(chan, &conn->chan_l, list) {
- struct sock *sk = chan->sk;

- bh_lock_sock(sk);
+ l2cap_chan_lock(chan);

if (conn->hcon->type == LE_LINK) {
if (smp_conn_security(conn, chan->sec_level))
l2cap_chan_ready(chan);

} else if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
+ struct sock *sk = chan->sk;
__clear_chan_timer(chan);
+ lock_sock(sk);
__l2cap_state_change(chan, BT_CONNECTED);
sk->sk_state_change(sk);
+ release_sock(sk);

} else if (chan->state == BT_CONNECT)
l2cap_do_start(chan);

- bh_unlock_sock(sk);
+ l2cap_chan_unlock(chan);
}

mutex_unlock(&conn->chan_lock);
@@ -994,8 +1011,12 @@ static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err)
list_for_each_entry(chan, &conn->chan_l, list) {
struct sock *sk = chan->sk;

+ lock_sock(sk);
+
if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags))
__l2cap_set_sock_err(sk, err);
+
+ release_sock(sk);
}

mutex_unlock(&conn->chan_lock);
@@ -1016,7 +1037,6 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
{
struct l2cap_conn *conn = hcon->l2cap_data;
struct l2cap_chan *chan, *l;
- struct sock *sk;

if (!conn)
return;
@@ -1029,10 +1049,12 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)

/* Kill channels */
list_for_each_entry_safe(chan, l, &conn->chan_l, list) {
- sk = chan->sk;
- lock_sock(sk);
+ l2cap_chan_lock(chan);
+
l2cap_chan_del(chan, err);
- release_sock(sk);
+
+ l2cap_chan_unlock(chan);
+
chan->ops->close(chan->data);
}

@@ -1300,40 +1322,39 @@ static void l2cap_monitor_timeout(struct work_struct *work)
{
struct l2cap_chan *chan = container_of(work, struct l2cap_chan,
monitor_timer.work);
- struct sock *sk = chan->sk;
-
BT_DBG("chan %p", chan);

- lock_sock(sk);
+ l2cap_chan_lock(chan);
+
if (chan->retry_count >= chan->remote_max_tx) {
l2cap_send_disconn_req(chan->conn, chan, ECONNABORTED);
- release_sock(sk);
- return;
+ goto done;
}

chan->retry_count++;
__set_monitor_timer(chan);

l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_POLL);
- release_sock(sk);
+done:
+ l2cap_chan_unlock(chan);
}

static void l2cap_retrans_timeout(struct work_struct *work)
{
struct l2cap_chan *chan = container_of(work, struct l2cap_chan,
retrans_timer.work);
- struct sock *sk = chan->sk;
-
BT_DBG("chan %p", chan);

- lock_sock(sk);
+ l2cap_chan_lock(chan);
+
chan->retry_count = 1;
__set_monitor_timer(chan);

set_bit(CONN_WAIT_F, &chan->conn_state);

l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_POLL);
- release_sock(sk);
+
+ l2cap_chan_unlock(chan);
}

static void l2cap_drop_acked_frames(struct l2cap_chan *chan)
@@ -2022,9 +2043,11 @@ static void l2cap_ack_timeout(struct work_struct *work)

BT_DBG("chan %p", chan);

- lock_sock(chan->sk);
+ l2cap_chan_lock(chan);
+
__l2cap_send_ack(chan);
- release_sock(chan->sk);
+
+ l2cap_chan_unlock(chan);

l2cap_chan_put(chan);
}
@@ -2767,16 +2790,18 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd
BT_DBG("dcid 0x%4.4x scid 0x%4.4x result 0x%2.2x status 0x%2.2x", dcid, scid, result, status);

if (scid) {
- chan = l2cap_get_chan_by_scid(conn, scid);
+ chan = __l2cap_get_chan_by_scid(conn, scid);
if (!chan)
return -EFAULT;
} else {
- chan = l2cap_get_chan_by_ident(conn, cmd->ident);
+ chan = __l2cap_get_chan_by_ident(conn, cmd->ident);
if (!chan)
return -EFAULT;
}

mutex_lock(&conn->chan_lock);
+ l2cap_chan_lock(chan);
+
sk = chan->sk;

switch (result) {
@@ -2803,7 +2828,7 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd
break;
}

- release_sock(sk);
+ l2cap_chan_unlock(chan);
mutex_unlock(&conn->chan_lock);

return 0;
@@ -2834,10 +2859,12 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr

BT_DBG("dcid 0x%4.4x flags 0x%2.2x", dcid, flags);

- chan = l2cap_get_chan_by_scid(conn, dcid);
+ chan = __l2cap_get_chan_by_scid(conn, dcid);
if (!chan)
return -ENOENT;

+ l2cap_chan_lock(chan);
+
sk = chan->sk;

if (chan->state != BT_CONFIG && chan->state != BT_CONNECT2) {
@@ -2927,7 +2954,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
}

unlock:
- release_sock(sk);
+ l2cap_chan_unlock(chan);
return 0;
}

@@ -2946,10 +2973,12 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
BT_DBG("scid 0x%4.4x flags 0x%2.2x result 0x%2.2x",
scid, flags, result);

- chan = l2cap_get_chan_by_scid(conn, scid);
+ chan = __l2cap_get_chan_by_scid(conn, scid);
if (!chan)
return 0;

+ l2cap_chan_lock(chan);
+
sk = chan->sk;

switch (result) {
@@ -3036,7 +3065,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
}

done:
- release_sock(sk);
+ l2cap_chan_unlock(chan);
return 0;
}

@@ -3053,21 +3082,26 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd

BT_DBG("scid 0x%4.4x dcid 0x%4.4x", scid, dcid);

- chan = l2cap_get_chan_by_scid(conn, dcid);
+ chan = __l2cap_get_chan_by_scid(conn, dcid);
if (!chan)
return 0;

mutex_lock(&conn->chan_lock);
+ l2cap_chan_lock(chan);
+
sk = chan->sk;

rsp.dcid = cpu_to_le16(chan->scid);
rsp.scid = cpu_to_le16(chan->dcid);
l2cap_send_cmd(conn, cmd->ident, L2CAP_DISCONN_RSP, sizeof(rsp), &rsp);

+ lock_sock(sk);
sk->sk_shutdown = SHUTDOWN_MASK;
+ release_sock(sk);

l2cap_chan_del(chan, ECONNRESET);
- release_sock(sk);
+
+ l2cap_chan_unlock(chan);
mutex_unlock(&conn->chan_lock);

chan->ops->close(chan->data);
@@ -3079,22 +3113,23 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd
struct l2cap_disconn_rsp *rsp = (struct l2cap_disconn_rsp *) data;
u16 dcid, scid;
struct l2cap_chan *chan;
- struct sock *sk;

scid = __le16_to_cpu(rsp->scid);
dcid = __le16_to_cpu(rsp->dcid);

BT_DBG("dcid 0x%4.4x scid 0x%4.4x", dcid, scid);

- chan = l2cap_get_chan_by_scid(conn, scid);
+ chan = __l2cap_get_chan_by_scid(conn, scid);
if (!chan)
return 0;

mutex_lock(&conn->chan_lock);
- sk = chan->sk;
+ l2cap_chan_lock(chan);

l2cap_chan_del(chan, 0);
- release_sock(sk);
+
+ l2cap_chan_unlock(chan);
+ mutex_unlock(&conn->chan_lock);

chan->ops->close(chan->data);
return 0;
@@ -4243,18 +4278,17 @@ drop:
static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk_buff *skb)
{
struct l2cap_chan *chan;
- struct sock *sk = NULL;
u32 control;
u16 tx_seq;
int len;

- chan = l2cap_get_chan_by_scid(conn, cid);
+ chan = __l2cap_get_chan_by_scid(conn, cid);
if (!chan) {
BT_DBG("unknown cid 0x%4.4x", cid);
goto drop;
}

- sk = chan->sk;
+ l2cap_chan_lock(chan);

BT_DBG("chan %p, len %d", chan, skb->len);

@@ -4325,8 +4359,7 @@ drop:
kfree_skb(skb);

done:
- if (sk)
- release_sock(sk);
+ l2cap_chan_unlock(chan);

return 0;
}
@@ -4545,7 +4578,7 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
mutex_lock(&conn->chan_lock);

list_for_each_entry(chan, &conn->chan_l, list) {
- bh_lock_sock(sk);
+ l2cap_chan_lock(chan);

BT_DBG("chan->scid %d", chan->scid);

@@ -4555,19 +4588,19 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
l2cap_chan_ready(chan);
}

- bh_unlock_sock(sk);
+ l2cap_chan_unlock(chan);
continue;
}

if (test_bit(CONF_CONNECT_PEND, &chan->conf_state)) {
- bh_unlock_sock(sk);
+ l2cap_chan_unlock(chan);
continue;
}

if (!status && (chan->state == BT_CONNECTED ||
chan->state == BT_CONFIG)) {
l2cap_check_encryption(chan, encrypt);
- bh_unlock_sock(sk);
+ l2cap_chan_unlock(chan);
continue;
}

@@ -4588,9 +4621,12 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
msecs_to_jiffies(L2CAP_DISC_TIMEOUT));
}
} else if (chan->state == BT_CONNECT2) {
+ struct sock *sk = chan->sk;
struct l2cap_conn_rsp rsp;
__u16 res, stat;

+ lock_sock(sk);
+
if (!status) {
if (bt_sk(sk)->defer_setup) {
struct sock *parent = bt_sk(sk)->parent;
@@ -4611,6 +4647,8 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
stat = L2CAP_CS_NO_INFO;
}

+ release_sock(sk);
+
rsp.scid = cpu_to_le16(chan->dcid);
rsp.dcid = cpu_to_le16(chan->scid);
rsp.result = cpu_to_le16(res);
@@ -4619,7 +4657,7 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
sizeof(rsp), &rsp);
}

- bh_unlock_sock(sk);
+ l2cap_chan_unlock(chan);
}

mutex_unlock(&conn->chan_lock);
@@ -4679,10 +4717,11 @@ int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
goto drop;
}

- chan = l2cap_get_chan_by_scid(conn, cid);
+ chan = __l2cap_get_chan_by_scid(conn, cid);

if (chan && chan->sk) {
struct sock *sk = chan->sk;
+ lock_sock(sk);

if (chan->imtu < len - L2CAP_HDR_SIZE) {
BT_ERR("Frame exceeding recv MTU (len %d, "
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 1636029..dea5418 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -809,7 +809,9 @@ static int l2cap_sock_shutdown(struct socket *sock, int how)
err = __l2cap_wait_ack(sk);

sk->sk_shutdown = SHUTDOWN_MASK;
+ release_sock(sk);
l2cap_chan_close(chan, 0);
+ lock_sock(sk);

if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime)
err = bt_sock_wait_state(sk, BT_CLOSED,
@@ -862,8 +864,12 @@ static int l2cap_sock_recv_cb(void *data, struct sk_buff *skb)
struct sock *sk = data;
struct l2cap_pinfo *pi = l2cap_pi(sk);

- if (pi->rx_busy_skb)
- return -ENOMEM;
+ lock_sock(sk);
+
+ if (pi->rx_busy_skb) {
+ err = -ENOMEM;
+ goto done;
+ }

err = sock_queue_rcv_skb(sk, skb);

@@ -882,6 +888,9 @@ static int l2cap_sock_recv_cb(void *data, struct sk_buff *skb)
err = 0;
}

+done:
+ release_sock(sk);
+
return err;
}

--
1.7.8.3


2012-02-03 14:36:28

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv2 09/10] Bluetooth: Use l2cap chan lock in socket connect

From: Andrei Emeltchenko <[email protected]>


Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/l2cap_core.c | 26 ++++++++++++++++++++------
net/bluetooth/l2cap_sock.c | 2 ++
2 files changed, 22 insertions(+), 6 deletions(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index d70d0f5..4e9de8c 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -1185,7 +1185,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *d

hci_dev_lock(hdev);

- lock_sock(sk);
+ l2cap_chan_lock(chan);

/* PSM must be odd and lsb of upper byte must be 0 */
if ((__le16_to_cpu(psm) & 0x0101) != 0x0001 && !cid &&
@@ -1212,18 +1212,20 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *d
goto done;
}

+ lock_sock(sk);
+
switch (sk->sk_state) {
case BT_CONNECT:
case BT_CONNECT2:
case BT_CONFIG:
/* Already connecting */
err = 0;
- goto done;
+ goto sock_release;

case BT_CONNECTED:
/* Already connected */
err = -EISCONN;
- goto done;
+ goto sock_release;

case BT_OPEN:
case BT_BOUND:
@@ -1232,11 +1234,14 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *d

default:
err = -EBADFD;
- goto done;
+ goto sock_release;
}

/* Set destination address and psm */
bacpy(&bt_sk(sk)->dst, dst);
+
+ release_sock(sk);
+
chan->psm = psm;
chan->dcid = cid;

@@ -1264,23 +1269,30 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *d
/* Update source addr of the socket */
bacpy(src, conn->src);

+ l2cap_chan_unlock(chan);
l2cap_chan_add(conn, chan);
+ l2cap_chan_lock(chan);

- __l2cap_state_change(chan, BT_CONNECT);
+ l2cap_state_change(chan, BT_CONNECT);
__set_chan_timer(chan, sk->sk_sndtimeo);

if (hcon->state == BT_CONNECTED) {
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
__clear_chan_timer(chan);
if (l2cap_chan_check_security(chan))
- __l2cap_state_change(chan, BT_CONNECTED);
+ l2cap_state_change(chan, BT_CONNECTED);
} else
l2cap_do_start(chan);
}

err = 0;
+ goto done;
+
+sock_release:
+ release_sock(sk);

done:
+ l2cap_chan_unlock(chan);
hci_dev_unlock(hdev);
hci_dev_put(hdev);
return err;
@@ -2707,7 +2719,9 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd

bt_accept_enqueue(parent, sk);

+ release_sock(parent);
l2cap_chan_add(conn, chan);
+ lock_sock(parent);

dcid = chan->scid;

diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index dea5418..ddac4cb 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -127,6 +127,8 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al
if (err)
goto done;

+ lock_sock(sk);
+
err = bt_sock_wait_state(sk, BT_CONNECTED,
sock_sndtimeo(sk, flags & O_NONBLOCK));
done:
--
1.7.8.3


2012-02-03 14:36:25

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv2 06/10] Bluetooth: Add locked and unlocked state_change

From: Andrei Emeltchenko <[email protected]>

Split to locked and unlocked versions of l2cap_state_change helping
to remove socket locks from l2cap code.

Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/l2cap_core.c | 39 +++++++++++++++++++++++----------------
1 files changed, 23 insertions(+), 16 deletions(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index bb828ca..cdc9d0c 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -227,7 +227,7 @@ static char *state_to_string(int state)
return "invalid state";
}

-static void l2cap_state_change(struct l2cap_chan *chan, int state)
+static void __l2cap_state_change(struct l2cap_chan *chan, int state)
{
BT_DBG("%p %s -> %s", chan, state_to_string(chan->state),
state_to_string(state));
@@ -236,6 +236,13 @@ static void l2cap_state_change(struct l2cap_chan *chan, int state)
chan->ops->state_change(chan->data, state);
}

+static void l2cap_state_change(struct l2cap_chan *chan, int state)
+{
+ lock_sock(chan->sk);
+ __l2cap_state_change(chan, state);
+ release_sock(chan->sk);
+}
+
static void l2cap_chan_timeout(struct work_struct *work)
{
struct l2cap_chan *chan = container_of(work, struct l2cap_chan,
@@ -374,7 +381,7 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
hci_conn_put(conn->hcon);
}

- l2cap_state_change(chan, BT_CLOSED);
+ __l2cap_state_change(chan, BT_CLOSED);
sock_set_flag(sk, SOCK_ZAPPED);

if (err)
@@ -440,7 +447,7 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason)
case BT_LISTEN:
l2cap_chan_cleanup_listen(sk);

- l2cap_state_change(chan, BT_CLOSED);
+ __l2cap_state_change(chan, BT_CLOSED);
sock_set_flag(sk, SOCK_ZAPPED);
break;

@@ -731,8 +738,8 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *c
l2cap_send_cmd(conn, l2cap_get_ident(conn),
L2CAP_DISCONN_REQ, sizeof(req), &req);

- l2cap_state_change(chan, BT_DISCONN);
sk->sk_err = err;
+ __l2cap_state_change(chan, BT_DISCONN);
}

/* ---- L2CAP connections ---- */
@@ -797,7 +804,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
parent->sk_data_ready(parent, 0);

} else {
- l2cap_state_change(chan, BT_CONFIG);
+ __l2cap_state_change(chan, BT_CONFIG);
rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS);
rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
}
@@ -900,7 +907,7 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)

__set_chan_timer(chan, sk->sk_sndtimeo);

- l2cap_state_change(chan, BT_CONNECTED);
+ __l2cap_state_change(chan, BT_CONNECTED);
parent->sk_data_ready(parent, 0);

clean:
@@ -917,7 +924,7 @@ static void l2cap_chan_ready(struct l2cap_chan *chan)
chan->conf_state = 0;
__clear_chan_timer(chan);

- l2cap_state_change(chan, BT_CONNECTED);
+ __l2cap_state_change(chan, BT_CONNECTED);
sk->sk_state_change(sk);

if (parent)
@@ -949,7 +956,7 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)

} else if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
__clear_chan_timer(chan);
- l2cap_state_change(chan, BT_CONNECTED);
+ __l2cap_state_change(chan, BT_CONNECTED);
sk->sk_state_change(sk);

} else if (chan->state == BT_CONNECT)
@@ -1223,14 +1230,14 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *d

l2cap_chan_add(conn, chan);

- l2cap_state_change(chan, BT_CONNECT);
+ __l2cap_state_change(chan, BT_CONNECT);
__set_chan_timer(chan, sk->sk_sndtimeo);

if (hcon->state == BT_CONNECTED) {
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
__clear_chan_timer(chan);
if (l2cap_chan_check_security(chan))
- l2cap_state_change(chan, BT_CONNECTED);
+ __l2cap_state_change(chan, BT_CONNECTED);
} else
l2cap_do_start(chan);
}
@@ -2674,22 +2681,22 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) {
if (l2cap_chan_check_security(chan)) {
if (bt_sk(sk)->defer_setup) {
- l2cap_state_change(chan, BT_CONNECT2);
+ __l2cap_state_change(chan, BT_CONNECT2);
result = L2CAP_CR_PEND;
status = L2CAP_CS_AUTHOR_PEND;
parent->sk_data_ready(parent, 0);
} else {
- l2cap_state_change(chan, BT_CONFIG);
+ __l2cap_state_change(chan, BT_CONFIG);
result = L2CAP_CR_SUCCESS;
status = L2CAP_CS_NO_INFO;
}
} else {
- l2cap_state_change(chan, BT_CONNECT2);
+ __l2cap_state_change(chan, BT_CONNECT2);
result = L2CAP_CR_PEND;
status = L2CAP_CS_AUTHEN_PEND;
}
} else {
- l2cap_state_change(chan, BT_CONNECT2);
+ __l2cap_state_change(chan, BT_CONNECT2);
result = L2CAP_CR_PEND;
status = L2CAP_CS_NO_INFO;
}
@@ -4577,12 +4584,12 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
if (parent)
parent->sk_data_ready(parent, 0);
} else {
- l2cap_state_change(chan, BT_CONFIG);
+ __l2cap_state_change(chan, BT_CONFIG);
res = L2CAP_CR_SUCCESS;
stat = L2CAP_CS_NO_INFO;
}
} else {
- l2cap_state_change(chan, BT_DISCONN);
+ __l2cap_state_change(chan, BT_DISCONN);
__set_chan_timer(chan,
msecs_to_jiffies(L2CAP_DISC_TIMEOUT));
res = L2CAP_CR_SEC_BLOCK;
--
1.7.8.3


2012-02-03 14:36:21

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv2 02/10] Bluetooth: Clean up l2cap_chan_add

From: Andrei Emeltchenko <[email protected]>

Change elseif to switch. This make sense even more with following
patches which otherwise have to add elseifs statements.

Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/l2cap_core.c | 11 ++++++++---
1 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 80fbfcf..48fc01e 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -320,7 +320,8 @@ static void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)

chan->conn = conn;

- if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED) {
+ switch (chan->chan_type) {
+ case L2CAP_CHAN_CONN_ORIENTED:
if (conn->hcon->type == LE_LINK) {
/* LE connection */
chan->omtu = L2CAP_LE_DEFAULT_MTU;
@@ -331,12 +332,16 @@ static void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
chan->scid = l2cap_alloc_cid(conn);
chan->omtu = L2CAP_DEFAULT_MTU;
}
- } else if (chan->chan_type == L2CAP_CHAN_CONN_LESS) {
+ break;
+
+ case L2CAP_CHAN_CONN_LESS:
/* Connectionless socket */
chan->scid = L2CAP_CID_CONN_LESS;
chan->dcid = L2CAP_CID_CONN_LESS;
chan->omtu = L2CAP_DEFAULT_MTU;
- } else {
+ break;
+
+ default:
/* Raw socket can send/recv signalling messages only */
chan->scid = L2CAP_CID_SIGNALING;
chan->dcid = L2CAP_CID_SIGNALING;
--
1.7.8.3


2012-02-03 14:36:23

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv2 04/10] Bluetooth: Revert to mutexes from RCU list

From: Andrei Emeltchenko <[email protected]>

Usage of RCU list looks not reasonalbe for a number of reasons:
our code sleep and we have to use socket spinlocks, some parts
of code are updaters thus we need to use mutexes anyway.

Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/l2cap_core.c | 110 ++++++++++++++++++++++---------------------
1 files changed, 56 insertions(+), 54 deletions(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index f1a6b3c..010ef75 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -77,36 +77,24 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn,

static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn, u16 cid)
{
- struct l2cap_chan *c, *r = NULL;
-
- rcu_read_lock();
+ struct l2cap_chan *c;

- list_for_each_entry_rcu(c, &conn->chan_l, list) {
- if (c->dcid == cid) {
- r = c;
- break;
- }
+ list_for_each_entry(c, &conn->chan_l, list) {
+ if (c->dcid == cid)
+ return c;
}
-
- rcu_read_unlock();
- return r;
+ return NULL;
}

static struct l2cap_chan *__l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 cid)
{
- struct l2cap_chan *c, *r = NULL;
-
- rcu_read_lock();
+ struct l2cap_chan *c;

- list_for_each_entry_rcu(c, &conn->chan_l, list) {
- if (c->scid == cid) {
- r = c;
- break;
- }
+ list_for_each_entry(c, &conn->chan_l, list) {
+ if (c->scid == cid)
+ return c;
}
-
- rcu_read_unlock();
- return r;
+ return NULL;
}

/* Find channel with given SCID.
@@ -115,36 +103,34 @@ static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 ci
{
struct l2cap_chan *c;

+ mutex_lock(&conn->chan_lock);
c = __l2cap_get_chan_by_scid(conn, cid);
if (c)
lock_sock(c->sk);
+ mutex_unlock(&conn->chan_lock);
return c;
}

static struct l2cap_chan *__l2cap_get_chan_by_ident(struct l2cap_conn *conn, u8 ident)
{
- struct l2cap_chan *c, *r = NULL;
-
- rcu_read_lock();
+ struct l2cap_chan *c;

- list_for_each_entry_rcu(c, &conn->chan_l, list) {
- if (c->ident == ident) {
- r = c;
- break;
- }
+ list_for_each_entry(c, &conn->chan_l, list) {
+ if (c->ident == ident)
+ return c;
}
-
- rcu_read_unlock();
- return r;
+ return NULL;
}

static inline struct l2cap_chan *l2cap_get_chan_by_ident(struct l2cap_conn *conn, u8 ident)
{
struct l2cap_chan *c;

+ mutex_lock(&conn->chan_lock);
c = __l2cap_get_chan_by_ident(conn, ident);
if (c)
lock_sock(c->sk);
+ mutex_unlock(&conn->chan_lock);
return c;
}

@@ -259,6 +245,7 @@ static void l2cap_chan_timeout(struct work_struct *work)

BT_DBG("chan %p state %d", chan, chan->state);

+ mutex_lock(&chan->conn->chan_lock);
lock_sock(sk);

if (chan->state == BT_CONNECTED || chan->state == BT_CONFIG)
@@ -272,6 +259,7 @@ static void l2cap_chan_timeout(struct work_struct *work)
l2cap_chan_close(chan, reason);

release_sock(sk);
+ mutex_unlock(&chan->conn->chan_lock);

chan->ops->close(chan->data);
l2cap_chan_put(chan);
@@ -357,7 +345,9 @@ static void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)

l2cap_chan_hold(chan);

- list_add_rcu(&chan->list, &conn->chan_l);
+ mutex_lock(&conn->chan_lock);
+ list_add(&chan->list, &conn->chan_l);
+ mutex_unlock(&conn->chan_lock);
}

/* Delete channel.
@@ -374,8 +364,7 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)

if (conn) {
/* Delete from channel list */
- list_del_rcu(&chan->list);
- synchronize_rcu();
+ list_del(&chan->list);

l2cap_chan_put(chan);

@@ -426,10 +415,14 @@ static void l2cap_chan_cleanup_listen(struct sock *parent)
/* Close not yet accepted channels */
while ((sk = bt_accept_dequeue(parent, NULL))) {
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
+
+ mutex_lock(&chan->conn->chan_lock);
__clear_chan_timer(chan);
lock_sock(sk);
l2cap_chan_close(chan, ECONNRESET);
release_sock(sk);
+ mutex_unlock(&chan->conn->chan_lock);
+
chan->ops->close(chan->data);
}
}
@@ -743,13 +736,13 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *c
/* ---- L2CAP connections ---- */
static void l2cap_conn_start(struct l2cap_conn *conn)
{
- struct l2cap_chan *chan;
+ struct l2cap_chan *chan, *tmp;

BT_DBG("conn %p", conn);

- rcu_read_lock();
+ mutex_lock(&conn->chan_lock);

- list_for_each_entry_rcu(chan, &conn->chan_l, list) {
+ list_for_each_entry_safe(chan, tmp, &conn->chan_l, list) {
struct sock *sk = chan->sk;

bh_lock_sock(sk);
@@ -829,7 +822,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
bh_unlock_sock(sk);
}

- rcu_read_unlock();
+ mutex_unlock(&conn->chan_lock);
}

/* Find socket with cid and source bdaddr.
@@ -941,9 +934,9 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
if (conn->hcon->out && conn->hcon->type == LE_LINK)
smp_conn_security(conn, conn->hcon->pending_sec_level);

- rcu_read_lock();
+ mutex_lock(&conn->chan_lock);

- list_for_each_entry_rcu(chan, &conn->chan_l, list) {
+ list_for_each_entry(chan, &conn->chan_l, list) {
struct sock *sk = chan->sk;

bh_lock_sock(sk);
@@ -963,7 +956,7 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
bh_unlock_sock(sk);
}

- rcu_read_unlock();
+ mutex_unlock(&conn->chan_lock);
}

/* Notify sockets that we cannot guaranty reliability anymore */
@@ -973,16 +966,16 @@ static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err)

BT_DBG("conn %p", conn);

- rcu_read_lock();
+ mutex_lock(&conn->chan_lock);

- list_for_each_entry_rcu(chan, &conn->chan_l, list) {
+ list_for_each_entry(chan, &conn->chan_l, list) {
struct sock *sk = chan->sk;

if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags))
sk->sk_err = err;
}

- rcu_read_unlock();
+ mutex_unlock(&conn->chan_lock);
}

static void l2cap_info_timeout(struct work_struct *work)
@@ -1009,6 +1002,8 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)

kfree_skb(conn->rx_skb);

+ mutex_lock(&conn->chan_lock);
+
/* Kill channels */
list_for_each_entry_safe(chan, l, &conn->chan_l, list) {
sk = chan->sk;
@@ -1018,6 +1013,8 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
chan->ops->close(chan->data);
}

+ mutex_unlock(&conn->chan_lock);
+
hci_chan_del(conn->hchan);

if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT)
@@ -1075,6 +1072,7 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
conn->feat_mask = 0;

spin_lock_init(&conn->lock);
+ mutex_init(&conn->chan_lock);

INIT_LIST_HEAD(&conn->chan_l);

@@ -1815,9 +1813,9 @@ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)

BT_DBG("conn %p", conn);

- rcu_read_lock();
+ mutex_lock(&conn->chan_lock);

- list_for_each_entry_rcu(chan, &conn->chan_l, list) {
+ list_for_each_entry(chan, &conn->chan_l, list) {
struct sock *sk = chan->sk;
if (chan->chan_type != L2CAP_CHAN_RAW)
continue;
@@ -1833,7 +1831,7 @@ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
kfree_skb(nskb);
}

- rcu_read_unlock();
+ mutex_unlock(&conn->chan_lock);
}

/* ---- L2CAP signalling commands ---- */
@@ -2755,6 +2753,7 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd
return -EFAULT;
}

+ mutex_lock(&conn->chan_lock);
sk = chan->sk;

switch (result) {
@@ -2782,6 +2781,8 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd
}

release_sock(sk);
+ mutex_unlock(&conn->chan_lock);
+
return 0;
}

@@ -3032,6 +3033,7 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd
if (!chan)
return 0;

+ mutex_lock(&conn->chan_lock);
sk = chan->sk;

rsp.dcid = cpu_to_le16(chan->scid);
@@ -3042,6 +3044,7 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd

l2cap_chan_del(chan, ECONNRESET);
release_sock(sk);
+ mutex_unlock(&conn->chan_lock);

chan->ops->close(chan->data);
return 0;
@@ -3063,6 +3066,7 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd
if (!chan)
return 0;

+ mutex_lock(&conn->chan_lock);
sk = chan->sk;

l2cap_chan_del(chan, 0);
@@ -4514,11 +4518,9 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
cancel_delayed_work(&conn->security_timer);
}

- rcu_read_lock();
-
- list_for_each_entry_rcu(chan, &conn->chan_l, list) {
- struct sock *sk = chan->sk;
+ mutex_lock(&conn->chan_lock);

+ list_for_each_entry(chan, &conn->chan_l, list) {
bh_lock_sock(sk);

BT_DBG("chan->scid %d", chan->scid);
@@ -4596,7 +4598,7 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
bh_unlock_sock(sk);
}

- rcu_read_unlock();
+ mutex_unlock(&conn->chan_lock);

return 0;
}
--
1.7.8.3


2012-02-03 14:36:26

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv2 07/10] Bluetooth: Add socket error function

From: Andrei Emeltchenko <[email protected]>

Use locked and unlocked versions to help removing socket
locks from l2cap.

Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/l2cap_core.c | 23 +++++++++++++++++++----
1 files changed, 19 insertions(+), 4 deletions(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index cdc9d0c..79d6996 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -243,6 +243,20 @@ static void l2cap_state_change(struct l2cap_chan *chan, int state)
release_sock(chan->sk);
}

+static inline void __l2cap_set_sock_err(struct sock *sk, int err)
+{
+ sk->sk_err = err;
+}
+
+static inline void l2cap_set_sock_err(struct l2cap_chan *chan, int err)
+{
+ struct sock *sk = chan->sk;
+
+ lock_sock(sk);
+ __l2cap_set_sock_err(sk, err);
+ release_sock(sk);
+}
+
static void l2cap_chan_timeout(struct work_struct *work)
{
struct l2cap_chan *chan = container_of(work, struct l2cap_chan,
@@ -385,7 +399,7 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
sock_set_flag(sk, SOCK_ZAPPED);

if (err)
- sk->sk_err = err;
+ __l2cap_set_sock_err(sk, err);

if (parent) {
bt_accept_unlink(sk);
@@ -738,8 +752,8 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *c
l2cap_send_cmd(conn, l2cap_get_ident(conn),
L2CAP_DISCONN_REQ, sizeof(req), &req);

- sk->sk_err = err;
__l2cap_state_change(chan, BT_DISCONN);
+ __l2cap_set_sock_err(sk, err);
}

/* ---- L2CAP connections ---- */
@@ -981,7 +995,7 @@ static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err)
struct sock *sk = chan->sk;

if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags))
- sk->sk_err = err;
+ __l2cap_set_sock_err(sk, err);
}

mutex_unlock(&conn->chan_lock);
@@ -2995,7 +3009,8 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
}

default:
- sk->sk_err = ECONNRESET;
+ l2cap_set_sock_err(chan, ECONNRESET);
+
__set_chan_timer(chan,
msecs_to_jiffies(L2CAP_DISC_REJ_TIMEOUT));
l2cap_send_disconn_req(conn, chan, ECONNRESET);
--
1.7.8.3


2012-02-03 14:36:22

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv2 03/10] Bluetooth: Remove unneeded sk variable

From: Andrei Emeltchenko <[email protected]>

In debug use chan %p instead of sk.

Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/l2cap_core.c | 9 +++------
1 files changed, 3 insertions(+), 6 deletions(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 48fc01e..f1a6b3c 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -1561,13 +1561,12 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan,
struct msghdr *msg, size_t len,
u32 priority)
{
- struct sock *sk = chan->sk;
struct l2cap_conn *conn = chan->conn;
struct sk_buff *skb;
int err, count, hlen = L2CAP_HDR_SIZE + L2CAP_PSMLEN_SIZE;
struct l2cap_hdr *lh;

- BT_DBG("sk %p len %d priority %u", sk, (int)len, priority);
+ BT_DBG("chan %p len %d priority %u", chan, (int)len, priority);

count = min_t(unsigned int, (conn->mtu - hlen), len);

@@ -1597,13 +1596,12 @@ static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan,
struct msghdr *msg, size_t len,
u32 priority)
{
- struct sock *sk = chan->sk;
struct l2cap_conn *conn = chan->conn;
struct sk_buff *skb;
int err, count, hlen = L2CAP_HDR_SIZE;
struct l2cap_hdr *lh;

- BT_DBG("sk %p len %d", sk, (int)len);
+ BT_DBG("chan %p len %d", chan, (int)len);

count = min_t(unsigned int, (conn->mtu - hlen), len);

@@ -1632,13 +1630,12 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan,
struct msghdr *msg, size_t len,
u32 control, u16 sdulen)
{
- struct sock *sk = chan->sk;
struct l2cap_conn *conn = chan->conn;
struct sk_buff *skb;
int err, count, hlen;
struct l2cap_hdr *lh;

- BT_DBG("sk %p len %d", sk, (int)len);
+ BT_DBG("chan %p len %d", chan, (int)len);

if (!conn)
return ERR_PTR(-ENOTCONN);
--
1.7.8.3


2012-02-03 14:36:20

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv2 01/10] Bluetooth: Change chan_ready param from sk to chan

From: Andrei Emeltchenko <[email protected]>

Change is needed to remove dependency on sk when possible
before introducing l2cap channel lock.

Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/l2cap_core.c | 12 ++++++------
1 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 09cd860..80fbfcf 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -907,9 +907,9 @@ clean:
release_sock(parent);
}

-static void l2cap_chan_ready(struct sock *sk)
+static void l2cap_chan_ready(struct l2cap_chan *chan)
{
- struct l2cap_chan *chan = l2cap_pi(sk)->chan;
+ struct sock *sk = chan->sk;
struct sock *parent = bt_sk(sk)->parent;

BT_DBG("sk %p, parent %p", sk, parent);
@@ -945,7 +945,7 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)

if (conn->hcon->type == LE_LINK) {
if (smp_conn_security(conn, chan->sec_level))
- l2cap_chan_ready(sk);
+ l2cap_chan_ready(chan);

} else if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
__clear_chan_timer(chan);
@@ -2874,7 +2874,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
if (chan->mode == L2CAP_MODE_ERTM)
l2cap_ertm_init(chan);

- l2cap_chan_ready(sk);
+ l2cap_chan_ready(chan);
goto unlock;
}

@@ -3005,7 +3005,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
if (chan->mode == L2CAP_MODE_ERTM)
l2cap_ertm_init(chan);

- l2cap_chan_ready(sk);
+ l2cap_chan_ready(chan);
}

done:
@@ -4524,7 +4524,7 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
if (chan->scid == L2CAP_CID_LE_DATA) {
if (!status && encrypt) {
chan->sec_level = hcon->sec_level;
- l2cap_chan_ready(sk);
+ l2cap_chan_ready(chan);
}

bh_unlock_sock(sk);
--
1.7.8.3