Bluetooth: Use proper nesting annotation for l2cap_chan lock
By default lockdep considers all L2CAP channels equal. This would mean that we get warnings if a channel is locked when another one's lock is tried to be acquired in the same thread. This kind of inter-channel locking dependencies exist in the form of parent-child channels as well as any channel wishing to elevate the security by requesting procedures on the SMP channel. To eliminate the chance for these lockdep warnings we introduce a nesting level for each channel and use that when acquiring the channel lock. For now there exists the earlier mentioned three identified categories: SMP, "normal" channels and parent channels (i.e. those in BT_LISTEN state). The nesting level is defined as atomic_t since we need access to it before the lock is actually acquired. Signed-off-by: Johan Hedberg <johan.hedberg@intel.com> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
This commit is contained in:
		
					parent
					
						
							
								24ccb9f4f7
							
						
					
				
			
			
				commit
				
					
						abe84903a8
					
				
			
		
					 3 changed files with 33 additions and 1 deletions
				
			
		| 
						 | 
					@ -28,6 +28,7 @@
 | 
				
			||||||
#define __L2CAP_H
 | 
					#define __L2CAP_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <asm/unaligned.h>
 | 
					#include <asm/unaligned.h>
 | 
				
			||||||
 | 
					#include <linux/atomic.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* L2CAP defaults */
 | 
					/* L2CAP defaults */
 | 
				
			||||||
#define L2CAP_DEFAULT_MTU		672
 | 
					#define L2CAP_DEFAULT_MTU		672
 | 
				
			||||||
| 
						 | 
					@ -481,6 +482,7 @@ struct l2cap_chan {
 | 
				
			||||||
	struct hci_conn		*hs_hcon;
 | 
						struct hci_conn		*hs_hcon;
 | 
				
			||||||
	struct hci_chan		*hs_hchan;
 | 
						struct hci_chan		*hs_hchan;
 | 
				
			||||||
	struct kref	kref;
 | 
						struct kref	kref;
 | 
				
			||||||
 | 
						atomic_t	nesting;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	__u8		state;
 | 
						__u8		state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -713,6 +715,17 @@ enum {
 | 
				
			||||||
	FLAG_HOLD_HCI_CONN,
 | 
						FLAG_HOLD_HCI_CONN,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Lock nesting levels for L2CAP channels. We need these because lockdep
 | 
				
			||||||
 | 
					 * otherwise considers all channels equal and will e.g. complain about a
 | 
				
			||||||
 | 
					 * connection oriented channel triggering SMP procedures or a listening
 | 
				
			||||||
 | 
					 * channel creating and locking a child channel.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					enum {
 | 
				
			||||||
 | 
						L2CAP_NESTING_SMP,
 | 
				
			||||||
 | 
						L2CAP_NESTING_NORMAL,
 | 
				
			||||||
 | 
						L2CAP_NESTING_PARENT,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum {
 | 
					enum {
 | 
				
			||||||
	L2CAP_TX_STATE_XMIT,
 | 
						L2CAP_TX_STATE_XMIT,
 | 
				
			||||||
	L2CAP_TX_STATE_WAIT_F,
 | 
						L2CAP_TX_STATE_WAIT_F,
 | 
				
			||||||
| 
						 | 
					@ -778,7 +791,7 @@ void l2cap_chan_put(struct l2cap_chan *c);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline void l2cap_chan_lock(struct l2cap_chan *chan)
 | 
					static inline void l2cap_chan_lock(struct l2cap_chan *chan)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	mutex_lock(&chan->lock);
 | 
						mutex_lock_nested(&chan->lock, atomic_read(&chan->nesting));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline void l2cap_chan_unlock(struct l2cap_chan *chan)
 | 
					static inline void l2cap_chan_unlock(struct l2cap_chan *chan)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -285,6 +285,12 @@ static int l2cap_sock_listen(struct socket *sock, int backlog)
 | 
				
			||||||
	sk->sk_max_ack_backlog = backlog;
 | 
						sk->sk_max_ack_backlog = backlog;
 | 
				
			||||||
	sk->sk_ack_backlog = 0;
 | 
						sk->sk_ack_backlog = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Listening channels need to use nested locking in order not to
 | 
				
			||||||
 | 
						 * cause lockdep warnings when the created child channels end up
 | 
				
			||||||
 | 
						 * being locked in the same thread as the parent channel.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						atomic_set(&chan->nesting, L2CAP_NESTING_PARENT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	chan->state = BT_LISTEN;
 | 
						chan->state = BT_LISTEN;
 | 
				
			||||||
	sk->sk_state = BT_LISTEN;
 | 
						sk->sk_state = BT_LISTEN;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1497,6 +1503,9 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
 | 
				
			||||||
		l2cap_chan_set_defaults(chan);
 | 
							l2cap_chan_set_defaults(chan);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Set default lock nesting level */
 | 
				
			||||||
 | 
						atomic_set(&chan->nesting, L2CAP_NESTING_NORMAL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Default config options */
 | 
						/* Default config options */
 | 
				
			||||||
	chan->flush_to = L2CAP_DEFAULT_FLUSH_TO;
 | 
						chan->flush_to = L2CAP_DEFAULT_FLUSH_TO;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1658,6 +1658,13 @@ static inline struct l2cap_chan *smp_new_conn_cb(struct l2cap_chan *pchan)
 | 
				
			||||||
	chan->omtu	= pchan->omtu;
 | 
						chan->omtu	= pchan->omtu;
 | 
				
			||||||
	chan->mode	= pchan->mode;
 | 
						chan->mode	= pchan->mode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Other L2CAP channels may request SMP routines in order to
 | 
				
			||||||
 | 
						 * change the security level. This means that the SMP channel
 | 
				
			||||||
 | 
						 * lock must be considered in its own category to avoid lockdep
 | 
				
			||||||
 | 
						 * warnings.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						atomic_set(&chan->nesting, L2CAP_NESTING_SMP);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	BT_DBG("created chan %p", chan);
 | 
						BT_DBG("created chan %p", chan);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return chan;
 | 
						return chan;
 | 
				
			||||||
| 
						 | 
					@ -1715,6 +1722,9 @@ int smp_register(struct hci_dev *hdev)
 | 
				
			||||||
	chan->imtu = L2CAP_DEFAULT_MTU;
 | 
						chan->imtu = L2CAP_DEFAULT_MTU;
 | 
				
			||||||
	chan->ops = &smp_root_chan_ops;
 | 
						chan->ops = &smp_root_chan_ops;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Set correct nesting level for a parent/listening channel */
 | 
				
			||||||
 | 
						atomic_set(&chan->nesting, L2CAP_NESTING_PARENT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	hdev->smp_data = chan;
 | 
						hdev->smp_data = chan;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue