mirror of
https://github.com/torvalds/linux.git
synced 2026-05-25 15:41:52 +02:00
bluetooth pull request for net:
- af_bluetooth: serialize accept_q access - L2CAP: ecred_reconfigure: send packed pdu, not stack pointer - btmtk: accept too short WMT FUNC_CTRL events - hci_qca: Convert timeout from jiffies to ms -----BEGIN PGP SIGNATURE----- iQJNBAABCgA3FiEE7E6oRXp8w05ovYr/9JCA4xAyCykFAmoGBMsZHGx1aXoudm9u LmRlbnR6QGludGVsLmNvbQAKCRD0kIDjEDILKWqmD/9JZcOz8vrB/1XTwLyPcyAH xlQtplGkTylGLNh5Q2NctMkxaSTBNpwNqkcHu/TCa4qgu25iA8MmORwtn7Wcgsf7 I7gFevsfiGINQbGMpzHmWE059FeuAeXTuZtNYAOm0hal0JC8azsOef0PuswXJn/L FIBAmZGTZmeO20HtaV8Myp1sSwpPI3gK7HTpNrx2g20OKujxjuhVk1oHoi2Sn971 VujWFJ6rT2qfexq8Ljm+Uqj8LdmXfJZnL+pnoQ3aej4rQWKG20Pk0hJPF04cyE/9 tXVG27U9gdLrzWuOW2Qau4jcQBf/mtYVqvy8uAACQG79Brm2LDQVQ8vAOOEXp4Lb rz+CNlGSeQ3slkWIUCKlZWCv6lHb46pDxdKIpSD2GUSBToCxKkehCyCN8Msjo3/I 641l8QlAKyZhCAiYTVW/KOzDVjH7xe/QjpazGYimxtg7celcytqBSuKy6qt13GqK f9RAKaTJz3zX/XOoeGxEBIQgd524nl/fDb0wlsgT3AuQrSstliUJ23LhD4MKZPWe pTpcsPHDY7BNSFpXwprzlQ5a4xUNrDzgBDDNWmgz3qCOaS7An6gwbgS6HUhFQ0bK IQcVYxu31IKJfJJBNRoYpurp5rtRUdlEX+iVWUJAq3p2FM142ZOT0nkOp+s6s4+e qLjB6B+i6n2EnaCd8/qwdA== =GLxh -----END PGP SIGNATURE----- Merge tag 'for-net-2026-05-14' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth Luiz Augusto von Dentz says: ==================== bluetooth pull request for net: - af_bluetooth: serialize accept_q access - L2CAP: ecred_reconfigure: send packed pdu, not stack pointer - btmtk: accept too short WMT FUNC_CTRL events - hci_qca: Convert timeout from jiffies to ms * tag 'for-net-2026-05-14' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth: Bluetooth: hci_qca: Convert timeout from jiffies to ms Bluetooth: L2CAP: ecred_reconfigure: send packed pdu, not stack pointer Bluetooth: btmtk: accept too short WMT FUNC_CTRL events Bluetooth: serialize accept_q access ==================== Link: https://patch.msgid.link/20260514172340.1515042-1-luiz.dentz@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
23f3b04f15
|
|
@ -719,8 +719,8 @@ static int btmtk_usb_hci_wmt_sync(struct hci_dev *hdev,
|
|||
case BTMTK_WMT_FUNC_CTRL:
|
||||
if (!skb_pull_data(data->evt_skb,
|
||||
sizeof(wmt_evt_funcc->status))) {
|
||||
err = -EINVAL;
|
||||
goto err_free_skb;
|
||||
status = BTMTK_WMT_ON_UNDONE;
|
||||
break;
|
||||
}
|
||||
|
||||
wmt_evt_funcc = (struct btmtk_hci_wmt_evt_funcc *)wmt_evt;
|
||||
|
|
|
|||
|
|
@ -48,13 +48,12 @@
|
|||
#define HCI_MAX_IBS_SIZE 10
|
||||
|
||||
#define IBS_WAKE_RETRANS_TIMEOUT_MS 100
|
||||
#define IBS_BTSOC_TX_IDLE_TIMEOUT_MS 200
|
||||
#define IBS_BTSOC_TX_IDLE_TIMEOUT msecs_to_jiffies(200)
|
||||
#define IBS_HOST_TX_IDLE_TIMEOUT_MS 2000
|
||||
#define CMD_TRANS_TIMEOUT_MS 100
|
||||
#define MEMDUMP_TIMEOUT_MS 8000
|
||||
#define IBS_DISABLE_SSR_TIMEOUT_MS \
|
||||
(MEMDUMP_TIMEOUT_MS + FW_DOWNLOAD_TIMEOUT_MS)
|
||||
#define FW_DOWNLOAD_TIMEOUT_MS 3000
|
||||
#define CMD_TRANS_TIMEOUT msecs_to_jiffies(100)
|
||||
#define MEMDUMP_TIMEOUT msecs_to_jiffies(8000)
|
||||
#define FW_DOWNLOAD_TIMEOUT msecs_to_jiffies(3000)
|
||||
#define IBS_DISABLE_SSR_TIMEOUT (MEMDUMP_TIMEOUT + FW_DOWNLOAD_TIMEOUT)
|
||||
|
||||
/* susclk rate */
|
||||
#define SUSCLK_RATE_32KHZ 32768
|
||||
|
|
@ -1096,7 +1095,7 @@ static void qca_controller_memdump(struct work_struct *work)
|
|||
|
||||
queue_delayed_work(qca->workqueue,
|
||||
&qca->ctrl_memdump_timeout,
|
||||
msecs_to_jiffies(MEMDUMP_TIMEOUT_MS));
|
||||
MEMDUMP_TIMEOUT);
|
||||
skb_pull(skb, sizeof(qca_memdump->ram_dump_size));
|
||||
qca_memdump->current_seq_no = 0;
|
||||
qca_memdump->received_dump = 0;
|
||||
|
|
@ -1369,7 +1368,7 @@ static int qca_set_baudrate(struct hci_dev *hdev, uint8_t baudrate)
|
|||
|
||||
if (hu->serdev)
|
||||
serdev_device_wait_until_sent(hu->serdev,
|
||||
msecs_to_jiffies(CMD_TRANS_TIMEOUT_MS));
|
||||
CMD_TRANS_TIMEOUT);
|
||||
|
||||
/* Give the controller time to process the request */
|
||||
switch (qca_soc_type(hu)) {
|
||||
|
|
@ -1401,8 +1400,8 @@ static inline void host_set_baudrate(struct hci_uart *hu, unsigned int speed)
|
|||
|
||||
static int qca_send_power_pulse(struct hci_uart *hu, bool on)
|
||||
{
|
||||
int timeout = CMD_TRANS_TIMEOUT;
|
||||
int ret;
|
||||
int timeout = msecs_to_jiffies(CMD_TRANS_TIMEOUT_MS);
|
||||
u8 cmd = on ? QCA_WCN3990_POWERON_PULSE : QCA_WCN3990_POWEROFF_PULSE;
|
||||
|
||||
/* These power pulses are single byte command which are sent
|
||||
|
|
@ -1607,7 +1606,7 @@ static void qca_wait_for_dump_collection(struct hci_dev *hdev)
|
|||
struct qca_data *qca = hu->priv;
|
||||
|
||||
wait_on_bit_timeout(&qca->flags, QCA_MEMDUMP_COLLECTION,
|
||||
TASK_UNINTERRUPTIBLE, MEMDUMP_TIMEOUT_MS);
|
||||
TASK_UNINTERRUPTIBLE, MEMDUMP_TIMEOUT);
|
||||
|
||||
clear_bit(QCA_MEMDUMP_COLLECTION, &qca->flags);
|
||||
}
|
||||
|
|
@ -2591,7 +2590,7 @@ static void qca_serdev_remove(struct serdev_device *serdev)
|
|||
static void qca_serdev_shutdown(struct serdev_device *serdev)
|
||||
{
|
||||
int ret;
|
||||
int timeout = msecs_to_jiffies(CMD_TRANS_TIMEOUT_MS);
|
||||
int timeout = CMD_TRANS_TIMEOUT;
|
||||
struct qca_serdev *qcadev = serdev_device_get_drvdata(serdev);
|
||||
struct hci_uart *hu = &qcadev->serdev_hu;
|
||||
struct hci_dev *hdev = hu->hdev;
|
||||
|
|
@ -2648,7 +2647,7 @@ static int __maybe_unused qca_suspend(struct device *dev)
|
|||
bool tx_pending = false;
|
||||
int ret = 0;
|
||||
u8 cmd;
|
||||
u32 wait_timeout = 0;
|
||||
unsigned long wait_timeout = 0;
|
||||
|
||||
set_bit(QCA_SUSPENDING, &qca->flags);
|
||||
|
||||
|
|
@ -2669,15 +2668,15 @@ static int __maybe_unused qca_suspend(struct device *dev)
|
|||
if (test_bit(QCA_IBS_DISABLED, &qca->flags) ||
|
||||
test_bit(QCA_SSR_TRIGGERED, &qca->flags)) {
|
||||
wait_timeout = test_bit(QCA_SSR_TRIGGERED, &qca->flags) ?
|
||||
IBS_DISABLE_SSR_TIMEOUT_MS :
|
||||
FW_DOWNLOAD_TIMEOUT_MS;
|
||||
IBS_DISABLE_SSR_TIMEOUT :
|
||||
FW_DOWNLOAD_TIMEOUT;
|
||||
|
||||
/* QCA_IBS_DISABLED flag is set to true, During FW download
|
||||
* and during memory dump collection. It is reset to false,
|
||||
* After FW download complete.
|
||||
*/
|
||||
wait_on_bit_timeout(&qca->flags, QCA_IBS_DISABLED,
|
||||
TASK_UNINTERRUPTIBLE, msecs_to_jiffies(wait_timeout));
|
||||
TASK_UNINTERRUPTIBLE, wait_timeout);
|
||||
|
||||
if (test_bit(QCA_IBS_DISABLED, &qca->flags)) {
|
||||
bt_dev_err(hu->hdev, "SSR or FW download time out");
|
||||
|
|
@ -2729,7 +2728,7 @@ static int __maybe_unused qca_suspend(struct device *dev)
|
|||
|
||||
if (tx_pending) {
|
||||
serdev_device_wait_until_sent(hu->serdev,
|
||||
msecs_to_jiffies(CMD_TRANS_TIMEOUT_MS));
|
||||
CMD_TRANS_TIMEOUT);
|
||||
serial_clock_vote(HCI_IBS_TX_VOTE_CLOCK_OFF, hu);
|
||||
}
|
||||
|
||||
|
|
@ -2738,7 +2737,7 @@ static int __maybe_unused qca_suspend(struct device *dev)
|
|||
*/
|
||||
ret = wait_event_interruptible_timeout(qca->suspend_wait_q,
|
||||
qca->rx_ibs_state == HCI_IBS_RX_ASLEEP,
|
||||
msecs_to_jiffies(IBS_BTSOC_TX_IDLE_TIMEOUT_MS));
|
||||
IBS_BTSOC_TX_IDLE_TIMEOUT);
|
||||
if (ret == 0) {
|
||||
ret = -ETIMEDOUT;
|
||||
goto error;
|
||||
|
|
|
|||
|
|
@ -398,6 +398,7 @@ void baswap(bdaddr_t *dst, const bdaddr_t *src);
|
|||
struct bt_sock {
|
||||
struct sock sk;
|
||||
struct list_head accept_q;
|
||||
spinlock_t accept_q_lock; /* protects accept_q */
|
||||
struct sock *parent;
|
||||
unsigned long flags;
|
||||
void (*skb_msg_name)(struct sk_buff *, void *, int *);
|
||||
|
|
|
|||
|
|
@ -154,6 +154,7 @@ struct sock *bt_sock_alloc(struct net *net, struct socket *sock,
|
|||
|
||||
sock_init_data(sock, sk);
|
||||
INIT_LIST_HEAD(&bt_sk(sk)->accept_q);
|
||||
spin_lock_init(&bt_sk(sk)->accept_q_lock);
|
||||
|
||||
sock_reset_flag(sk, SOCK_ZAPPED);
|
||||
|
||||
|
|
@ -214,6 +215,7 @@ void bt_accept_enqueue(struct sock *parent, struct sock *sk, bool bh)
|
|||
{
|
||||
const struct cred *old_cred;
|
||||
struct pid *old_pid;
|
||||
struct bt_sock *par = bt_sk(parent);
|
||||
|
||||
BT_DBG("parent %p, sk %p", parent, sk);
|
||||
|
||||
|
|
@ -224,9 +226,13 @@ void bt_accept_enqueue(struct sock *parent, struct sock *sk, bool bh)
|
|||
else
|
||||
lock_sock_nested(sk, SINGLE_DEPTH_NESTING);
|
||||
|
||||
list_add_tail(&bt_sk(sk)->accept_q, &bt_sk(parent)->accept_q);
|
||||
bt_sk(sk)->parent = parent;
|
||||
|
||||
spin_lock_bh(&par->accept_q_lock);
|
||||
list_add_tail(&bt_sk(sk)->accept_q, &par->accept_q);
|
||||
sk_acceptq_added(parent);
|
||||
spin_unlock_bh(&par->accept_q_lock);
|
||||
|
||||
/* Copy credentials from parent since for incoming connections the
|
||||
* socket is allocated by the kernel.
|
||||
*/
|
||||
|
|
@ -244,8 +250,6 @@ void bt_accept_enqueue(struct sock *parent, struct sock *sk, bool bh)
|
|||
bh_unlock_sock(sk);
|
||||
else
|
||||
release_sock(sk);
|
||||
|
||||
sk_acceptq_added(parent);
|
||||
}
|
||||
EXPORT_SYMBOL(bt_accept_enqueue);
|
||||
|
||||
|
|
@ -254,45 +258,72 @@ EXPORT_SYMBOL(bt_accept_enqueue);
|
|||
*/
|
||||
void bt_accept_unlink(struct sock *sk)
|
||||
{
|
||||
struct sock *parent = bt_sk(sk)->parent;
|
||||
|
||||
BT_DBG("sk %p state %d", sk, sk->sk_state);
|
||||
|
||||
spin_lock_bh(&bt_sk(parent)->accept_q_lock);
|
||||
list_del_init(&bt_sk(sk)->accept_q);
|
||||
sk_acceptq_removed(bt_sk(sk)->parent);
|
||||
sk_acceptq_removed(parent);
|
||||
spin_unlock_bh(&bt_sk(parent)->accept_q_lock);
|
||||
bt_sk(sk)->parent = NULL;
|
||||
sock_put(sk);
|
||||
}
|
||||
EXPORT_SYMBOL(bt_accept_unlink);
|
||||
|
||||
static struct sock *bt_accept_get(struct sock *parent, struct sock *sk)
|
||||
{
|
||||
struct bt_sock *bt = bt_sk(parent);
|
||||
struct sock *next = NULL;
|
||||
|
||||
/* accept_q is modified from child teardown paths too, so take a
|
||||
* temporary reference before dropping the queue lock.
|
||||
*/
|
||||
spin_lock_bh(&bt->accept_q_lock);
|
||||
|
||||
if (sk) {
|
||||
if (bt_sk(sk)->parent != parent)
|
||||
goto out;
|
||||
|
||||
if (!list_is_last(&bt_sk(sk)->accept_q, &bt->accept_q)) {
|
||||
next = &list_next_entry(bt_sk(sk), accept_q)->sk;
|
||||
sock_hold(next);
|
||||
}
|
||||
} else if (!list_empty(&bt->accept_q)) {
|
||||
next = &list_first_entry(&bt->accept_q,
|
||||
struct bt_sock, accept_q)->sk;
|
||||
sock_hold(next);
|
||||
}
|
||||
|
||||
out:
|
||||
spin_unlock_bh(&bt->accept_q_lock);
|
||||
return next;
|
||||
}
|
||||
|
||||
struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock)
|
||||
{
|
||||
struct bt_sock *s, *n;
|
||||
struct sock *sk;
|
||||
struct sock *sk, *next;
|
||||
|
||||
BT_DBG("parent %p", parent);
|
||||
|
||||
restart:
|
||||
list_for_each_entry_safe(s, n, &bt_sk(parent)->accept_q, accept_q) {
|
||||
sk = (struct sock *)s;
|
||||
|
||||
for (sk = bt_accept_get(parent, NULL); sk; sk = next) {
|
||||
/* Prevent early freeing of sk due to unlink and sock_kill */
|
||||
sock_hold(sk);
|
||||
lock_sock(sk);
|
||||
|
||||
/* Check sk has not already been unlinked via
|
||||
* bt_accept_unlink() due to serialisation caused by sk locking
|
||||
*/
|
||||
if (!bt_sk(sk)->parent) {
|
||||
if (bt_sk(sk)->parent != parent) {
|
||||
BT_DBG("sk %p, already unlinked", sk);
|
||||
release_sock(sk);
|
||||
sock_put(sk);
|
||||
|
||||
/* Restart the loop as sk is no longer in the list
|
||||
* and also avoid a potential infinite loop because
|
||||
* list_for_each_entry_safe() is not thread safe.
|
||||
*/
|
||||
goto restart;
|
||||
}
|
||||
|
||||
next = bt_accept_get(parent, sk);
|
||||
|
||||
/* sk is safely in the parent list so reduce reference count */
|
||||
sock_put(sk);
|
||||
|
||||
|
|
@ -310,6 +341,8 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock)
|
|||
sock_graft(sk, newsock);
|
||||
|
||||
release_sock(sk);
|
||||
if (next)
|
||||
sock_put(next);
|
||||
return sk;
|
||||
}
|
||||
|
||||
|
|
@ -518,18 +551,28 @@ EXPORT_SYMBOL(bt_sock_stream_recvmsg);
|
|||
|
||||
static inline __poll_t bt_accept_poll(struct sock *parent)
|
||||
{
|
||||
struct bt_sock *s, *n;
|
||||
struct bt_sock *bt = bt_sk(parent);
|
||||
struct bt_sock *s;
|
||||
struct sock *sk;
|
||||
__poll_t mask = 0;
|
||||
|
||||
spin_lock_bh(&bt->accept_q_lock);
|
||||
list_for_each_entry(s, &bt->accept_q, accept_q) {
|
||||
int state;
|
||||
|
||||
list_for_each_entry_safe(s, n, &bt_sk(parent)->accept_q, accept_q) {
|
||||
sk = (struct sock *)s;
|
||||
if (sk->sk_state == BT_CONNECTED ||
|
||||
(test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags) &&
|
||||
sk->sk_state == BT_CONNECT2))
|
||||
return EPOLLIN | EPOLLRDNORM;
|
||||
}
|
||||
state = READ_ONCE(sk->sk_state);
|
||||
|
||||
return 0;
|
||||
if (state == BT_CONNECTED ||
|
||||
(test_bit(BT_SK_DEFER_SETUP, &bt->flags) &&
|
||||
state == BT_CONNECT2)) {
|
||||
mask = EPOLLIN | EPOLLRDNORM;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_bh(&bt->accept_q_lock);
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
__poll_t bt_sock_poll(struct file *file, struct socket *sock,
|
||||
|
|
|
|||
|
|
@ -7274,7 +7274,7 @@ static void l2cap_ecred_reconfigure(struct l2cap_chan *chan)
|
|||
chan->ident = l2cap_get_ident(conn);
|
||||
|
||||
l2cap_send_cmd(conn, chan->ident, L2CAP_ECRED_RECONF_REQ,
|
||||
sizeof(pdu), &pdu);
|
||||
struct_size(pdu, scid, 1), pdu);
|
||||
}
|
||||
|
||||
int l2cap_chan_reconfigure(struct l2cap_chan *chan, __u16 mtu)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user