From patchwork Wed Feb 28 20:03:39 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pauli Virtanen X-Patchwork-Id: 13575892 Received: from meesny.iki.fi (meesny.iki.fi [195.140.195.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C71D840843 for ; Wed, 28 Feb 2024 20:04:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=195.140.195.201 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1709150643; cv=pass; b=ERaH9VjhLW0g9MW/KfwoYwglUTQXSdBnM2YTcPZ15H9ScFqQp9Am7H2sQ0z6dYSsEyx1n3coLMbCg+dmEzI8be3IDNbgYjBsNJowWzhfEusEup29BpS217iFPtwsy2NItvwJg5h1KUKBCwgxq+pEPiqpYjSboasczU2QbWTP18I= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1709150643; c=relaxed/simple; bh=9JjouAwOyLhzIlTMD+TGKxS9AKHPZlfeje/8acJem04=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=XLGtvWF/1I4515EwOrp7bzfH7eW6ZfhzGkRA6uws/QBr8yhHZ4+re0ITH0MemQ1VqibOyaQeKRWh8PMRRV6RuPH1t5SJFBnUuf17CclrMQAfIuQKPbNi79nEAV9Tv26XUcK2PK6NSB7Kz6pSMIaxIazzBfQCEw3t952OlNiuRYw= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=iki.fi; spf=pass smtp.mailfrom=iki.fi; dkim=pass (1024-bit key) header.d=iki.fi header.i=@iki.fi header.b=xHOCFBzO; arc=pass smtp.client-ip=195.140.195.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=iki.fi Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=iki.fi Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=iki.fi header.i=@iki.fi header.b="xHOCFBzO" Received: from monolith.lan (unknown [193.138.7.158]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: pav) by meesny.iki.fi (Postfix) with ESMTPSA id 4TlQKT5CqlzygB; Wed, 28 Feb 2024 22:03:57 +0200 (EET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=iki.fi; s=meesny; t=1709150638; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=DGI5sx30aratdFRZ9rzSr7kDp70SlF9692eNmRNcwXo=; b=xHOCFBzOv+Y762z5aafSyeTg9GOZK9jQ1og8V1kkZOgYfVPmUPG5Mq1r+NKLTeUpUIdqlG py5C+teB145q4z+2qhxqhS1G1MLvohgsryJgU3clbjhSoNWaID2QwViHTO+Bhit4DKhUl1 dYMrlGxl7sEuYMqhWdWnp5KjDHOfDWw= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=iki.fi; s=meesny; t=1709150638; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=DGI5sx30aratdFRZ9rzSr7kDp70SlF9692eNmRNcwXo=; b=O4+xLrqbHA//aAvA9+RizK8Gj16JrfkitkpE+suwXMbjpko4x9EOzd4wotWSzgTD+mur+s YoA1W47qsgiTc+Hlau4VcHsbwpoe75qrqKZzShQx6ITUX89TsB9KeG4KBJopme38v/BMjb oeujCJzQi4k5Sn26R1gjESteMcJnr3Q= ARC-Authentication-Results: i=1; ORIGINATING; auth=pass smtp.auth=pav smtp.mailfrom=pav@iki.fi ARC-Seal: i=1; s=meesny; d=iki.fi; t=1709150638; a=rsa-sha256; cv=none; b=OrdlJrpwIno20ThwNZ5E2A2zxnxhCTLPQSU+DvOAD+p64SyFdieJ0FRAFNEA5dNpueCA9H aiT/E5HmFgQNfhL3u753FFhcNBhpMnksYDj8KwHCnlgysavafzC0RP512R3efNaEx8yvmp CRbi1mcN6CzwPXGGDHfvtHE6mPFnsIE= From: Pauli Virtanen To: linux-bluetooth@vger.kernel.org Cc: Pauli Virtanen Subject: [RFC PATCH 1/3] Bluetooth: add transmission latency tracking for ISO and ACL Date: Wed, 28 Feb 2024 22:03:39 +0200 Message-ID: X-Mailer: git-send-email 2.44.0 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-bluetooth@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Add mechanism to track specific skbs from socket to controller packet completion. This will be used to allow users submitting packets to sockets, to find for the latest completed packet: the total socket+controller queue length (in packets they submitted), controller packet completion timing, and current total transmission latency. This handles fragmentation of user-submitted packets and flow control in L2CAP, which make raw skb queue lengths not informative to the user, and does not change skb life cycle. Implementation: Sockets may mark selected skbs. When controller reports the corresponding packet completed, information in struct tx_latency in the corresponding hci_conn or hci_chan is updated. Add struct tx_latency to hci_conn and hci_chan, to track last completed packet status. Add hci_tx_info_queue to each hci_conn, and use it to track which packets they sent to controller. In packet completion event, pop info from the queues to find if the packet had marked skb and which hci_chan it came from. Allow safe concurrent reading of struct tx_latency. Signed-off-by: Pauli Virtanen --- include/net/bluetooth/bluetooth.h | 2 + include/net/bluetooth/hci_core.h | 30 ++++++++ net/bluetooth/hci_conn.c | 110 +++++++++++++++++++++++++++++- net/bluetooth/hci_core.c | 14 ++++ net/bluetooth/hci_event.c | 66 ++++++++++++++++++ 5 files changed, 221 insertions(+), 1 deletion(-) diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 7ffa8c192c3f..f6bdd040adaa 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -462,6 +462,8 @@ struct bt_skb_cb { u8 pkt_type; u8 force_active; u16 expect; + u16 user_sn; + u8 have_user_sn:1; u8 incoming:1; u8 pkt_status:2; union { diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 56fb42df44a3..e84a37c1e8b1 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -267,6 +268,26 @@ struct adv_info { struct delayed_work rpa_expired_cb; }; +struct tx_latency { + seqcount_t seq; + struct { + ktime_t time; + __s64 offset; + unsigned int queue; + } now; + __u16 sn; +}; + +struct tx_info_queue { + struct { + __u16 sn; + void *data; + } *info; + unsigned int size; + unsigned int head; + unsigned int num; +}; + #define HCI_MAX_ADV_INSTANCES 5 #define HCI_DEFAULT_ADV_DURATION 2 @@ -746,6 +767,9 @@ struct hci_conn { struct bt_iso_qos iso_qos; unsigned long flags; + struct tx_latency tx_latency; + struct tx_info_queue tx_info_queue; + enum conn_reasons conn_reason; __u8 abort_reason; @@ -803,6 +827,7 @@ struct hci_chan { unsigned int sent; __u8 state; bool amp; + struct tx_latency tx_latency; }; struct hci_conn_params { @@ -1546,6 +1571,11 @@ void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active); void hci_conn_failed(struct hci_conn *conn, u8 status); u8 hci_conn_set_handle(struct hci_conn *conn, u16 handle); +void hci_conn_tx_info_push(struct hci_conn *conn, void *ptr, __u16 sn); +void *hci_conn_tx_info_pop(struct hci_conn *conn, __u16 *sn); +void hci_mark_tx_latency(struct tx_latency *tx, struct sk_buff *skb); +void hci_copy_tx_latency(struct tx_latency *dst, struct tx_latency *src); + /* * hci_conn_get() and hci_conn_put() are used to control the life-time of an * "hci_conn" object. They do not guarantee that the hci_conn object is running, diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 3ad74f76983b..2c557eb63b93 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -138,6 +138,12 @@ void hci_connect_le_scan_cleanup(struct hci_conn *conn, u8 status) hci_update_passive_scan(hdev); } +static void hci_conn_tx_info_cleanup(struct hci_conn *conn) +{ + kfree(conn->tx_info_queue.info); + conn->tx_info_queue.info = NULL; +} + static void hci_conn_cleanup(struct hci_conn *conn) { struct hci_dev *hdev = conn->hdev; @@ -158,6 +164,8 @@ static void hci_conn_cleanup(struct hci_conn *conn) if (conn->cleanup) conn->cleanup(conn); + hci_conn_tx_info_cleanup(conn); + if (conn->type == SCO_LINK || conn->type == ESCO_LINK) { switch (conn->setting & SCO_AIRMODE_MASK) { case SCO_AIRMODE_CVSD: @@ -904,6 +912,39 @@ static int hci_conn_hash_alloc_unset(struct hci_dev *hdev) U16_MAX, GFP_ATOMIC); } +static int hci_conn_tx_info_init(struct hci_conn *conn) +{ + struct tx_info_queue *txq = &conn->tx_info_queue; + size_t size = 0; + + switch (conn->type) { + case ISO_LINK: + size = conn->hdev->iso_pkts; + if (!size) + size = conn->hdev->le_pkts; + if (!size) + size = conn->hdev->acl_pkts; + break; + case ACL_LINK: + size = conn->hdev->acl_pkts; + break; + } + + if (size) { + txq->info = kcalloc(size, sizeof(txq->info[0]), GFP_KERNEL); + if (!txq->info) + return -ENOMEM; + } else { + txq->info = NULL; + } + + txq->size = size; + txq->head = 0; + txq->num = 0; + + return 0; +} + struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst, u8 role, u16 handle) { @@ -932,6 +973,12 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst, conn->max_tx_power = HCI_TX_POWER_INVALID; conn->sync_handle = HCI_SYNC_HANDLE_INVALID; + seqcount_init(&conn->tx_latency.seq); + if (hci_conn_tx_info_init(conn) < 0) { + kfree(conn); + return NULL; + } + set_bit(HCI_CONN_POWER_SAVE, &conn->flags); conn->disc_timeout = HCI_DISCONN_TIMEOUT; @@ -1005,6 +1052,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst, struct hci_conn *hci_conn_add_unset(struct hci_dev *hdev, int type, bdaddr_t *dst, u8 role) { + struct hci_conn *conn; int handle; bt_dev_dbg(hdev, "dst %pMR", dst); @@ -1013,7 +1061,11 @@ struct hci_conn *hci_conn_add_unset(struct hci_dev *hdev, int type, if (unlikely(handle < 0)) return NULL; - return hci_conn_add(hdev, type, dst, role, handle); + conn = hci_conn_add(hdev, type, dst, role, handle); + if (!conn) + ida_free(&hdev->unset_handle_ida, handle); + + return conn; } static void hci_conn_cleanup_child(struct hci_conn *conn, u8 reason) @@ -2711,6 +2763,8 @@ struct hci_chan *hci_chan_create(struct hci_conn *conn) skb_queue_head_init(&chan->data_q); chan->state = BT_CONNECTED; + seqcount_init(&chan->tx_latency.seq); + list_add_rcu(&chan->list, &conn->chan_list); return chan; @@ -2928,3 +2982,57 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason) return hci_cmd_sync_queue_once(hdev, abort_conn_sync, conn, NULL); } + +void hci_conn_tx_info_push(struct hci_conn *conn, void *ptr, __u16 sn) +{ + struct tx_info_queue *txq = &conn->tx_info_queue; + unsigned int tail; + + if (!txq->num && !ptr) + return; + if (txq->num >= txq->size || !txq->info) + return; + + tail = (txq->head + txq->num) % txq->size; + txq->info[tail].data = ptr; + txq->info[tail].sn = sn; + txq->num++; +} + +void *hci_conn_tx_info_pop(struct hci_conn *conn, __u16 *sn) +{ + struct tx_info_queue *txq = &conn->tx_info_queue; + void *ptr; + + if (!txq->num || !txq->info || !txq->size) + return NULL; + + ptr = txq->info[txq->head].data; + *sn = txq->info[txq->head].sn; + txq->head = (txq->head + 1) % txq->size; + txq->num--; + + return ptr; +} + +void hci_mark_tx_latency(struct tx_latency *tx, struct sk_buff *skb) +{ + if (!skb) + return; + + /* Reads may be concurrent */ + WRITE_ONCE(tx->sn, tx->sn + 1u); + + bt_cb(skb)->user_sn = tx->sn; + bt_cb(skb)->have_user_sn = true; +} + +void hci_copy_tx_latency(struct tx_latency *dst, struct tx_latency *src) +{ + unsigned int seq; + + do { + seq = read_seqcount_begin(&src->seq); + dst->now = src->now; + } while (read_seqcount_retry(&src->seq, seq)); +} diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 6ca4c0df9f9c..bbdd8b28fb2c 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3703,6 +3703,9 @@ static void hci_sched_acl_pkt(struct hci_dev *hdev) (chan = hci_chan_sent(hdev, ACL_LINK, "e))) { u32 priority = (skb_peek(&chan->data_q))->priority; while (quote-- && (skb = skb_peek(&chan->data_q))) { + u16 sn = bt_cb(skb)->user_sn; + bool have_sn = bt_cb(skb)->have_user_sn; + BT_DBG("chan %p skb %p len %d priority %u", chan, skb, skb->len, skb->priority); @@ -3722,6 +3725,11 @@ static void hci_sched_acl_pkt(struct hci_dev *hdev) chan->sent++; chan->conn->sent++; + if (have_sn) + hci_conn_tx_info_push(chan->conn, chan, sn); + else + hci_conn_tx_info_push(chan->conn, NULL, 0); + /* Send pending SCO packets right away */ hci_sched_sco(hdev); hci_sched_esco(hdev); @@ -3875,9 +3883,15 @@ static void hci_sched_iso(struct hci_dev *hdev) hdev->le_pkts ? &hdev->le_cnt : &hdev->acl_cnt; while (*cnt && (conn = hci_low_sent(hdev, ISO_LINK, "e))) { while (quote-- && (skb = skb_dequeue(&conn->data_q))) { + u16 sn = bt_cb(skb)->user_sn; + bool have_sn = bt_cb(skb)->have_user_sn; + BT_DBG("skb %p len %d", skb, skb->len); + hci_send_frame(hdev, skb); + hci_conn_tx_info_push(conn, UINT_PTR(have_sn), sn); + conn->sent++; if (conn->sent == ~0) conn->sent = 0; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index bffd2c7ff608..144d4b481309 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4417,10 +4417,72 @@ static void hci_role_change_evt(struct hci_dev *hdev, void *data, hci_dev_unlock(hdev); } +static void update_acl_tx_latency(struct hci_conn *conn, int count, ktime_t now) +{ + unsigned int i; + + rcu_read_lock(); + + for (i = 0; i < count; ++i) { + struct hci_chan *chan; + void *ptr; + u16 sn; + + ptr = hci_conn_tx_info_pop(conn, &sn); + if (!ptr) + continue; + + list_for_each_entry_rcu(chan, &conn->chan_list, list) { + struct tx_latency *tx = &chan->tx_latency; + + if (chan != ptr) + continue; + + preempt_disable(); + write_seqcount_begin(&tx->seq); + tx->now.time = now; + tx->now.queue = (u16)(READ_ONCE(tx->sn) - sn); + tx->now.offset = 0; + write_seqcount_end(&tx->seq); + preempt_enable(); + } + } + + rcu_read_unlock(); +} + +static void update_iso_tx_latency(struct hci_conn *conn, int count, ktime_t now) +{ + struct tx_latency *tx = NULL; + u16 sn; + unsigned int i; + + for (i = 0; i < count; ++i) { + u16 tx_sn; + + if (hci_conn_tx_info_pop(conn, &tx_sn)) { + tx = &conn->tx_latency; + sn = tx_sn; + } + } + + if (!tx) + return; + + preempt_disable(); + write_seqcount_begin(&tx->seq); + tx->now.time = now; + tx->now.queue = (u16)(READ_ONCE(tx->sn) - sn); + tx->now.offset = 0; + write_seqcount_end(&tx->seq); + preempt_enable(); +} + static void hci_num_comp_pkts_evt(struct hci_dev *hdev, void *data, struct sk_buff *skb) { struct hci_ev_num_comp_pkts *ev = data; + ktime_t now = ktime_get(); int i; if (!hci_ev_skb_pull(hdev, skb, HCI_EV_NUM_COMP_PKTS, @@ -4450,6 +4512,8 @@ static void hci_num_comp_pkts_evt(struct hci_dev *hdev, void *data, switch (conn->type) { case ACL_LINK: + update_acl_tx_latency(conn, count, now); + hdev->acl_cnt += count; if (hdev->acl_cnt > hdev->acl_pkts) hdev->acl_cnt = hdev->acl_pkts; @@ -4474,6 +4538,8 @@ static void hci_num_comp_pkts_evt(struct hci_dev *hdev, void *data, break; case ISO_LINK: + update_iso_tx_latency(conn, count, now); + if (hdev->iso_pkts) { hdev->iso_cnt += count; if (hdev->iso_cnt > hdev->iso_pkts) From patchwork Wed Feb 28 20:03:40 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pauli Virtanen X-Patchwork-Id: 13575893 Received: from meesny.iki.fi (meesny.iki.fi [195.140.195.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C67BD74412 for ; Wed, 28 Feb 2024 20:04:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=195.140.195.201 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1709150643; cv=pass; b=mvp8vDiUUXnXMcP8oZ5OJe3eZczFccAD2iaeeFGoB4XiiPcl8u8vmQSEcgmYBvNIHP6wulRiMKdxVFMIcI/t+Eh2wN1e5D2i87SlaqdI829RyKdA73ISJskV9rdgwNlFu5aUc8qIa8pEYk9hPuoMTn4RMZ/ShKGm33F1J1tB7O8= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1709150643; c=relaxed/simple; bh=dJ2QqBvUOy/j0pSEoZu6dOKIEiu58XqmvjqDqFAcriM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=A/IOZuf5zVWFRMHYj4HqR1zO7p8+Kt0Z+z0GB2WuQ3QHj3ajta/o7dtt42Pzn9ggYByiTDIY4gLbQnizkZmf/co81ayqD3BUysATRFVvPcikLZUcZVvOaVcIctoMmrwUYpPEHzs7atTE6YrLiCTWlYKlBCSnvrKkDoiuhh3kzUM= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=iki.fi; spf=pass smtp.mailfrom=iki.fi; dkim=pass (1024-bit key) header.d=iki.fi header.i=@iki.fi header.b=X9GKiMyz; arc=pass smtp.client-ip=195.140.195.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=iki.fi Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=iki.fi Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=iki.fi header.i=@iki.fi header.b="X9GKiMyz" Received: from monolith.lan (unknown [193.138.7.158]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: pav) by meesny.iki.fi (Postfix) with ESMTPSA id 4TlQKW00snzyxR; Wed, 28 Feb 2024 22:03:58 +0200 (EET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=iki.fi; s=meesny; t=1709150639; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=w4G0jAuJ8gvFLcHu+ubt+Ek1wrUKLqbuD+fTQ4Z84og=; b=X9GKiMyzUHyPyYUNMn+58c2XFP6E7K2GnAAbAblWTxNFecK6qgH+BvAoNOyOrK1DKIViFI 61y0e3ygmoocastlK8oXvwLtOSSWHbIoYxB1l0EYMob+lR2uqh29E7A6qvWgvopR5KvwYe qxI/AxURhl+x0QrDK92+tfB8Z/3U//E= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=iki.fi; s=meesny; t=1709150639; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=w4G0jAuJ8gvFLcHu+ubt+Ek1wrUKLqbuD+fTQ4Z84og=; b=sCFkplaVs+ZTf7neOq7bxSC52PfSYDeISMMJQK2GgOhSplauSMexdakkcJIJpnaM1QJa2c vbcvOVaCQ+SFG40/TYdbbHeZuxoBc3X12JoEWS9M3OtW6nM+MyaEsNxfs8c2rPmp2XWb0T IxMpZnX600ix3HyNO7nr2xPnPvwo7cM= ARC-Authentication-Results: i=1; ORIGINATING; auth=pass smtp.auth=pav smtp.mailfrom=pav@iki.fi ARC-Seal: i=1; s=meesny; d=iki.fi; t=1709150639; a=rsa-sha256; cv=none; b=ctg3Q/DpYu4/KsUFmLZ17kLumbfHr3Fko/5Dz1V73KK5C99AKoMsqlBl+REytFgnVjLgEl STxx972wzTRAVQHYWiLqJJCXePVMPMda5HEzJubqfmbc2pCABIO5VZxKi/tK3iPgbcp0i1 CsOXQLhVoTstQNhBqt0Nk+w3zmjx55c= From: Pauli Virtanen To: linux-bluetooth@vger.kernel.org Cc: Pauli Virtanen Subject: [RFC PATCH 2/3] Bluetooth: ISO: add new ioctl() for reading tx latency Date: Wed, 28 Feb 2024 22:03:40 +0200 Message-ID: <8e5df7837d596cbedcfa04d766ce1caba50d853b.1709150574.git.pav@iki.fi> X-Mailer: git-send-email 2.44.0 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-bluetooth@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Add ioctl BTGETTXINFO to read information of a previous packet completion event concerning a given ISO socket. It contains sufficient information so the user can know which previous sendmsg() the packet corresponds to and what was its completion time in monotonic clock, so latency can be calculated. The user can then also know the packet queue length (in units of their sendmsg packets). Mark submitted skb so that hci_core updates the latency information, and read it in the ioctl. Signed-off-by: Pauli Virtanen --- include/net/bluetooth/bluetooth.h | 37 ++++++++++++++++++++ net/bluetooth/iso.c | 58 ++++++++++++++++++++++++++++--- 2 files changed, 90 insertions(+), 5 deletions(-) diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index f6bdd040adaa..ff4230d15461 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -239,6 +239,43 @@ struct bt_codecs { #define BT_ISO_BASE 20 +/* BTGETTXINFO: Transmission latency information. + * + * Produces information of a previous event when a packet was sent, and the + * length of packet queue at that time. + * + * Applicable to: Bluetooth ISO sockets. + * + * Input: Zero-initialize reserved flag bits and other fields. + * + * Output: + * + * Fails with ENOENT if no packet has been sent. + * + * - flags: currently always 0, all bits reserved for extensions. + * + * - queue: total number of packets in queue (controller and socket buffers) + * at the time of the event. + * + * - time: reference event timestamp (nsec, in system monotonic clock). + * + * - offset: offset (nsec) of actual packet timing from the reference timestamp. + * Currently always 0. + * + * For packet latencies in nanoseconds, the application can track timestamps + * t[j] when it sent packet j. Then, given t[k] < tx_info.time < t[k + 1], + * event packet j = k - tx.queue and ref_latency_nsec = tx_info.time - t[j]. + */ + +#define BTGETTXINFO _IOWR('B', 100, struct bt_tx_info) + +struct bt_tx_info { + __u32 flags; + __u32 queue; + __s64 time; + __s64 offset; +} __packed; + __printf(1, 2) void bt_info(const char *fmt, ...); __printf(1, 2) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index 30c777c469f9..4953c987e4fd 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -500,6 +500,8 @@ static int iso_send_frame(struct sock *sk, struct sk_buff *skb) if (skb->len > qos->ucast.out.sdu) return -EMSGSIZE; + if (sk->sk_state != BT_CONNECTED) + return -ENOTCONN; len = skb->len; @@ -509,10 +511,9 @@ static int iso_send_frame(struct sock *sk, struct sk_buff *skb) hdr->slen = cpu_to_le16(hci_iso_data_len_pack(len, HCI_ISO_STATUS_VALID)); - if (sk->sk_state == BT_CONNECTED) - hci_send_iso(conn->hcon, skb); - else - len = -ENOTCONN; + hci_mark_tx_latency(&conn->hcon->tx_latency, skb); + + hci_send_iso(conn->hcon, skb); return len; } @@ -2232,6 +2233,53 @@ void iso_recv(struct hci_conn *hcon, struct sk_buff *skb, u16 flags) kfree_skb(skb); } +static int iso_sock_ioctl(struct socket *sock, unsigned int cmd, + unsigned long arg) +{ + struct sock *sk = sock->sk; + struct tx_latency latency; + struct bt_tx_info info; + + BT_DBG("sk %p cmd %x arg %lx", sk, cmd, arg); + + switch (cmd) { + case BTGETTXINFO: + /* Require zero-initialized, to allow later extensions */ + if (copy_from_user(&info, (void __user *)arg, sizeof(info))) + return -EFAULT; + if (info.flags || info.queue || info.time || info.offset) + return -EINVAL; + + memset(&info, 0, sizeof(info)); + + lock_sock(sk); + + if (sk->sk_state != BT_CONNECTED) { + release_sock(sk); + return -EINVAL; + } + + hci_copy_tx_latency(&latency, + &iso_pi(sk)->conn->hcon->tx_latency); + + release_sock(sk); + + if (!latency.now.time) + return -ENOENT; + + info.queue = latency.now.queue; + info.time = ktime_to_ns(latency.now.time); + info.offset = latency.now.offset; + + if (copy_to_user((void __user *)arg, &info, sizeof(info))) + return -EFAULT; + + return 0; + } + + return bt_sock_ioctl(sock, cmd, arg); +} + static struct hci_cb iso_cb = { .name = "ISO", .connect_cfm = iso_connect_cfm, @@ -2270,7 +2318,7 @@ static const struct proto_ops iso_sock_ops = { .sendmsg = iso_sock_sendmsg, .recvmsg = iso_sock_recvmsg, .poll = bt_sock_poll, - .ioctl = bt_sock_ioctl, + .ioctl = iso_sock_ioctl, .mmap = sock_no_mmap, .socketpair = sock_no_socketpair, .shutdown = iso_sock_shutdown, From patchwork Wed Feb 28 20:03:41 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pauli Virtanen X-Patchwork-Id: 13575894 Received: from meesny.iki.fi (meesny.iki.fi [195.140.195.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5775C74426 for ; Wed, 28 Feb 2024 20:04:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=195.140.195.201 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1709150644; cv=pass; b=sChufF6nHoyTiA7aje0VvZNrSNx0GtIaEwVvV37t+sTPgKCN9QLg/EuhzAbu8ab6mpGeclg8iXbnm29hNB9TxHLBu6LXCkDRxPATTYh0ybOgawFmPim/VeON2daZ0eF3sIW5JQwJWTqr2Y5Iu0ewwN54toXOhhfPWuKsskVx6ac= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1709150644; c=relaxed/simple; bh=Xpgw8RzfiOV7c607k6YknS+aKHFD32znYo7Mdenl2OA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=S+OL+2Q0GWFlSq/g5tMjvvVPVYbyw0MjjFh5sfT5Mf82AYaA4VzlOh7wC+lRlgCHkRHZG++mzpUBOoXLJrln49SodAkn++9sXdqDFXckIwYKQmcARj1BEwWHFfF8BQpIG+8gGl1YsyN6AcufbDoz7c6+RZby8WiIYngB0Gle0PA= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=iki.fi; spf=pass smtp.mailfrom=iki.fi; dkim=pass (1024-bit key) header.d=iki.fi header.i=@iki.fi header.b=V1CeytCc; arc=pass smtp.client-ip=195.140.195.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=iki.fi Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=iki.fi Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=iki.fi header.i=@iki.fi header.b="V1CeytCc" Received: from monolith.lan (unknown [193.138.7.158]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: pav) by meesny.iki.fi (Postfix) with ESMTPSA id 4TlQKW6ZqNzyyw; Wed, 28 Feb 2024 22:03:59 +0200 (EET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=iki.fi; s=meesny; t=1709150640; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=YCy+DFVmyNrdCZXqYvszAitDkeet40gm8286NI506HU=; b=V1CeytCcdArOz8/qfqTDYVExJzsugoySyWpk8tFSN0PvDAyeFltjN/su/DE5KtqDHcnf5b qhckOiPAhQ4g4YXVK85B29gbo8SOr29lMiiL9b1rUZZmQlF/1XoN78Hi/R2rD55hcJVQgT m3Xt2VHkKOE6sS1GKq/6+0ZhBZ53/NY= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=iki.fi; s=meesny; t=1709150640; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=YCy+DFVmyNrdCZXqYvszAitDkeet40gm8286NI506HU=; b=SY16Zr0QuLVfXx4bAEdU9yORBZBsh7zH/1u+T6M3vY8FFNkstaKQMu/F4pI7jOXfHUisfL 5JW1jFIGzG0eXKo7/u6ZLBIjTzUbGVUoQUqqpWa6lwv5Mnc0Q44/wqhS/pdWRiUP6n5xf/ 7Nhh8sOEmSTW/dXcAig8PL8cpnDVsp0= ARC-Authentication-Results: i=1; ORIGINATING; auth=pass smtp.auth=pav smtp.mailfrom=pav@iki.fi ARC-Seal: i=1; s=meesny; d=iki.fi; t=1709150640; a=rsa-sha256; cv=none; b=i1NhawL3fS5Nb0yac3oov2/G3E4DsHCGpXRrdiqBIlJgA00pE0jOFgHM/eiKMyPaRPWBH6 I5QcFhAV2Ijv1CzSzZt+ooeVQcAvDOUxVDvOuQudWA/jNortGhJyM/O19z9lUGgbzPhjgl 7kxvMAEDe5TOUzRkZYnmK5kOwTyQdb0= From: Pauli Virtanen To: linux-bluetooth@vger.kernel.org Cc: Pauli Virtanen Subject: [RFC PATCH 3/3] Bluetooth: L2CAP: add new ioctl() for reading tx latency Date: Wed, 28 Feb 2024 22:03:41 +0200 Message-ID: X-Mailer: git-send-email 2.44.0 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-bluetooth@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Add ioctl BTGETTXINFO to read information of a previous packet completion event concerning a given L2CAP socket. Mark skbs submitted so that hci_core updates latency info in the hci_chan. Read the information in the ioctl. The ioctl is the same as for ISO sockets. Signed-off-by: Pauli Virtanen --- include/net/bluetooth/bluetooth.h | 2 +- net/bluetooth/l2cap_core.c | 12 ++++++++ net/bluetooth/l2cap_sock.c | 50 ++++++++++++++++++++++++++++++- 3 files changed, 62 insertions(+), 2 deletions(-) diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index ff4230d15461..480f5cbc24e4 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -244,7 +244,7 @@ struct bt_codecs { * Produces information of a previous event when a packet was sent, and the * length of packet queue at that time. * - * Applicable to: Bluetooth ISO sockets. + * Applicable to: Bluetooth ISO and L2CAP sockets. * * Input: Zero-initialize reserved flag bits and other fields. * diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 467b242d8be0..c70f935d9242 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -2488,6 +2488,13 @@ static void l2cap_le_flowctl_send(struct l2cap_chan *chan) skb_queue_len(&chan->tx_q)); } +static void mark_tx_latency(struct l2cap_chan *chan, struct sk_buff *skb) +{ + struct hci_chan *hchan = chan->conn->hchan; + + hci_mark_tx_latency(&hchan->tx_latency, skb); +} + int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len) { struct sk_buff *skb; @@ -2526,6 +2533,8 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len) if (err) return err; + mark_tx_latency(chan, skb_peek(&seg_queue)); + skb_queue_splice_tail_init(&seg_queue, &chan->tx_q); l2cap_le_flowctl_send(chan); @@ -2547,6 +2556,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len) if (IS_ERR(skb)) return PTR_ERR(skb); + mark_tx_latency(chan, skb); l2cap_do_send(chan, skb); err = len; break; @@ -2570,6 +2580,8 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len) if (err) break; + mark_tx_latency(chan, skb_peek(&seg_queue)); + if (chan->mode == L2CAP_MODE_ERTM) l2cap_tx(chan, NULL, &seg_queue, L2CAP_EV_DATA_REQUEST); else diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 4287aa6cc988..08c130bd8b98 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -1207,6 +1207,54 @@ static int l2cap_sock_recvmsg(struct socket *sock, struct msghdr *msg, return err; } +static int l2cap_sock_ioctl(struct socket *sock, unsigned int cmd, + unsigned long arg) +{ + struct sock *sk = sock->sk; + struct l2cap_chan *chan = l2cap_pi(sk)->chan; + struct tx_latency latency; + struct bt_tx_info info; + + BT_DBG("sk %p cmd %x arg %lx", sk, cmd, arg); + + switch (cmd) { + case BTGETTXINFO: + /* Require zero-initialized, to allow later extensions */ + if (copy_from_user(&info, (void __user *)arg, sizeof(info))) + return -EFAULT; + if (info.flags || info.queue || info.time || info.offset) + return -EINVAL; + + memset(&info, 0, sizeof(info)); + + lock_sock(sk); + + if (sk->sk_state != BT_CONNECTED || !chan->conn || + !chan->conn->hchan) { + release_sock(sk); + return -EINVAL; + } + + hci_copy_tx_latency(&latency, &chan->conn->hchan->tx_latency); + + release_sock(sk); + + if (!latency.now.time) + return -ENOENT; + + info.queue = latency.now.queue; + info.time = ktime_to_ns(latency.now.time); + info.offset = latency.now.offset; + + if (copy_to_user((void __user *)arg, &info, sizeof(info))) + return -EFAULT; + + return 0; + } + + return bt_sock_ioctl(sock, cmd, arg); +} + /* Kill socket (only if zapped and orphan) * Must be called on unlocked socket, with l2cap channel lock. */ @@ -1883,7 +1931,7 @@ static const struct proto_ops l2cap_sock_ops = { .sendmsg = l2cap_sock_sendmsg, .recvmsg = l2cap_sock_recvmsg, .poll = bt_sock_poll, - .ioctl = bt_sock_ioctl, + .ioctl = l2cap_sock_ioctl, .gettstamp = sock_gettstamp, .mmap = sock_no_mmap, .socketpair = sock_no_socketpair,