timestamping.c 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  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. #include <linux/export.h>
  25. static struct sock_filter ptp_filter[] = {
  26. PTP_FILTER
  27. };
  28. static unsigned int classify(const struct sk_buff *skb)
  29. {
  30. if (likely(skb->dev &&
  31. skb->dev->phydev &&
  32. skb->dev->phydev->drv))
  33. return sk_run_filter(skb, ptp_filter);
  34. else
  35. return PTP_CLASS_NONE;
  36. }
  37. void skb_clone_tx_timestamp(struct sk_buff *skb)
  38. {
  39. struct phy_device *phydev;
  40. struct sk_buff *clone;
  41. struct sock *sk = skb->sk;
  42. unsigned int type;
  43. if (!sk)
  44. return;
  45. type = classify(skb);
  46. switch (type) {
  47. case PTP_CLASS_V1_IPV4:
  48. case PTP_CLASS_V1_IPV6:
  49. case PTP_CLASS_V2_IPV4:
  50. case PTP_CLASS_V2_IPV6:
  51. case PTP_CLASS_V2_L2:
  52. case PTP_CLASS_V2_VLAN:
  53. phydev = skb->dev->phydev;
  54. if (likely(phydev->drv->txtstamp)) {
  55. if (!atomic_inc_not_zero(&sk->sk_refcnt))
  56. return;
  57. clone = skb_clone(skb, GFP_ATOMIC);
  58. if (!clone) {
  59. sock_put(sk);
  60. return;
  61. }
  62. clone->sk = sk;
  63. phydev->drv->txtstamp(phydev, clone, type);
  64. }
  65. break;
  66. default:
  67. break;
  68. }
  69. }
  70. EXPORT_SYMBOL_GPL(skb_clone_tx_timestamp);
  71. void skb_complete_tx_timestamp(struct sk_buff *skb,
  72. struct skb_shared_hwtstamps *hwtstamps)
  73. {
  74. struct sock *sk = skb->sk;
  75. struct sock_exterr_skb *serr;
  76. int err;
  77. if (!hwtstamps) {
  78. sock_put(sk);
  79. kfree_skb(skb);
  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. sock_put(sk);
  90. if (err)
  91. kfree_skb(skb);
  92. }
  93. EXPORT_SYMBOL_GPL(skb_complete_tx_timestamp);
  94. bool skb_defer_rx_timestamp(struct sk_buff *skb)
  95. {
  96. struct phy_device *phydev;
  97. unsigned int type;
  98. if (skb_headroom(skb) < ETH_HLEN)
  99. return false;
  100. __skb_push(skb, ETH_HLEN);
  101. type = classify(skb);
  102. __skb_pull(skb, ETH_HLEN);
  103. switch (type) {
  104. case PTP_CLASS_V1_IPV4:
  105. case PTP_CLASS_V1_IPV6:
  106. case PTP_CLASS_V2_IPV4:
  107. case PTP_CLASS_V2_IPV6:
  108. case PTP_CLASS_V2_L2:
  109. case PTP_CLASS_V2_VLAN:
  110. phydev = skb->dev->phydev;
  111. if (likely(phydev->drv->rxtstamp))
  112. return phydev->drv->rxtstamp(phydev, skb, type);
  113. break;
  114. default:
  115. break;
  116. }
  117. return false;
  118. }
  119. EXPORT_SYMBOL_GPL(skb_defer_rx_timestamp);
  120. void __init skb_timestamping_init(void)
  121. {
  122. BUG_ON(sk_chk_filter(ptp_filter, ARRAY_SIZE(ptp_filter)));
  123. }