Skip to content

Commit 134f4b3

Browse files
pvVudentz
authored andcommitted
Bluetooth: add support for skb TX SND/COMPLETION timestamping
Support enabling TX timestamping for some skbs, and track them until packet completion. Generate software SCM_TSTAMP_COMPLETION when getting completion report from hardware. Generate software SCM_TSTAMP_SND before sending to driver. Sending from driver requires changes in the driver API, and drivers mostly are going to send the skb immediately. Make the default situation with no COMPLETION TX timestamping more efficient by only counting packets in the queue when there is nothing to track. When there is something to track, we need to make clones, since the driver may modify sent skbs. The tx_q queue length is bounded by the hdev flow control, which will not send new packets before it has got completion reports for old ones. Signed-off-by: Pauli Virtanen <pav@iki.fi> Reviewed-by: Willem de Bruijn <willemb@google.com> Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
1 parent 983e0e4 commit 134f4b3

File tree

4 files changed

+157
-4
lines changed

4 files changed

+157
-4
lines changed

include/net/bluetooth/hci_core.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,12 @@ struct adv_info {
261261
struct delayed_work rpa_expired_cb;
262262
};
263263

264+
struct tx_queue {
265+
struct sk_buff_head queue;
266+
unsigned int extra;
267+
unsigned int tracked;
268+
};
269+
264270
#define HCI_MAX_ADV_INSTANCES 5
265271
#define HCI_DEFAULT_ADV_DURATION 2
266272

@@ -733,6 +739,8 @@ struct hci_conn {
733739
struct sk_buff_head data_q;
734740
struct list_head chan_list;
735741

742+
struct tx_queue tx_q;
743+
736744
struct delayed_work disc_work;
737745
struct delayed_work auto_accept_work;
738746
struct delayed_work idle_work;
@@ -1572,6 +1580,18 @@ void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active);
15721580
void hci_conn_failed(struct hci_conn *conn, u8 status);
15731581
u8 hci_conn_set_handle(struct hci_conn *conn, u16 handle);
15741582

