diff options
-rw-r--r-- | drivers/net/phy/phy.c | 5 | ||||
-rw-r--r-- | drivers/net/phy/phy_device.c | 2 | ||||
-rw-r--r-- | include/linux/netdevice.h | 4 | ||||
-rw-r--r-- | include/linux/phy.h | 22 | ||||
-rw-r--r-- | include/linux/skbuff.h | 31 | ||||
-rw-r--r-- | net/Kconfig | 10 | ||||
-rw-r--r-- | net/core/Makefile | 2 | ||||
-rw-r--r-- | net/core/dev.c | 3 | ||||
-rw-r--r-- | net/core/timestamping.c | 126 | ||||
-rw-r--r-- | net/socket.c | 4 |
10 files changed, 208 insertions, 1 deletions
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index bd88d818f082..5130db8f5c4e 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c | |||
@@ -361,6 +361,11 @@ int phy_mii_ioctl(struct phy_device *phydev, | |||
361 | } | 361 | } |
362 | break; | 362 | break; |
363 | 363 | ||
364 | case SIOCSHWTSTAMP: | ||
365 | if (phydev->drv->hwtstamp) | ||
366 | return phydev->drv->hwtstamp(phydev, ifr); | ||
367 | /* fall through */ | ||
368 | |||
364 | default: | 369 | default: |
365 | return -EOPNOTSUPP; | 370 | return -EOPNOTSUPP; |
366 | } | 371 | } |
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 1a99bb244106..c0761197c07e 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c | |||
@@ -460,6 +460,7 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, | |||
460 | } | 460 | } |
461 | 461 | ||
462 | phydev->attached_dev = dev; | 462 | phydev->attached_dev = dev; |
463 | dev->phydev = phydev; | ||
463 | 464 | ||
464 | phydev->dev_flags = flags; | 465 | phydev->dev_flags = flags; |
465 | 466 | ||
@@ -513,6 +514,7 @@ EXPORT_SYMBOL(phy_attach); | |||
513 | */ | 514 | */ |
514 | void phy_detach(struct phy_device *phydev) | 515 | void phy_detach(struct phy_device *phydev) |
515 | { | 516 | { |
517 | phydev->attached_dev->phydev = NULL; | ||
516 | phydev->attached_dev = NULL; | 518 | phydev->attached_dev = NULL; |
517 | 519 | ||
518 | /* If the device had no specific driver before (i.e. - it | 520 | /* If the device had no specific driver before (i.e. - it |
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index c4fedf000541..fdc3f2992230 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h | |||
@@ -54,6 +54,7 @@ | |||
54 | 54 | ||
55 | struct vlan_group; | 55 | struct vlan_group; |
56 | struct netpoll_info; | 56 | struct netpoll_info; |
57 | struct phy_device; | ||
57 | /* 802.11 specific */ | 58 | /* 802.11 specific */ |
58 | struct wireless_dev; | 59 | struct wireless_dev; |
59 | /* source back-compat hooks */ | 60 | /* source back-compat hooks */ |
@@ -1065,6 +1066,9 @@ struct net_device { | |||
1065 | #endif | 1066 | #endif |
1066 | /* n-tuple filter list attached to this device */ | 1067 | /* n-tuple filter list attached to this device */ |
1067 | struct ethtool_rx_ntuple_list ethtool_ntuple_list; | 1068 | struct ethtool_rx_ntuple_list ethtool_ntuple_list; |
1069 | |||
1070 | /* phy device may attach itself for hardware timestamping */ | ||
1071 | struct phy_device *phydev; | ||
1068 | }; | 1072 | }; |
1069 | #define to_net_dev(d) container_of(d, struct net_device, dev) | 1073 | #define to_net_dev(d) container_of(d, struct net_device, dev) |
1070 | 1074 | ||
diff --git a/include/linux/phy.h b/include/linux/phy.h index d63736a84002..6b0a782c6224 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h | |||
@@ -234,6 +234,8 @@ enum phy_state { | |||
234 | PHY_RESUMING | 234 | PHY_RESUMING |
235 | }; | 235 | }; |
236 | 236 | ||
237 | struct sk_buff; | ||
238 | |||
237 | /* phy_device: An instance of a PHY | 239 | /* phy_device: An instance of a PHY |
238 | * | 240 | * |
239 | * drv: Pointer to the driver for this PHY instance | 241 | * drv: Pointer to the driver for this PHY instance |
@@ -402,6 +404,26 @@ struct phy_driver { | |||
402 | /* Clears up any memory if needed */ | 404 | /* Clears up any memory if needed */ |
403 | void (*remove)(struct phy_device *phydev); | 405 | void (*remove)(struct phy_device *phydev); |
404 | 406 | ||
407 | /* Handles SIOCSHWTSTAMP ioctl for hardware time stamping. */ | ||
408 | int (*hwtstamp)(struct phy_device *phydev, struct ifreq *ifr); | ||
409 | |||
410 | /* | ||
411 | * Requests a Rx timestamp for 'skb'. If the skb is accepted, | ||
412 | * the phy driver promises to deliver it using netif_rx() as | ||
413 | * soon as a timestamp becomes available. One of the | ||
414 | * PTP_CLASS_ values is passed in 'type'. The function must | ||
415 | * return true if the skb is accepted for delivery. | ||
416 | */ | ||
417 | bool (*rxtstamp)(struct phy_device *dev, struct sk_buff *skb, int type); | ||
418 | |||
419 | /* | ||
420 | * Requests a Tx timestamp for 'skb'. The phy driver promises | ||
421 | * to deliver it to the socket's error queue as soon as a | ||
422 | * timestamp becomes available. One of the PTP_CLASS_ values | ||
423 | * is passed in 'type'. | ||
424 | */ | ||
425 | void (*txtstamp)(struct phy_device *dev, struct sk_buff *skb, int type); | ||
426 | |||
405 | struct device_driver driver; | 427 | struct device_driver driver; |
406 | }; | 428 | }; |
407 | #define to_phy_driver(d) container_of(d, struct phy_driver, driver) | 429 | #define to_phy_driver(d) container_of(d, struct phy_driver, driver) |
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index a1b0400c8d86..f5aa87e1e0c8 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h | |||
@@ -1933,6 +1933,36 @@ static inline ktime_t net_invalid_timestamp(void) | |||
1933 | return ktime_set(0, 0); | 1933 | return ktime_set(0, 0); |
1934 | } | 1934 | } |
1935 | 1935 | ||
1936 | extern void skb_timestamping_init(void); | ||
1937 | |||
1938 | #ifdef CONFIG_NETWORK_PHY_TIMESTAMPING | ||
1939 | |||
1940 | extern void skb_clone_tx_timestamp(struct sk_buff *skb); | ||
1941 | extern bool skb_defer_rx_timestamp(struct sk_buff *skb); | ||
1942 | |||
1943 | #else /* CONFIG_NETWORK_PHY_TIMESTAMPING */ | ||
1944 | |||
1945 | static inline void skb_clone_tx_timestamp(struct sk_buff *skb) | ||
1946 | { | ||
1947 | } | ||
1948 | |||
1949 | static inline bool skb_defer_rx_timestamp(struct sk_buff *skb) | ||
1950 | { | ||
1951 | return false; | ||
1952 | } | ||
1953 | |||
1954 | #endif /* !CONFIG_NETWORK_PHY_TIMESTAMPING */ | ||
1955 | |||
1956 | /** | ||
1957 | * skb_complete_tx_timestamp() - deliver cloned skb with tx timestamps | ||
1958 | * | ||
1959 | * @skb: clone of the the original outgoing packet | ||
1960 | * @hwtstamps: hardware time stamps | ||
1961 | * | ||
1962 | */ | ||
1963 | void skb_complete_tx_timestamp(struct sk_buff *skb, | ||
1964 | struct skb_shared_hwtstamps *hwtstamps); | ||
1965 | |||
1936 | /** | 1966 | /** |
1937 | * skb_tstamp_tx - queue clone of skb with send time stamps | 1967 | * skb_tstamp_tx - queue clone of skb with send time stamps |
1938 | * @orig_skb: the original outgoing packet | 1968 | * @orig_skb: the original outgoing packet |
@@ -1965,6 +1995,7 @@ static inline void sw_tx_timestamp(struct sk_buff *skb) | |||
1965 | */ | 1995 | */ |
1966 | static inline void skb_tx_timestamp(struct sk_buff *skb) | 1996 | static inline void skb_tx_timestamp(struct sk_buff *skb) |
1967 | { | 1997 | { |
1998 | skb_clone_tx_timestamp(skb); | ||
1968 | sw_tx_timestamp(skb); | 1999 | sw_tx_timestamp(skb); |
1969 | } | 2000 | } |
1970 | 2001 | ||
diff --git a/net/Kconfig b/net/Kconfig index 0d68b40fc0e6..b3250944cde9 100644 --- a/net/Kconfig +++ b/net/Kconfig | |||
@@ -86,6 +86,16 @@ config NETWORK_SECMARK | |||
86 | to nfmark, but designated for security purposes. | 86 | to nfmark, but designated for security purposes. |
87 | If you are unsure how to answer this question, answer N. | 87 | If you are unsure how to answer this question, answer N. |
88 | 88 | ||
89 | config NETWORK_PHY_TIMESTAMPING | ||
90 | bool "Timestamping in PHY devices" | ||
91 | depends on EXPERIMENTAL | ||
92 | help | ||
93 | This allows timestamping of network packets by PHYs with | ||
94 | hardware timestamping capabilities. This option adds some | ||
95 | overhead in the transmit and receive paths. | ||
96 | |||
97 | If you are unsure how to answer this question, answer N. | ||
98 | |||
89 | menuconfig NETFILTER | 99 | menuconfig NETFILTER |
90 | bool "Network packet filtering framework (Netfilter)" | 100 | bool "Network packet filtering framework (Netfilter)" |
91 | ---help--- | 101 | ---help--- |
diff --git a/net/core/Makefile b/net/core/Makefile index 51c3eec850ef..8a04dd22cf77 100644 --- a/net/core/Makefile +++ b/net/core/Makefile | |||
@@ -18,4 +18,4 @@ obj-$(CONFIG_NET_DMA) += user_dma.o | |||
18 | obj-$(CONFIG_FIB_RULES) += fib_rules.o | 18 | obj-$(CONFIG_FIB_RULES) += fib_rules.o |
19 | obj-$(CONFIG_TRACEPOINTS) += net-traces.o | 19 | obj-$(CONFIG_TRACEPOINTS) += net-traces.o |
20 | obj-$(CONFIG_NET_DROP_MONITOR) += drop_monitor.o | 20 | obj-$(CONFIG_NET_DROP_MONITOR) += drop_monitor.o |
21 | 21 | obj-$(CONFIG_NETWORK_PHY_TIMESTAMPING) += timestamping.o | |
diff --git a/net/core/dev.c b/net/core/dev.c index e2b9fa2c917e..1c002c7ef5d5 100644 --- a/net/core/dev.c +++ b/net/core/dev.c | |||
@@ -2957,6 +2957,9 @@ int netif_receive_skb(struct sk_buff *skb) | |||
2957 | if (netdev_tstamp_prequeue) | 2957 | if (netdev_tstamp_prequeue) |
2958 | net_timestamp_check(skb); | 2958 | net_timestamp_check(skb); |
2959 | 2959 | ||
2960 | if (skb_defer_rx_timestamp(skb)) | ||
2961 | return NET_RX_SUCCESS; | ||
2962 | |||
2960 | #ifdef CONFIG_RPS | 2963 | #ifdef CONFIG_RPS |
2961 | { | 2964 | { |
2962 | struct rps_dev_flow voidflow, *rflow = &voidflow; | 2965 | struct rps_dev_flow voidflow, *rflow = &voidflow; |
diff --git a/net/core/timestamping.c b/net/core/timestamping.c new file mode 100644 index 000000000000..0ae6c22da85b --- /dev/null +++ b/net/core/timestamping.c | |||
@@ -0,0 +1,126 @@ | |||
1 | /* | ||
2 | * PTP 1588 clock support - support for timestamping in PHY devices | ||
3 | * | ||
4 | * Copyright (C) 2010 OMICRON electronics GmbH | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
19 | */ | ||
20 | #include <linux/errqueue.h> | ||
21 | #include <linux/phy.h> | ||
22 | #include <linux/ptp_classify.h> | ||
23 | #include <linux/skbuff.h> | ||
24 | |||
25 | static struct sock_filter ptp_filter[] = { | ||
26 | PTP_FILTER | ||
27 | }; | ||
28 | |||
29 | static unsigned int classify(struct sk_buff *skb) | ||
30 | { | ||
31 | if (likely(skb->dev && | ||
32 | skb->dev->phydev && | ||
33 | skb->dev->phydev->drv)) | ||
34 | return sk_run_filter(skb, ptp_filter, ARRAY_SIZE(ptp_filter)); | ||
35 | else | ||
36 | return PTP_CLASS_NONE; | ||
37 | } | ||
38 | |||
39 | void skb_clone_tx_timestamp(struct sk_buff *skb) | ||
40 | { | ||
41 | struct phy_device *phydev; | ||
42 | struct sk_buff *clone; | ||
43 | struct sock *sk = skb->sk; | ||
44 | unsigned int type; | ||
45 | |||
46 | if (!sk) | ||
47 | return; | ||
48 | |||
49 | type = classify(skb); | ||
50 | |||
51 | switch (type) { | ||
52 | case PTP_CLASS_V1_IPV4: | ||
53 | case PTP_CLASS_V1_IPV6: | ||
54 | case PTP_CLASS_V2_IPV4: | ||
55 | case PTP_CLASS_V2_IPV6: | ||
56 | case PTP_CLASS_V2_L2: | ||
57 | case PTP_CLASS_V2_VLAN: | ||
58 | phydev = skb->dev->phydev; | ||
59 | if (likely(phydev->drv->txtstamp)) { | ||
60 | clone = skb_clone(skb, GFP_ATOMIC); | ||
61 | if (!clone) | ||
62 | return; | ||
63 | clone->sk = sk; | ||
64 | phydev->drv->txtstamp(phydev, clone, type); | ||
65 | } | ||
66 | break; | ||
67 | default: | ||
68 | break; | ||
69 | } | ||
70 | } | ||
71 | |||
72 | void skb_complete_tx_timestamp(struct sk_buff *skb, | ||
73 | struct skb_shared_hwtstamps *hwtstamps) | ||
74 | { | ||
75 | struct sock *sk = skb->sk; | ||
76 | struct sock_exterr_skb *serr; | ||
77 | int err; | ||
78 | |||
79 | if (!hwtstamps) | ||
80 | return; | ||
81 | |||
82 | *skb_hwtstamps(skb) = *hwtstamps; | ||
83 | serr = SKB_EXT_ERR(skb); | ||
84 | memset(serr, 0, sizeof(*serr)); | ||
85 | serr->ee.ee_errno = ENOMSG; | ||
86 | serr->ee.ee_origin = SO_EE_ORIGIN_TIMESTAMPING; | ||
87 | skb->sk = NULL; | ||
88 | err = sock_queue_err_skb(sk, skb); | ||
89 | if (err) | ||
90 | kfree_skb(skb); | ||
91 | } | ||
92 | EXPORT_SYMBOL_GPL(skb_complete_tx_timestamp); | ||
93 | |||
94 | bool skb_defer_rx_timestamp(struct sk_buff *skb) | ||
95 | { | ||
96 | struct phy_device *phydev; | ||
97 | unsigned int type; | ||
98 | |||
99 | skb_push(skb, ETH_HLEN); | ||
100 | |||
101 | type = classify(skb); | ||
102 | |||
103 | skb_pull(skb, ETH_HLEN); | ||
104 | |||
105 | switch (type) { | ||
106 | case PTP_CLASS_V1_IPV4: | ||
107 | case PTP_CLASS_V1_IPV6: | ||
108 | case PTP_CLASS_V2_IPV4: | ||
109 | case PTP_CLASS_V2_IPV6: | ||
110 | case PTP_CLASS_V2_L2: | ||
111 | case PTP_CLASS_V2_VLAN: | ||
112 | phydev = skb->dev->phydev; | ||
113 | if (likely(phydev->drv->rxtstamp)) | ||
114 | return phydev->drv->rxtstamp(phydev, skb, type); | ||
115 | break; | ||
116 | default: | ||
117 | break; | ||
118 | } | ||
119 | |||
120 | return false; | ||
121 | } | ||
122 | |||
123 | void __init skb_timestamping_init(void) | ||
124 | { | ||
125 | BUG_ON(sk_chk_filter(ptp_filter, ARRAY_SIZE(ptp_filter))); | ||
126 | } | ||
diff --git a/net/socket.c b/net/socket.c index 6fe484122a44..2270b941bcc7 100644 --- a/net/socket.c +++ b/net/socket.c | |||
@@ -2394,6 +2394,10 @@ static int __init sock_init(void) | |||
2394 | netfilter_init(); | 2394 | netfilter_init(); |
2395 | #endif | 2395 | #endif |
2396 | 2396 | ||
2397 | #ifdef CONFIG_NETWORK_PHY_TIMESTAMPING | ||
2398 | skb_timestamping_init(); | ||
2399 | #endif | ||
2400 | |||
2397 | return 0; | 2401 | return 0; |
2398 | } | 2402 | } |
2399 | 2403 | ||