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 | ||