1583+
void hci_conn_tx_queue(struct hci_conn *conn, struct sk_buff *skb);
1584+
void hci_conn_tx_dequeue(struct hci_conn *conn);
1585+
void hci_setup_tx_timestamp(struct sk_buff *skb, size_t key_offset,
1586+
const struct sockcm_cookie *sockc);
1587+
1588+
static inline void hci_sockcm_init(struct sockcm_cookie *sockc, struct sock *sk)
1589+
{
1590+
*sockc = (struct sockcm_cookie) {
1591+
.tsflags = READ_ONCE(sk->sk_tsflags),
1592+
};
1593+
}
1594+
15751595
/*
15761596
* hci_conn_get() and hci_conn_put() are used to control the life-time of an
15771597
* "hci_conn" object. They do not guarantee that the hci_conn object is running,

net/bluetooth/hci_conn.c

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
#include <linux/export.h>
2929
#include <linux/debugfs.h>
30+
#include <linux/errqueue.h>
3031

3132
#include <net/bluetooth/bluetooth.h>
3233
#include <net/bluetooth/hci_core.h>
@@ -1002,6 +1003,7 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t
10021003
}
10031004

10041005
skb_queue_head_init(&conn->data_q);
1006+
skb_queue_head_init(&conn->tx_q.queue);
10051007

10061008
INIT_LIST_HEAD(&conn->chan_list);
10071009
INIT_LIST_HEAD(&conn->link_list);
@@ -1155,6 +1157,7 @@ void hci_conn_del(struct hci_conn *conn)
11551157
}
11561158

11571159
skb_queue_purge(&conn->data_q);
1160+
skb_queue_purge(&conn->tx_q.queue);
11581161

11591162
/* Remove the connection from the list and cleanup its remaining
11601163
* state. This is a separate function since for some cases like
@@ -3064,3 +3067,122 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)
30643067
*/
30653068
return hci_cmd_sync_run_once(hdev, abort_conn_sync, conn, NULL);
30663069
}
3070+
3071+
void hci_setup_tx_timestamp(struct sk_buff *skb, size_t key_offset,
3072+
const struct sockcm_cookie *sockc)
3073+
{
3074+
struct sock *sk = skb ? skb->sk : NULL;
3075+
3076+
/* This shall be called on a single skb of those generated by user
3077+
* sendmsg(), and only when the sendmsg() does not return error to
3078+
* user. This is required for keeping the tskey that increments here in
3079+
* sync with possible sendmsg() counting by user.
3080+
*
3081+
* Stream sockets shall set key_offset to sendmsg() length in bytes
3082+
* and call with the last fragment, others to 1 and first fragment.
3083+
*/
3084+
3085+
if (!skb || !sockc || !sk || !key_offset)
3086+
return;
3087+
3088+
sock_tx_timestamp(sk, sockc, &skb_shinfo(skb)->tx_flags);
3089+
3090+
if (sockc->tsflags & SOF_TIMESTAMPING_OPT_ID &&
3091+
sockc->tsflags & SOF_TIMESTAMPING_TX_RECORD_MASK) {
3092+
if (sockc->tsflags & SOCKCM_FLAG_TS_OPT_ID) {
3093+
skb_shinfo(skb)->tskey = sockc->ts_opt_id;
3094+
} else {
3095+
int key = atomic_add_return(key_offset, &sk->sk_tskey);
3096+
3097+
skb_shinfo(skb)->tskey = key - 1;
3098+
}
3099+
}
3100+
}
3101+
3102+
void hci_conn_tx_queue(struct hci_conn *conn, struct sk_buff *skb)
3103+
{
3104+
struct tx_queue *comp = &conn->tx_q;
3105+
bool track = false;
3106+
3107+
/* Emit SND now, ie. just before sending to driver */
3108+
if (skb_shinfo(skb)->tx_flags & SKBTX_SW_TSTAMP)
3109+
__skb_tstamp_tx(skb, NULL, NULL, skb->sk, SCM_TSTAMP_SND);
3110+
3111+
/* COMPLETION tstamp is emitted for tracked skb later in Number of
3112+
* Completed Packets event. Available only for flow controlled cases.
3113+
*
3114+
* TODO: SCO support without flowctl (needs to be done in drivers)
3115+
*/
3116+
switch (conn->type) {
3117+
case ISO_LINK:
3118+
case ACL_LINK:
3119+
case LE_LINK:
3120+
break;
3121+
case SCO_LINK:
3122+
case ESCO_LINK:
3123+
if (!hci_dev_test_flag(conn->hdev, HCI_SCO_FLOWCTL))
3124+
return;
3125+
break;
3126+
default:
3127+
return;
3128+
}
3129+
3130+
if (skb->sk && (skb_shinfo(skb)->tx_flags & SKBTX_COMPLETION_TSTAMP))
3131+
track = true;
3132+
3133+
/* If nothing is tracked, just count extra skbs at the queue head */
3134+
if (!track && !comp->tracked) {
3135+
comp->extra++;
3136+
return;
3137+
}
3138+
3139+
if (track) {
3140+
skb = skb_clone_sk(skb);
3141+
if (!skb)
3142+
goto count_only;
3143+
3144+
comp->tracked++;
3145+
} else {
3146+
skb = skb_clone(skb, GFP_KERNEL);
3147+
if (!skb)
3148+
goto count_only;
3149+
}
3150+
3151+
skb_queue_tail(&comp->queue, skb);
3152+
return;
3153+
3154+
count_only:
3155+
/* Stop tracking skbs, and only count. This will not emit timestamps for
3156+
* the packets, but if we get here something is more seriously wrong.
3157+
*/
3158+
comp->tracked = 0;
3159+
comp->extra += skb_queue_len(&comp->queue) + 1;
3160+
skb_queue_purge(&comp->queue);
3161+
}
3162+
3163+
void hci_conn_tx_dequeue(struct hci_conn *conn)
3164+
{
3165+
struct tx_queue *comp = &conn->tx_q;
3166+
struct sk_buff *skb;
3167+
3168+
/* If there are tracked skbs, the counted extra go before dequeuing real
3169+
* skbs, to keep ordering. When nothing is tracked, the ordering doesn't
3170+
* matter so dequeue real skbs first to get rid of them ASAP.
3171+
*/
3172+
if (comp->extra && (comp->tracked || skb_queue_empty(&comp->queue))) {
3173+
comp->extra--;
3174+
return;
3175+
}
3176+
3177+
skb = skb_dequeue(&comp->queue);
3178+
if (!skb)
3179+
return;
3180+
3181+
if (skb->sk) {
3182+
comp->tracked--;
3183+
__skb_tstamp_tx(skb, NULL, NULL, skb->sk,
3184+
SCM_TSTAMP_COMPLETION);
3185+
}
3186+
3187+
kfree_skb(skb);
3188+
}

