xref: /linux/net/core/timestamping.c (revision 164d8c6665213c931645578310256da7b1259331)
1c1f19b51SRichard Cochran /*
2c1f19b51SRichard Cochran  * PTP 1588 clock support - support for timestamping in PHY devices
3c1f19b51SRichard Cochran  *
4c1f19b51SRichard Cochran  * Copyright (C) 2010 OMICRON electronics GmbH
5c1f19b51SRichard Cochran  *
6c1f19b51SRichard Cochran  *  This program is free software; you can redistribute it and/or modify
7c1f19b51SRichard Cochran  *  it under the terms of the GNU General Public License as published by
8c1f19b51SRichard Cochran  *  the Free Software Foundation; either version 2 of the License, or
9c1f19b51SRichard Cochran  *  (at your option) any later version.
10c1f19b51SRichard Cochran  *
11c1f19b51SRichard Cochran  *  This program is distributed in the hope that it will be useful,
12c1f19b51SRichard Cochran  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13c1f19b51SRichard Cochran  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14c1f19b51SRichard Cochran  *  GNU General Public License for more details.
15c1f19b51SRichard Cochran  *
16c1f19b51SRichard Cochran  *  You should have received a copy of the GNU General Public License
17c1f19b51SRichard Cochran  *  along with this program; if not, write to the Free Software
18c1f19b51SRichard Cochran  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19c1f19b51SRichard Cochran  */
20c1f19b51SRichard Cochran #include <linux/errqueue.h>
21c1f19b51SRichard Cochran #include <linux/phy.h>
22c1f19b51SRichard Cochran #include <linux/ptp_classify.h>
23c1f19b51SRichard Cochran #include <linux/skbuff.h>
24bc3b2d7fSPaul Gortmaker #include <linux/export.h>
25c1f19b51SRichard Cochran 
26e62d2df0SDaniel Borkmann static struct sk_filter *ptp_insns __read_mostly;
27c1f19b51SRichard Cochran 
28*164d8c66SDaniel Borkmann unsigned int ptp_classify_raw(const struct sk_buff *skb)
29*164d8c66SDaniel Borkmann {
30*164d8c66SDaniel Borkmann 	return SK_RUN_FILTER(ptp_insns, skb);
31*164d8c66SDaniel Borkmann }
32*164d8c66SDaniel Borkmann EXPORT_SYMBOL_GPL(ptp_classify_raw);
33*164d8c66SDaniel Borkmann 
3462ab0812SEric Dumazet static unsigned int classify(const struct sk_buff *skb)
35c1f19b51SRichard Cochran {
36e62d2df0SDaniel Borkmann 	if (likely(skb->dev && skb->dev->phydev &&
37c1f19b51SRichard Cochran 		   skb->dev->phydev->drv))
38*164d8c66SDaniel Borkmann 		return ptp_classify_raw(skb);
39c1f19b51SRichard Cochran 	else
40c1f19b51SRichard Cochran 		return PTP_CLASS_NONE;
41c1f19b51SRichard Cochran }
42c1f19b51SRichard Cochran 
43c1f19b51SRichard Cochran void skb_clone_tx_timestamp(struct sk_buff *skb)
44c1f19b51SRichard Cochran {
45c1f19b51SRichard Cochran 	struct phy_device *phydev;
46c1f19b51SRichard Cochran 	struct sk_buff *clone;
47c1f19b51SRichard Cochran 	struct sock *sk = skb->sk;
48c1f19b51SRichard Cochran 	unsigned int type;
49c1f19b51SRichard Cochran 
50c1f19b51SRichard Cochran 	if (!sk)
51c1f19b51SRichard Cochran 		return;
52c1f19b51SRichard Cochran 
53c1f19b51SRichard Cochran 	type = classify(skb);
54c1f19b51SRichard Cochran 
55c1f19b51SRichard Cochran 	switch (type) {
56c1f19b51SRichard Cochran 	case PTP_CLASS_V1_IPV4:
57c1f19b51SRichard Cochran 	case PTP_CLASS_V1_IPV6:
58c1f19b51SRichard Cochran 	case PTP_CLASS_V2_IPV4:
59c1f19b51SRichard Cochran 	case PTP_CLASS_V2_IPV6:
60c1f19b51SRichard Cochran 	case PTP_CLASS_V2_L2:
61c1f19b51SRichard Cochran 	case PTP_CLASS_V2_VLAN:
62c1f19b51SRichard Cochran 		phydev = skb->dev->phydev;
63c1f19b51SRichard Cochran 		if (likely(phydev->drv->txtstamp)) {
64da92b194SRichard Cochran 			if (!atomic_inc_not_zero(&sk->sk_refcnt))
65c1f19b51SRichard Cochran 				return;
66e62d2df0SDaniel Borkmann 
67da92b194SRichard Cochran 			clone = skb_clone(skb, GFP_ATOMIC);
68da92b194SRichard Cochran 			if (!clone) {
69da92b194SRichard Cochran 				sock_put(sk);
70da92b194SRichard Cochran 				return;
71da92b194SRichard Cochran 			}
72e62d2df0SDaniel Borkmann 
73c1f19b51SRichard Cochran 			clone->sk = sk;
74c1f19b51SRichard Cochran 			phydev->drv->txtstamp(phydev, clone, type);
75c1f19b51SRichard Cochran 		}
76c1f19b51SRichard Cochran 		break;
77c1f19b51SRichard Cochran 	default:
78c1f19b51SRichard Cochran 		break;
79c1f19b51SRichard Cochran 	}
80c1f19b51SRichard Cochran }
811c17216eSRichard Cochran EXPORT_SYMBOL_GPL(skb_clone_tx_timestamp);
82c1f19b51SRichard Cochran 
83c1f19b51SRichard Cochran void skb_complete_tx_timestamp(struct sk_buff *skb,
84c1f19b51SRichard Cochran 			       struct skb_shared_hwtstamps *hwtstamps)
85c1f19b51SRichard Cochran {
86c1f19b51SRichard Cochran 	struct sock *sk = skb->sk;
87c1f19b51SRichard Cochran 	struct sock_exterr_skb *serr;
88c1f19b51SRichard Cochran 	int err;
89c1f19b51SRichard Cochran 
90da92b194SRichard Cochran 	if (!hwtstamps) {
91da92b194SRichard Cochran 		sock_put(sk);
92da92b194SRichard Cochran 		kfree_skb(skb);
93c1f19b51SRichard Cochran 		return;
94da92b194SRichard Cochran 	}
95c1f19b51SRichard Cochran 
96c1f19b51SRichard Cochran 	*skb_hwtstamps(skb) = *hwtstamps;
97e62d2df0SDaniel Borkmann 
98c1f19b51SRichard Cochran 	serr = SKB_EXT_ERR(skb);
99c1f19b51SRichard Cochran 	memset(serr, 0, sizeof(*serr));
100c1f19b51SRichard Cochran 	serr->ee.ee_errno = ENOMSG;
101c1f19b51SRichard Cochran 	serr->ee.ee_origin = SO_EE_ORIGIN_TIMESTAMPING;
102c1f19b51SRichard Cochran 	skb->sk = NULL;
103e62d2df0SDaniel Borkmann 
104c1f19b51SRichard Cochran 	err = sock_queue_err_skb(sk, skb);
105e62d2df0SDaniel Borkmann 
106da92b194SRichard Cochran 	sock_put(sk);
107c1f19b51SRichard Cochran 	if (err)
108c1f19b51SRichard Cochran 		kfree_skb(skb);
109c1f19b51SRichard Cochran }
110c1f19b51SRichard Cochran EXPORT_SYMBOL_GPL(skb_complete_tx_timestamp);
111c1f19b51SRichard Cochran 
112c1f19b51SRichard Cochran bool skb_defer_rx_timestamp(struct sk_buff *skb)
113c1f19b51SRichard Cochran {
114c1f19b51SRichard Cochran 	struct phy_device *phydev;
115c1f19b51SRichard Cochran 	unsigned int type;
116c1f19b51SRichard Cochran 
117a19faf02SEric Dumazet 	if (skb_headroom(skb) < ETH_HLEN)
118a19faf02SEric Dumazet 		return false;
119a19faf02SEric Dumazet 	__skb_push(skb, ETH_HLEN);
120c1f19b51SRichard Cochran 
121c1f19b51SRichard Cochran 	type = classify(skb);
122c1f19b51SRichard Cochran 
123a19faf02SEric Dumazet 	__skb_pull(skb, ETH_HLEN);
124c1f19b51SRichard Cochran 
125c1f19b51SRichard Cochran 	switch (type) {
126c1f19b51SRichard Cochran 	case PTP_CLASS_V1_IPV4:
127c1f19b51SRichard Cochran 	case PTP_CLASS_V1_IPV6:
128c1f19b51SRichard Cochran 	case PTP_CLASS_V2_IPV4:
129c1f19b51SRichard Cochran 	case PTP_CLASS_V2_IPV6:
130c1f19b51SRichard Cochran 	case PTP_CLASS_V2_L2:
131c1f19b51SRichard Cochran 	case PTP_CLASS_V2_VLAN:
132c1f19b51SRichard Cochran 		phydev = skb->dev->phydev;
133c1f19b51SRichard Cochran 		if (likely(phydev->drv->rxtstamp))
134c1f19b51SRichard Cochran 			return phydev->drv->rxtstamp(phydev, skb, type);
135c1f19b51SRichard Cochran 		break;
136c1f19b51SRichard Cochran 	default:
137c1f19b51SRichard Cochran 		break;
138c1f19b51SRichard Cochran 	}
139c1f19b51SRichard Cochran 
140c1f19b51SRichard Cochran 	return false;
141c1f19b51SRichard Cochran }
142238442f6SRichard Cochran EXPORT_SYMBOL_GPL(skb_defer_rx_timestamp);
143c1f19b51SRichard Cochran 
144c1f19b51SRichard Cochran void __init skb_timestamping_init(void)
145c1f19b51SRichard Cochran {
146e62d2df0SDaniel Borkmann 	static struct sock_filter ptp_filter[] = { PTP_FILTER };
147e62d2df0SDaniel Borkmann 	struct sock_fprog ptp_prog = {
148e62d2df0SDaniel Borkmann 		.len = ARRAY_SIZE(ptp_filter), .filter = ptp_filter,
149e62d2df0SDaniel Borkmann 	};
150e62d2df0SDaniel Borkmann 
151e62d2df0SDaniel Borkmann 	BUG_ON(sk_unattached_filter_create(&ptp_insns, &ptp_prog));
152c1f19b51SRichard Cochran }
153