@@ -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.
*
@@ -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
@@ -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,
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 <pav@iki.fi> --- 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(-)