net/bluetooth/hci_core.c

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3029,6 +3029,13 @@ static int hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
30293029
return 0;
30303030
}
30313031

3032+
static int hci_send_conn_frame(struct hci_dev *hdev, struct hci_conn *conn,
3033+
struct sk_buff *skb)
3034+
{
3035+
hci_conn_tx_queue(conn, skb);
3036+
return hci_send_frame(hdev, skb);
3037+
}
3038+
30323039
/* Send HCI command */
30333040
int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen,
30343041
const void *param)
@@ -3575,7 +3582,7 @@ static void hci_sched_sco(struct hci_dev *hdev, __u8 type)
35753582
while (*cnt && (conn = hci_low_sent(hdev, type, &quote))) {
35763583
while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
35773584
BT_DBG("skb %p len %d", skb, skb->len);
3578-
hci_send_frame(hdev, skb);
3585+
hci_send_conn_frame(hdev, conn, skb);
35793586

35803587
conn->sent++;
35813588
if (conn->sent == ~0)
@@ -3618,7 +3625,7 @@ static void hci_sched_acl_pkt(struct hci_dev *hdev)
36183625
hci_conn_enter_active_mode(chan->conn,
36193626
bt_cb(skb)->force_active);
36203627

3621-
hci_send_frame(hdev, skb);
3628+
hci_send_conn_frame(hdev, chan->conn, skb);
36223629
hdev->acl_last_tx = jiffies;
36233630

36243631
hdev->acl_cnt--;
@@ -3674,7 +3681,7 @@ static void hci_sched_le(struct hci_dev *hdev)
36743681

36753682
skb = skb_dequeue(&chan->data_q);
36763683

3677-
hci_send_frame(hdev, skb);
3684+
hci_send_conn_frame(hdev, chan->conn, skb);
36783685
hdev->le_last_tx = jiffies;
36793686

36803687
(*cnt)--;
@@ -3708,7 +3715,7 @@ static void hci_sched_iso(struct hci_dev *hdev)
37083715
while (*cnt && (conn = hci_low_sent(hdev, ISO_LINK, &quote))) {
37093716
while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
37103717
BT_DBG("skb %p len %d", skb, skb->len);
3711-
hci_send_frame(hdev, skb);
3718+
hci_send_conn_frame(hdev, conn, skb);
37123719

37133720
conn->sent++;
37143721
if (conn->sent == ~0)

net/bluetooth/hci_event.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4415,6 +4415,7 @@ static void hci_num_comp_pkts_evt(struct hci_dev *hdev, void *data,
44154415
struct hci_comp_pkts_info *info = &ev->handles[i];
44164416
struct hci_conn *conn;
44174417
__u16 handle, count;
4418+
unsigned int i;
44184419

44194420
handle = __le16_to_cpu(info->handle);
44204421
count = __le16_to_cpu(info->count);
@@ -4425,6 +4426,9 @@ static void hci_num_comp_pkts_evt(struct hci_dev *hdev, void *data,
44254426

44264427
conn->sent -= count;
44274428

4429+
for (i = 0; i < count; ++i)
4430+
hci_conn_tx_dequeue(conn);
4431+
44284432
switch (conn->type) {
44294433
case ACL_LINK:
44304434
hdev->acl_cnt += count;

0 commit comments

Comments
 (0)