DPDK patches and discussions
 help / color / mirror / Atom feed
* [dpdk-dev] [PATCH 0/3] add sample ptp slave application
@ 2015-10-02 15:20 Daniel Mrzyglod
  2015-10-02 15:20 ` [dpdk-dev] [PATCH 1/3] ethdev: add additional ieee1588 support functions Daniel Mrzyglod
                   ` (6 more replies)
  0 siblings, 7 replies; 84+ messages in thread
From: Daniel Mrzyglod @ 2015-10-02 15:20 UTC (permalink / raw)
  To: dev

Add a sample application that acts as a PTP slave using the DPDK IEEE1588
functions.

Also add some additional IEEE1588 support functions to enable getting,
setting and adjusting the device time.

Some V1 limitations of the app:

* The mater clock sequence id and clock id are not verified fully.
* Only one master clock is supported/assumed.

To be added:

* Support for igb and i40e.
* Multiple checks on clock source.
* Some additional protocol values may be required to be parsed for more
  complex PTP environments.
* Add frequency adjustment as well as absolute time adjustment.
* Make the implementation NIC speed independent.
* Check for linkup/down.




Daniel Mrzyglod (3):
  ethdev: add additional ieee1588 support functions
  ixgbe: add additional ieee1588 support functions
  example: PTP client slave minimal implementation

 MAINTAINERS                            |   3 +
 drivers/net/ixgbe/ixgbe_ethdev.c       | 250 +++++++++++++++-
 drivers/net/ixgbe/ixgbe_ethdev.h       |  24 ++
 examples/Makefile                      |   1 +
 examples/ptpclient/Makefile            |  59 ++++
 examples/ptpclient/ptpclient.c         | 525 +++++++++++++++++++++++++++++++++
 lib/librte_ether/rte_ethdev.c          |  36 +++
 lib/librte_ether/rte_ethdev.h          |  64 ++++
 lib/librte_ether/rte_ether_version.map |   9 +
 9 files changed, 960 insertions(+), 11 deletions(-)
 create mode 100644 examples/ptpclient/Makefile
 create mode 100644 examples/ptpclient/ptpclient.c

-- 
2.1.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH 1/3] ethdev: add additional ieee1588 support functions
  2015-10-02 15:20 [dpdk-dev] [PATCH 0/3] add sample ptp slave application Daniel Mrzyglod
@ 2015-10-02 15:20 ` Daniel Mrzyglod
  2015-10-02 15:20 ` [dpdk-dev] [PATCH 2/3] ixgbe: " Daniel Mrzyglod
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 84+ messages in thread
From: Daniel Mrzyglod @ 2015-10-02 15:20 UTC (permalink / raw)
  To: dev

Add additional functions to support the existing IEEE1588
functionality.

* rte_eth_timesync_settime(), function to set the device clock time.
* rte_eth_timesync_gettime, function to get the device clock time.
* rte_eth_timesync_adjust, function to adjust the device clock time.

Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
---
 lib/librte_ether/rte_ethdev.c          | 36 +++++++++++++++++++
 lib/librte_ether/rte_ethdev.h          | 64 ++++++++++++++++++++++++++++++++++
 lib/librte_ether/rte_ether_version.map |  9 +++++
 3 files changed, 109 insertions(+)

diff --git a/lib/librte_ether/rte_ethdev.c b/lib/librte_ether/rte_ethdev.c
index f593f6e..6f26f3a 100644
--- a/lib/librte_ether/rte_ethdev.c
+++ b/lib/librte_ether/rte_ethdev.c
@@ -3272,6 +3272,42 @@ rte_eth_timesync_read_rx_timestamp(uint8_t port_id, struct timespec *timestamp,
 }
 
 int
+rte_eth_timesync_adjust(uint8_t port_id, int64_t delta)
+{
+	struct rte_eth_dev *dev;
+
+	VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	FUNC_PTR_OR_ERR_RET(*dev->dev_ops->timesync_adjust, -ENOTSUP);
+	return (*dev->dev_ops->timesync_adjust)(dev, delta);
+}
+
+int
+rte_eth_timesync_gettime(uint8_t port_id, struct timespec *timestamp)
+{
+	struct rte_eth_dev *dev;
+
+	VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	FUNC_PTR_OR_ERR_RET(*dev->dev_ops->timesync_gettime, -ENOTSUP);
+	return (*dev->dev_ops->timesync_gettime)(dev, timestamp);
+}
+
+int
+rte_eth_timesync_settime(uint8_t port_id, struct timespec *timestamp)
+{
+	struct rte_eth_dev *dev;
+
+	VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	FUNC_PTR_OR_ERR_RET(*dev->dev_ops->timesync_settime, -ENOTSUP);
+	return (*dev->dev_ops->timesync_settime)(dev, timestamp);
+}
+
+int
 rte_eth_timesync_read_tx_timestamp(uint8_t port_id, struct timespec *timestamp)
 {
 	struct rte_eth_dev *dev;
diff --git a/lib/librte_ether/rte_ethdev.h b/lib/librte_ether/rte_ethdev.h
index 8a8c82b..6fdaacd 100644
--- a/lib/librte_ether/rte_ethdev.h
+++ b/lib/librte_ether/rte_ethdev.h
@@ -1129,6 +1129,17 @@ typedef int (*eth_timesync_read_tx_timestamp_t)(struct rte_eth_dev *dev,
 						struct timespec *timestamp);
 /**< @internal Function used to read a TX IEEE1588/802.1AS timestamp. */
 
+typedef int (*eth_timesync_adjust)(struct rte_eth_dev *dev, int64_t);
+/**< @internal Function used to adjust device clock */
+
+typedef int (*eth_timesync_gettime)(struct rte_eth_dev *dev,
+						struct timespec *timestamp);
+/**< @internal Function used to get time from device clock. */
+
+typedef int (*eth_timesync_settime)(struct rte_eth_dev *dev,
+						struct timespec *timestamp);
+/**< @internal Function used to get time from device clock */
+
 typedef int (*eth_get_reg_length_t)(struct rte_eth_dev *dev);
 /**< @internal Retrieve device register count  */
 
@@ -1312,6 +1323,12 @@ struct eth_dev_ops {
 	eth_timesync_read_rx_timestamp_t timesync_read_rx_timestamp;
 	/** Read the IEEE1588/802.1AS TX timestamp. */
 	eth_timesync_read_tx_timestamp_t timesync_read_tx_timestamp;
+	/** Adjust the device clock */
+	eth_timesync_adjust timesync_adjust;
+	/** Get the device clock timespec */
+	eth_timesync_gettime timesync_gettime;
+	/** Set the device clock timespec */
+	eth_timesync_settime timesync_settime;
 };
 
 /**
@@ -3598,6 +3615,53 @@ extern int rte_eth_timesync_read_rx_timestamp(uint8_t port_id,
 extern int rte_eth_timesync_read_tx_timestamp(uint8_t port_id,
 					      struct timespec *timestamp);
 
+/**
+ * Adjust the timesync clock on an Ethernet device..
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param delta
+ *   The adjustment in nanoseconds
+ *
+ * @return
+ *   - 0: Success.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -ENOTSUP: The function is not supported by the Ethernet driver.
+ */
+extern int rte_eth_timesync_adjust(uint8_t port_id, int64_t delta);
+
+/**
+ * Read the time from the timesync clock on an Ethernet device.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param time
+ *   Pointer to the timespec struct.
+ *
+ * @return
+ *   - 0: Success.
+ */
+extern int rte_eth_timesync_gettime(uint8_t port_id,
+	      struct timespec *time);
+
+
+/**
+ * Set the time of the timesync clock on an Ethernet device.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param time
+ *   Pointer to the timespec struct.
+ *
+ * @return
+ *   - 0: Success.
+ *   - -EINVAL: No timestamp is available.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -ENOTSUP: The function is not supported by the Ethernet driver.
+ */
+extern int rte_eth_timesync_settime(uint8_t port_id,
+	      struct timespec *time);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/librte_ether/rte_ether_version.map b/lib/librte_ether/rte_ether_version.map
index 8345a6c..0820af3 100644
--- a/lib/librte_ether/rte_ether_version.map
+++ b/lib/librte_ether/rte_ether_version.map
@@ -127,3 +127,12 @@ DPDK_2.1 {
 	rte_eth_timesync_read_tx_timestamp;
 
 } DPDK_2.0;
+
+DPDK_2.2 {
+	global:
+
+	rte_eth_timesync_adjust;
+	rte_eth_timesync_gettime;
+	rte_eth_timesync_settime;
+
+} DPDK_2.1;
-- 
2.1.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH 2/3] ixgbe: add additional ieee1588 support functions
  2015-10-02 15:20 [dpdk-dev] [PATCH 0/3] add sample ptp slave application Daniel Mrzyglod
  2015-10-02 15:20 ` [dpdk-dev] [PATCH 1/3] ethdev: add additional ieee1588 support functions Daniel Mrzyglod
@ 2015-10-02 15:20 ` Daniel Mrzyglod
  2015-10-02 15:20 ` [dpdk-dev] [PATCH 3/3] example: PTP client slave minimal implementation Daniel Mrzyglod
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 84+ messages in thread
From: Daniel Mrzyglod @ 2015-10-02 15:20 UTC (permalink / raw)
  To: dev

Add additional functions to support the existing IEEE1588
functionality and to enable getting, setting and adjusting
the device time.

Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
---
 drivers/net/ixgbe/ixgbe_ethdev.c | 250 +++++++++++++++++++++++++++++++++++++--
 drivers/net/ixgbe/ixgbe_ethdev.h |  24 ++++
 2 files changed, 263 insertions(+), 11 deletions(-)

diff --git a/drivers/net/ixgbe/ixgbe_ethdev.c b/drivers/net/ixgbe/ixgbe_ethdev.c
index ec2918c..d0c575f 100644
--- a/drivers/net/ixgbe/ixgbe_ethdev.c
+++ b/drivers/net/ixgbe/ixgbe_ethdev.c
@@ -126,10 +126,12 @@
 #define IXGBE_HKEY_MAX_INDEX 10
 
 /* Additional timesync values. */
-#define IXGBE_TIMINCA_16NS_SHIFT 24
-#define IXGBE_TIMINCA_INCVALUE   16000000
-#define IXGBE_TIMINCA_INIT       ((0x02 << IXGBE_TIMINCA_16NS_SHIFT) \
-				  | IXGBE_TIMINCA_INCVALUE)
+#define NSEC_PER_SEC             1000000000L
+#define IXGBE_INCVAL_10GB        0x66666666
+#define IXGBE_INCVAL_SHIFT_10GB  28
+#define IXGBE_INCVAL_SHIFT_82599 7
+#define IXGBE_INCPER_SHIFT_82599 24
+#define IXGBE_CYCLECOUTER_MASK   0xffffffffffffffff
 
 static int eth_ixgbe_dev_init(struct rte_eth_dev *eth_dev);
 static int eth_ixgbe_dev_uninit(struct rte_eth_dev *eth_dev);
@@ -325,6 +327,11 @@ static int ixgbe_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 					    uint32_t flags);
 static int ixgbe_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 					    struct timespec *timestamp);
+static int ixgbe_timesync_adjust(struct rte_eth_dev *dev, int64_t delta);
+static int ixgbe_timesync_gettime(struct rte_eth_dev *dev,
+		struct timespec *timestamp);
+static int ixgbe_timesync_settime(struct rte_eth_dev *dev,
+		struct timespec *timestamp);
 
 /*
  * Define VF Stats MACRO for Non "cleared on read" register
@@ -465,6 +472,9 @@ static const struct eth_dev_ops ixgbe_eth_dev_ops = {
 	.get_eeprom_length    = ixgbe_get_eeprom_length,
 	.get_eeprom           = ixgbe_get_eeprom,
 	.set_eeprom           = ixgbe_set_eeprom,
+	.timesync_adjust = ixgbe_timesync_adjust,
+	.timesync_gettime = ixgbe_timesync_gettime,
+	.timesync_settime = ixgbe_timesync_settime,
 };
 
 /*
@@ -5241,20 +5251,223 @@ ixgbe_dev_set_mc_addr_list(struct rte_eth_dev *dev,
 					 ixgbe_dev_addr_list_itr, TRUE);
 }
 
+static inline uint64_t
+timespec_to_ns(const struct timespec *ts)
+{
+	return ((uint64_t) ts->tv_sec * NSEC_PER_SEC) + ts->tv_nsec;
+}
+
+static struct timespec
+ns_to_timespec(int64_t nsec)
+{
+	 struct timespec ts = {0, 0};
+	 int32_t rem;
+
+	 if (nsec == 0)
+			 return ts;
+	 rem = nsec % NSEC_PER_SEC;
+	 ts.tv_sec = nsec / NSEC_PER_SEC;
+
+	 if (unlikely(rem < 0)) {
+			 ts.tv_sec--;
+			 rem += NSEC_PER_SEC;
+	 }
+
+	 ts.tv_nsec = rem;
+
+	 return ts;
+}
+
+static inline uint64_t
+cyclecounter_cycles_to_ns(const struct cyclecounter *cc,
+				      uint64_t cycles, uint64_t mask, uint64_t *frac)
+{
+	uint64_t ns = cycles;
+
+	ns = (ns * cc->mult) + *frac;
+	*frac = ns & mask;
+	return ns >> cc->shift;
+}
+
+static uint64_t
+cyclecounter_cycles_to_ns_backwards(const struct cyclecounter *cc,
+			       uint64_t cycles, uint64_t mask __rte_unused, uint64_t frac)
+{
+	uint64_t ns = (uint64_t) cycles;
+
+	ns = ((ns * cc->mult) - frac) >> cc->shift;
+
+	return ns;
+}
+
+static uint64_t
+timecounter_cycles_to_ns_time(struct timecounter *tc, uint64_t cycle_tstamp)
+{
+	uint64_t delta = (cycle_tstamp - tc->cycle_last) & tc->cc->mask;
+	uint64_t nsec = tc->nsec, frac = tc->frac;
+
+
+	/* Cycle counts that are corectly converted as they
+	 * are between -1/2 max cycle count and +1/2max cycle count
+	 * */
+	if (delta > tc->cc->mask / 2) {
+		delta = (tc->cycle_last - cycle_tstamp) & tc->cc->mask;
+		nsec -= cyclecounter_cycles_to_ns_backwards(tc->cc, delta, tc->mask, frac);
+	} else {
+		nsec += cyclecounter_cycles_to_ns(tc->cc, delta, tc->mask, &frac);
+	}
+
+	return nsec;
+}
+
+static uint64_t
+ixgbe_read_timesync_cyclecounter(struct rte_eth_dev *dev)
+{
+	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	uint64_t systim_cycles = 0;
+
+	systim_cycles |= (uint64_t)IXGBE_READ_REG(hw, IXGBE_SYSTIML);
+	systim_cycles |= (uint64_t)IXGBE_READ_REG(hw, IXGBE_SYSTIMH) << 32;
+
+	return systim_cycles;
+}
+
+static uint64_t
+timecounter_read_ns_delta(struct rte_eth_dev *dev)
+{
+	uint64_t cycle_now, cycle_delta;
+	uint64_t ns_offset;
+	struct ixgbe_adapter *adapter =
+				(struct ixgbe_adapter *)dev->data->dev_private;
+
+	/* read cycle counter: */
+	cycle_now = adapter->tc.cc->read(dev);
+
+	/* calculate the delta since the last timecounter_read_delta(): */
+	cycle_delta = (cycle_now - adapter->tc.cycle_last) & adapter->tc.cc->mask;
+
+	/* convert to nanoseconds: */
+	ns_offset = cyclecounter_cycles_to_ns(adapter->tc.cc, cycle_delta,
+					adapter->tc.mask, &adapter->tc.frac);
+
+	/* update time stamp of timecounter_read_delta() call: */
+	adapter->tc.cycle_last = cycle_now;
+
+	return ns_offset;
+}
+
+static uint64_t
+timecounter_read(struct rte_eth_dev *dev)
+{
+	uint64_t nsec;
+	struct ixgbe_adapter *adapter =
+			(struct ixgbe_adapter *)dev->data->dev_private;
+
+	/* increment time by nanoseconds since last call */
+	nsec = timecounter_read_ns_delta(dev);
+	nsec += adapter->tc.nsec;
+	adapter->tc.nsec = nsec;
+
+	return nsec;
+}
+
+
+static void
+timecounter_init(struct rte_eth_dev *dev,
+		      uint64_t start_time)
+{
+	struct ixgbe_adapter *adapter =
+				(struct ixgbe_adapter *)dev->data->dev_private;
+	adapter->tc.cc = &adapter->cc;
+	adapter->tc.cycle_last = adapter->tc.cc->read(dev);
+	adapter->tc.nsec = start_time;
+	adapter->tc.mask = (1ULL << adapter->tc.cc->shift) - 1;
+	adapter->tc.frac = 0;
+}
+
+static void
+ixgbe_start_cyclecounter(struct rte_eth_dev *dev)
+{
+	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct ixgbe_adapter *adapter =
+		(struct ixgbe_adapter *)dev->data->dev_private;
+	uint32_t incval = 0;
+	uint32_t shift = 0;
+
+	incval = IXGBE_INCVAL_10GB >> IXGBE_INCVAL_SHIFT_82599;
+	shift = IXGBE_INCVAL_SHIFT_10GB - IXGBE_INCVAL_SHIFT_82599;
+
+	IXGBE_WRITE_REG(hw, IXGBE_TIMINCA,
+			(1 << IXGBE_INCPER_SHIFT_82599) |
+			incval);
+
+	memset(&adapter->cc, 0, sizeof(struct cyclecounter));
+	adapter->cc.read = ixgbe_read_timesync_cyclecounter;
+	adapter->cc.mask = IXGBE_CYCLECOUTER_MASK;
+	adapter->cc.shift = shift;
+	adapter->cc.mult = 1;
+}
+
+static int
+ixgbe_timesync_adjust(struct rte_eth_dev *dev, int64_t delta)
+{
+	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct ixgbe_adapter *adapter =
+			(struct ixgbe_adapter *)dev->data->dev_private;
+
+	adapter->tc.nsec += delta;
+	IXGBE_WRITE_REG(hw, IXGBE_TSAUXC, 0x0);
+	IXGBE_WRITE_FLUSH(hw);
+
+	return 0;
+}
+
+static int
+ixgbe_timesync_settime(struct rte_eth_dev *dev, struct timespec *ts)
+{
+	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	uint64_t ns;
+	ns = timespec_to_ns(ts);
+
+	/* reset the timecounter */
+	timecounter_init(dev, ns);
+	/* it's setup after every change */
+	IXGBE_WRITE_REG(hw, IXGBE_TSAUXC, 0x0);
+	IXGBE_WRITE_FLUSH(hw);
+
+	return 0;
+}
+
+static int
+ixgbe_timesync_gettime(struct rte_eth_dev *dev, struct timespec *ts)
+{
+	uint64_t ns;
+
+	ns = timecounter_read(dev);
+	*ts = ns_to_timespec(ns);
+
+	return 0;
+}
+
 static int
 ixgbe_timesync_enable(struct rte_eth_dev *dev)
 {
 	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
 	uint32_t tsync_ctl;
 	uint32_t tsauxc;
+	uint64_t ns;
+	struct timespec zerotime = {0, 0};
 
 	/* Enable system time for platforms where it isn't on by default. */
 	tsauxc = IXGBE_READ_REG(hw, IXGBE_TSAUXC);
 	tsauxc &= ~IXGBE_TSAUXC_DISABLE_SYSTIME;
-	IXGBE_WRITE_REG(hw, IXGBE_TSAUXC, tsauxc);
 
-	/* Start incrementing the register used to timestamp PTP packets. */
-	IXGBE_WRITE_REG(hw, IXGBE_TIMINCA, IXGBE_TIMINCA_INIT);
+	/* Set 0.0 epoch time to initialize timecounter*/
+	ns = timespec_to_ns(&zerotime);
+	ixgbe_start_cyclecounter(dev);
+	timecounter_init(dev, ns);
+
+	IXGBE_WRITE_REG(hw, IXGBE_TSAUXC, tsauxc);
 
 	/* Enable L2 filtering of IEEE1588/802.1AS Ethernet frame types. */
 	IXGBE_WRITE_REG(hw, IXGBE_ETQF(IXGBE_ETQF_FILTER_1588),
@@ -5272,6 +5485,9 @@ ixgbe_timesync_enable(struct rte_eth_dev *dev)
 	tsync_ctl |= IXGBE_TSYNCTXCTL_ENABLED;
 	IXGBE_WRITE_REG(hw, IXGBE_TSYNCTXCTL, tsync_ctl);
 
+	/* After writing to registers should be flush */
+	IXGBE_WRITE_FLUSH(hw);
+
 	return 0;
 }
 
@@ -5306,9 +5522,13 @@ ixgbe_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 				 uint32_t flags __rte_unused)
 {
 	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct ixgbe_adapter *adapter =
+		(struct ixgbe_adapter *)dev->data->dev_private;
+
 	uint32_t tsync_rxctl;
 	uint32_t rx_stmpl;
 	uint32_t rx_stmph;
+	uint64_t regival = 0;
 
 	tsync_rxctl = IXGBE_READ_REG(hw, IXGBE_TSYNCRXCTL);
 	if ((tsync_rxctl & IXGBE_TSYNCRXCTL_VALID) == 0)
@@ -5316,9 +5536,11 @@ ixgbe_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 
 	rx_stmpl = IXGBE_READ_REG(hw, IXGBE_RXSTMPL);
 	rx_stmph = IXGBE_READ_REG(hw, IXGBE_RXSTMPH);
+	timecounter_read(dev);
 
-	timestamp->tv_sec = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
-	timestamp->tv_nsec = 0;
+	regival = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
+	regival = timecounter_cycles_to_ns_time(&adapter->tc, regival);
+	*timestamp =  ns_to_timespec(regival);
 
 	return  0;
 }
@@ -5328,9 +5550,13 @@ ixgbe_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 				 struct timespec *timestamp)
 {
 	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct ixgbe_adapter *adapter =
+		(struct ixgbe_adapter *)dev->data->dev_private;
+
 	uint32_t tsync_txctl;
 	uint32_t tx_stmpl;
 	uint32_t tx_stmph;
+	uint64_t regival = 0;
 
 	tsync_txctl = IXGBE_READ_REG(hw, IXGBE_TSYNCTXCTL);
 	if ((tsync_txctl & IXGBE_TSYNCTXCTL_VALID) == 0)
@@ -5338,9 +5564,11 @@ ixgbe_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 
 	tx_stmpl = IXGBE_READ_REG(hw, IXGBE_TXSTMPL);
 	tx_stmph = IXGBE_READ_REG(hw, IXGBE_TXSTMPH);
+	timecounter_read(dev);
 
-	timestamp->tv_sec = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
-	timestamp->tv_nsec = 0;
+	regival = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
+	regival = timecounter_cycles_to_ns_time(&adapter->tc, regival);
+	*timestamp =  ns_to_timespec(regival);
 
 	return  0;
 }
diff --git a/drivers/net/ixgbe/ixgbe_ethdev.h b/drivers/net/ixgbe/ixgbe_ethdev.h
index c3d4f4f..6708e08 100644
--- a/drivers/net/ixgbe/ixgbe_ethdev.h
+++ b/drivers/net/ixgbe/ixgbe_ethdev.h
@@ -252,6 +252,27 @@ struct ixgbe_filter_info {
 };
 
 /*
+ * Structure for cyclecounter IEEE1588 functionality
+ */
+struct cyclecounter {
+	uint64_t (*read)(struct rte_eth_dev *dev);
+	uint64_t mask;
+	uint32_t mult;
+	uint32_t shift;
+};
+
+/*
+ * Structure for structure that keep UnixEpoch time
+ */
+struct timecounter {
+	struct cyclecounter *cc;
+	uint64_t cycle_last;
+	uint64_t nsec;
+	uint64_t mask;
+	uint64_t frac;
+};
+
+/*
  * Structure to store private data for each driver instance (for each port).
  */
 struct ixgbe_adapter {
@@ -273,6 +294,9 @@ struct ixgbe_adapter {
 
 	bool rx_bulk_alloc_allowed;
 	bool rx_vec_allowed;
+
+	struct cyclecounter cc;
+	struct timecounter tc;
 };
 
 #define IXGBE_DEV_PRIVATE_TO_HW(adapter)\
-- 
2.1.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH 3/3] example: PTP client slave minimal implementation
  2015-10-02 15:20 [dpdk-dev] [PATCH 0/3] add sample ptp slave application Daniel Mrzyglod
  2015-10-02 15:20 ` [dpdk-dev] [PATCH 1/3] ethdev: add additional ieee1588 support functions Daniel Mrzyglod
  2015-10-02 15:20 ` [dpdk-dev] [PATCH 2/3] ixgbe: " Daniel Mrzyglod
@ 2015-10-02 15:20 ` Daniel Mrzyglod
  2015-10-30  9:43 ` [dpdk-dev] [PATCH v2 0/6] add sample ptp slave application Daniel Mrzyglod
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 84+ messages in thread
From: Daniel Mrzyglod @ 2015-10-02 15:20 UTC (permalink / raw)
  To: dev

Add a sample application that acts as a PTP slave using the
DPDK ieee1588 functions.

Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
---
 MAINTAINERS                    |   3 +
 examples/Makefile              |   1 +
 examples/ptpclient/Makefile    |  59 +++++
 examples/ptpclient/ptpclient.c | 525 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 588 insertions(+)
 create mode 100644 examples/ptpclient/Makefile
 create mode 100644 examples/ptpclient/ptpclient.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 080a8e8..a80ce96 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -514,3 +514,6 @@ F: examples/tep_termination/
 F: examples/vmdq/
 F: examples/vmdq_dcb/
 F: doc/guides/sample_app_ug/vmdq_dcb_forwarding.rst
+
+M: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
+F: examples/ptpclient
\ No newline at end of file
diff --git a/examples/Makefile b/examples/Makefile
index b4eddbd..4672534 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -74,5 +74,6 @@ DIRS-$(CONFIG_RTE_LIBRTE_XEN_DOM0) += vhost_xen
 DIRS-y += vmdq
 DIRS-y += vmdq_dcb
 DIRS-$(CONFIG_RTE_LIBRTE_POWER) += vm_power_manager
+DIRS-$(CONFIG_RTE_LIBRTE_IEEE1588) += ptpclient
 
 include $(RTE_SDK)/mk/rte.extsubdir.mk
diff --git a/examples/ptpclient/Makefile b/examples/ptpclient/Makefile
new file mode 100644
index 0000000..503339f
--- /dev/null
+++ b/examples/ptpclient/Makefile
@@ -0,0 +1,59 @@
+#   BSD LICENSE
+#
+#   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+#   All rights reserved.
+#
+#   Redistribution and use in source and binary forms, with or without
+#   modification, are permitted provided that the following conditions
+#   are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in
+#       the documentation and/or other materials provided with the
+#       distribution.
+#     * Neither the name of Intel Corporation nor the names of its
+#       contributors may be used to endorse or promote products derived
+#       from this software without specific prior written permission.
+#
+#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+
+# Default target, can be overriddegitn by command line or environment
+RTE_TARGET ?= x86_64-native-linuxapp-gcc
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# binary name
+APP = ptpclient
+
+# all source are stored in SRCS-y
+SRCS-y := ptpclient.c
+#SRCS-$(CONFIG_RTE_LIBRTE_IEEE1588) := ptpclient.c
+
+
+CFLAGS += $(WERROR_FLAGS)
+
+# workaround for a gcc bug with noreturn attribute
+# http://gcc.gnu.org/bugzilla/show_bug.cgi?id=12603
+ifeq ($(CONFIG_RTE_TOOLCHAIN_GCC),y)
+CFLAGS_main.o += -Wno-return-type
+endif
+
+EXTRA_CFLAGS += -O3
+
+include $(RTE_SDK)/mk/rte.extapp.mk
diff --git a/examples/ptpclient/ptpclient.c b/examples/ptpclient/ptpclient.c
new file mode 100644
index 0000000..1fe8e6d
--- /dev/null
+++ b/examples/ptpclient/ptpclient.c
@@ -0,0 +1,525 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <inttypes.h>
+#include <rte_eal.h>
+#include <rte_ethdev.h>
+#include <rte_cycles.h>
+#include <rte_lcore.h>
+#include <rte_mbuf.h>
+#include <rte_ip.h>
+
+#define RX_RING_SIZE 128
+#define TX_RING_SIZE 512
+
+#define NUM_MBUFS            8191
+#define MBUF_CACHE_SIZE       250
+
+/* Values for the PTP messageType field */
+#define SYNC                  0x0
+#define DELAY_REQ             0x1
+#define PDELAY_REQ            0x2
+#define PDELAY_RESP           0x3
+#define FOLLOW_UP             0x8
+#define DELAY_RESP            0x9
+#define PDELAY_RESP_FOLLOW_UP 0xA
+#define ANNOUNCE              0xB
+#define SIGNALING             0xC
+#define MANAGEMENT            0xD
+
+#define NSEC_PER_SEC  1000000000L
+#define PTP_PROTOCOL       0x88F7
+
+struct rte_mempool *mbuf_pool;
+
+static const struct rte_eth_conf port_conf_default = {
+	.rxmode = { .max_rx_pkt_len = ETHER_MAX_LEN }
+};
+
+static const struct ether_addr ether_multicast = {
+		.addr_bytes = {0x01, 0x1b, 0x19, 0x0, 0x0, 0x0}
+};
+
+struct ptpv2_msg {
+	uint8_t msg_id;
+	uint8_t version; /* PTP V2 only */
+	uint8_t unused[34];
+};
+
+struct tstamp {
+	uint16_t   seconds_msb;
+	uint32_t   seconds_lsb;
+	uint32_t   nanoseconds;
+}  __attribute__((packed));
+
+struct clock_identity {
+	uint8_t id[8];
+};
+
+struct port_identity {
+	struct clock_identity  clock_identity;
+	uint16_t               port_number;
+}  __attribute__((packed));
+
+struct ptp_header {
+	uint8_t              msgtype;
+	uint8_t              ver;
+	uint16_t             message_length;
+	uint8_t              domain_number;
+	uint8_t              reserved1;
+	uint8_t              flag_field[2];
+	int64_t              correction;
+	uint32_t             reserved2;
+	struct port_identity source_port_identity;
+	uint16_t             sequenceId;
+	uint8_t              control;
+	int8_t               log_message_interval;
+} __attribute__((packed));
+
+struct sync_msg {
+	struct ptp_header   hdr;
+	struct tstamp       origin_timestamp;
+} __attribute__((packed));
+
+struct follow_up_msg {
+	struct ptp_header   hdr;
+	struct tstamp       precise_origin_timestamp;
+	uint8_t             suffix[0];
+} __attribute__((packed));
+
+struct delay_req_msg {
+	struct ptp_header   hdr;
+	struct tstamp       origin_timestamp;
+} __attribute__((packed));
+
+struct delay_resp_msg {
+	struct ptp_header    hdr;
+	struct tstamp        receive_timestamp;
+	struct port_identity requesting_port_identity;
+	uint8_t              suffix[0];
+} __attribute__((packed));
+
+struct ptp_message {
+	union {
+		struct ptp_header          header;
+		struct sync_msg            sync;
+		struct delay_req_msg       delay_req;
+		struct follow_up_msg       follow_up;
+		struct delay_resp_msg      delay_resp;
+	} __attribute__((packed));
+};
+
+struct ptpv2_data_slave_ordinary {
+	struct timespec timestamp;
+	struct timespec timestamp1;
+	struct timespec timestamp2;
+	struct timespec timestamp3;
+	struct timespec timestamp4;
+	int64_t delta;
+	uint8_t portid;
+	uint16_t seqID_SYNC;
+	uint16_t seqID_FOLLOWUP;
+	struct rte_mbuf *m;
+};
+
+void parse_ptp_frames(uint8_t portid, struct rte_mbuf *m);
+void print_clock_info(struct ptpv2_data_slave_ordinary *ptp_data);
+void parse_sync(struct ptpv2_data_slave_ordinary *ptp_data);
+void parse_fup(struct ptpv2_data_slave_ordinary *ptp_data);
+void parse_drsp(struct ptpv2_data_slave_ordinary *ptp_data);
+
+static inline uint64_t timespec64_to_ns(const struct timespec *ts)
+{
+	return ((uint64_t) ts->tv_sec * NSEC_PER_SEC) + ts->tv_nsec;
+}
+
+/*
+ * Initializes a given port using global settings and with the RX buffers
+ * coming from the mbuf_pool passed as a parameter.
+ */
+static inline int
+port_init(uint8_t port, struct rte_mempool *mbuf_pool)
+{
+	struct rte_eth_dev_info dev_info;
+	struct rte_eth_conf port_conf = port_conf_default;
+	const uint16_t rx_rings = 1;
+	const uint16_t tx_rings = 1;
+	int retval;
+	uint16_t q;
+
+
+	if (port >= rte_eth_dev_count())
+		return -1;
+
+	/* Configure the Ethernet device. */
+	retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf);
+	if (retval != 0)
+		return retval;
+
+	/* Allocate and set up 1 RX queue per Ethernet port. */
+	for (q = 0; q < rx_rings; q++) {
+		retval = rte_eth_rx_queue_setup(port, q, RX_RING_SIZE,
+				rte_eth_dev_socket_id(port), NULL, mbuf_pool);
+		if (retval < 0)
+			return retval;
+	}
+
+	/* Allocate and set up 1 TX queue per Ethernet port. */
+	for (q = 0; q < tx_rings; q++) {
+		/* Setup txq_flags */
+		struct rte_eth_txconf *txconf;
+		rte_eth_dev_info_get(q, &dev_info);
+		txconf = &dev_info.default_txconf;
+		txconf->txq_flags = 0;
+
+		retval = rte_eth_tx_queue_setup(port, q, TX_RING_SIZE,
+				rte_eth_dev_socket_id(port), txconf);
+		if (retval < 0)
+			return retval;
+	}
+
+	/* Start the Ethernet port. */
+	retval = rte_eth_dev_start(port);
+	if (retval < 0)
+		return retval;
+
+	/* Enable timesync timestamping for the Ethernet device */
+	rte_eth_timesync_enable(port);
+
+	/* Enable RX in promiscuous mode for the Ethernet device. */
+	rte_eth_promiscuous_enable(port);
+
+	return 0;
+}
+
+void
+print_clock_info(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	printf("\nT2 - Slave Clock.  %lds %ldns", (ptp_data->timestamp2.tv_sec),
+			(ptp_data->timestamp2.tv_nsec));
+	printf("\nT1 - Master Clock. %lds %ldns ",
+			ptp_data->timestamp1.tv_sec,
+			(ptp_data->timestamp1.tv_nsec));
+	printf("\nT3 - Slave Clock.  %lds %ldns", ptp_data->timestamp3.tv_sec,
+			(ptp_data->timestamp3.tv_nsec));
+	printf("\nT4 - Master Clock. %lds %ldns ",
+		ptp_data->timestamp4.tv_sec, (ptp_data->timestamp4.tv_nsec));
+	printf("\nDelta between master and slave clocks:%"PRId64"ns\n",
+			ptp_data->delta);
+	rte_eth_timesync_gettime(ptp_data->portid, &ptp_data->timestamp);
+	printf("Gettime:%llds %ldns",
+		(long long)ptp_data->timestamp.tv_sec,
+		ptp_data->timestamp.tv_nsec);
+	time_t tt = ptp_data->timestamp.tv_sec;
+	printf("\nConverted Current PTP Time: %.24s %.9ld ns \n", ctime(&tt), ptp_data->timestamp.tv_nsec);
+	printf("[Ctrl+C to quit]\n");
+
+	/* Clear screen and put cursor in column 1, row 1 */
+	printf("\033[2J\033[1;1H");
+}
+
+static int64_t
+delta_eval(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	int64_t delta;
+	uint64_t t1 = 0;
+	uint64_t t2 = 0;
+	uint64_t t3 = 0;
+	uint64_t t4 = 0;
+
+	t1 = timespec64_to_ns(&ptp_data->timestamp1);
+	t2 = timespec64_to_ns(&ptp_data->timestamp2);
+	t3 = timespec64_to_ns(&ptp_data->timestamp3);
+	t4 = timespec64_to_ns(&ptp_data->timestamp4);
+	/* Due to optimalisation */
+	delta = (t2-t1)-(t4-t3);
+	delta = delta/2;
+	delta = -delta;
+
+	return delta;
+}
+
+/*
+ * Parse PTP SYNC message
+ */
+void
+parse_sync(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	struct ptp_header *ptp_hdr;
+
+	ptp_hdr = (struct ptp_header *)(rte_pktmbuf_mtod(ptp_data->m, char *)
+			+ sizeof(struct ether_hdr));
+	ptp_data->seqID_SYNC = rte_be_to_cpu_16(ptp_hdr->sequenceId);
+	rte_eth_timesync_read_rx_timestamp(ptp_data->portid,
+			&ptp_data->timestamp2, 0);
+}
+/*
+ * Parse PTP FOLLOWUP message & Send DELAY_REQ to master clock.
+ */
+void
+parse_fup(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	struct ether_hdr *eth_hdr;
+	struct ptp_header *ptp_hdr;
+	struct clock_identity *client_clkid;
+	struct ptp_message *ptp_msg;
+	struct rte_mbuf *created_pkt;
+	struct ether_addr eth_multicast = ether_multicast;
+	size_t pkt_size;
+	int wait_us;
+	struct rte_mbuf *m = ptp_data->m;
+
+	eth_hdr = rte_pktmbuf_mtod(m, struct ether_hdr *);
+	ptp_hdr = (struct ptp_header *)(rte_pktmbuf_mtod(m, char *)
+			+ sizeof(struct ether_hdr));
+	ptp_data->seqID_FOLLOWUP = rte_be_to_cpu_16(ptp_hdr->sequenceId);
+	ptp_msg = (struct ptp_message *)
+			(rte_pktmbuf_mtod(m, char *) + sizeof(struct ether_hdr));
+	ptp_data->timestamp1.tv_nsec  =
+			ntohl(ptp_msg->follow_up.precise_origin_timestamp.nanoseconds);
+	ptp_data->timestamp1.tv_sec =
+		((uint64_t)ntohl
+				(ptp_msg->follow_up.precise_origin_timestamp.seconds_lsb)) |
+		(((uint64_t)ntohs
+				(ptp_msg->follow_up.precise_origin_timestamp.seconds_msb))
+				<< 32);
+
+	if (ptp_data->seqID_FOLLOWUP == ptp_data->seqID_SYNC) {
+
+		created_pkt = rte_pktmbuf_alloc(mbuf_pool);
+		pkt_size = sizeof(struct ether_hdr) + sizeof(struct ptp_message);
+		created_pkt->data_len = pkt_size;
+		created_pkt->pkt_len = pkt_size;
+		eth_hdr = rte_pktmbuf_mtod(created_pkt, struct ether_hdr *);
+		rte_eth_macaddr_get(ptp_data->portid, &eth_hdr->s_addr);
+
+		/* Set multicast adress 01-1B-19-00-00-00 */
+		ether_addr_copy(&eth_multicast, &eth_hdr->d_addr);
+
+		eth_hdr->ether_type = htons(PTP_PROTOCOL);
+		ptp_msg = (struct ptp_message *)(rte_pktmbuf_mtod(created_pkt, char *) +
+										sizeof(struct ether_hdr));
+
+		ptp_msg->delay_req.hdr.sequenceId = ptp_data->seqID_SYNC;
+		ptp_msg->delay_req.hdr.msgtype = DELAY_REQ;
+		ptp_msg->delay_req.hdr.ver = 2;
+		ptp_msg->delay_req.hdr.control = 1;
+		ptp_msg->delay_req.hdr.log_message_interval = 127;
+
+		/* Set up clock id */
+		client_clkid = &ptp_msg->delay_req.hdr.source_port_identity.clock_identity;
+
+		client_clkid->id[0] = eth_hdr->s_addr.addr_bytes[0];
+		client_clkid->id[1] = eth_hdr->s_addr.addr_bytes[1];
+		client_clkid->id[2] = eth_hdr->s_addr.addr_bytes[2];
+		client_clkid->id[3] = 0xFF;
+		client_clkid->id[4] = 0xFE;
+		client_clkid->id[5] = eth_hdr->s_addr.addr_bytes[3];
+		client_clkid->id[6] = eth_hdr->s_addr.addr_bytes[4];
+		client_clkid->id[7] = eth_hdr->s_addr.addr_bytes[5];
+
+		/* Enable Flag for Hardware Timestamping */
+		created_pkt->ol_flags |= PKT_TX_IEEE1588_TMST;
+
+		/* We read value from NIC to prevent latching with old value */
+		rte_eth_timesync_read_tx_timestamp(ptp_data->portid, &ptp_data->timestamp3);
+
+		/* the packet is being transmitted */
+		rte_eth_tx_burst(ptp_data->portid, 0, &created_pkt, 1);
+
+		wait_us = 0;
+		ptp_data->timestamp3.tv_nsec = 0;
+		ptp_data->timestamp3.tv_sec = 0;
+
+		/* we must wait at least 1us to read TX Timestamp */
+		while ((rte_eth_timesync_read_tx_timestamp(ptp_data->portid,
+				&ptp_data->timestamp3) < 0) && (wait_us < 1000)) {
+			rte_delay_us(1);
+			wait_us++;
+		}
+	} else {
+		return;
+	}
+}
+
+/*
+ * Parse DELAY_RESP message
+ */
+void
+parse_drsp(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	struct rte_mbuf *m = ptp_data->m;
+	struct ptp_message *ptp_msg;
+
+	ptp_msg = (struct ptp_message *) (rte_pktmbuf_mtod(m, char *) +
+									sizeof(struct ether_hdr));
+	ptp_data->timestamp4.tv_nsec =
+			ntohl(ptp_msg->delay_resp.receive_timestamp.nanoseconds);
+	ptp_data->timestamp4.tv_sec =
+		((uint64_t)ntohl
+				(ptp_msg->delay_resp.receive_timestamp.seconds_lsb)) |
+		(((uint64_t)ntohs
+				(ptp_msg->delay_resp.receive_timestamp.seconds_msb)) << 32);
+
+	/* Evaluate delta for adjustment */
+	ptp_data->delta = delta_eval(ptp_data);
+	rte_eth_timesync_adjust(ptp_data->portid, ptp_data->delta);
+	print_clock_info(ptp_data);
+}
+
+/* This function processes PTP packets, implementing
+ * slave PTP IEEE1588 L2 functionality
+ */
+void
+parse_ptp_frames(uint8_t portid, struct rte_mbuf *m) {
+	struct ptp_header *ptp_hdr;
+	struct ether_hdr *eth_hdr;
+	static struct ptpv2_data_slave_ordinary ptp_data;
+	uint16_t eth_type;
+
+	eth_hdr = rte_pktmbuf_mtod(m, struct ether_hdr *);
+	eth_type = rte_be_to_cpu_16(eth_hdr->ether_type);
+
+	if (eth_type == PTP_PROTOCOL) {
+
+		ptp_data.m = m;
+		ptp_data.portid = portid;
+		ptp_hdr = (struct ptp_header *)(rte_pktmbuf_mtod(m, char *)
+					+ sizeof(struct ether_hdr));
+
+		switch (ptp_hdr->msgtype) {
+		case SYNC:
+			parse_sync(&ptp_data);
+			break;
+		case FOLLOW_UP:
+			parse_fup(&ptp_data);
+			break;
+		case DELAY_RESP:
+			parse_drsp(&ptp_data);
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+/*
+ * The lcore main. This is the main thread that does the work, reading from
+ * an input port and writing to an output port.
+ */
+static __attribute__((noreturn)) void
+lcore_main(void)
+{
+	const uint8_t nb_ports = rte_eth_dev_count();
+	uint8_t portid;
+	unsigned nb_rx;
+	struct rte_mbuf *m;
+	unsigned ret;
+
+	/*
+	 * Check that the port is on the same NUMA node as the polling thread
+	 * for best performance.
+	 */
+	printf("\nCore %u Waiting for SYNC packets. [Ctrl+C to quit]\n",
+			rte_lcore_id());
+
+	/* Run until the application is quit or killed. */
+
+	while (1) {
+		/*
+		 * Read packet from RX queues
+		 */
+		for (portid = 0; portid < nb_ports; portid++) {
+
+			nb_rx = rte_eth_rx_burst(portid, 0, &m, 1);
+
+			if (likely(nb_rx == 0))
+					continue;
+
+			if ((m->ol_flags & PKT_RX_IEEE1588_PTP)) {
+				parse_ptp_frames(portid, m);
+			}
+
+			ret = 0;
+			do {
+				rte_pktmbuf_free(m);
+			} while (++ret < nb_rx);
+		}
+	}
+}
+
+/*
+ * The main function, which does initialization and calls the per-lcore
+ * functions.
+ */
+int
+main(int argc, char *argv[])
+{
+
+	unsigned nb_ports;
+	uint8_t portid;
+
+	/* Initialize the Environment Abstraction Layer (EAL). */
+	int ret = rte_eal_init(argc, argv);
+
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
+
+	argc -= ret;
+	argv += ret;
+
+	/* Check that there is an even number of ports to send/receive on. */
+	nb_ports = rte_eth_dev_count();
+
+	/* Creates a new mempool in memory to hold the mbufs. */
+	mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", NUM_MBUFS * nb_ports,
+		MBUF_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
+
+	if (mbuf_pool == NULL)
+		rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
+
+	/* Initialize all ports. */
+	for (portid = 0; portid < nb_ports; portid++)
+		if (port_init(portid, mbuf_pool) != 0)
+			rte_exit(EXIT_FAILURE, "Cannot init port %"PRIu8 "\n",
+					portid);
+
+	if (rte_lcore_count() > 1)
+		printf("\nWARNING: Too many lcores enabled. Only 1 used.\n");
+
+	/* Call lcore_main on the master core only. */
+	lcore_main();
+
+	return 0;
+}
-- 
2.1.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v2 0/6] add sample ptp slave application
  2015-10-02 15:20 [dpdk-dev] [PATCH 0/3] add sample ptp slave application Daniel Mrzyglod
                   ` (2 preceding siblings ...)
  2015-10-02 15:20 ` [dpdk-dev] [PATCH 3/3] example: PTP client slave minimal implementation Daniel Mrzyglod
@ 2015-10-30  9:43 ` Daniel Mrzyglod
  2015-10-30  9:43   ` [dpdk-dev] [PATCH v2 1/6] ethdev: add additional ieee1588 support functions Daniel Mrzyglod
                     ` (6 more replies)
  2015-11-03 16:38 ` [dpdk-dev] [PATCH v3 0/7] " Daniel Mrzyglod
                   ` (2 subsequent siblings)
  6 siblings, 7 replies; 84+ messages in thread
From: Daniel Mrzyglod @ 2015-10-30  9:43 UTC (permalink / raw)
  To: dev

Add a sample application that acts as a PTP slave using the DPDK IEEE1588
functions.

Also add some additional IEEE1588 support functions to enable getting,
setting and adjusting the device time.

V1->V2:
PMDs:
 - add support for e1000
 - add support for ixgbe
 - add support for i40
ethdev:
 - change function names to more proper.
Doc:
 - add documentation for ptpclient
sample:
 - add kernel adjustment option
 - add portmask option to provide portmask to aplication


Daniel Mrzyglod (4):
  ethdev: add additional ieee1588 support functions
  ixgbe: add additional ieee1588 support functions
  example: PTP client slave minimal implementation
  doc: add a PTPCLIENT sample guide

Pablo de Lara (2):
  igb: add additional ieee1588 support functions
  i40e: add additional ieee1588 support functions

 MAINTAINERS                                |   3 +
 doc/guides/rel_notes/release_2_2.rst       |   8 +
 doc/guides/sample_app_ug/img/ptpclient.svg | 520 +++++++++++++++++++
 doc/guides/sample_app_ug/index.rst         |   3 +
 doc/guides/sample_app_ug/ptpclient.rst     | 324 ++++++++++++
 drivers/net/e1000/e1000_ethdev.h           |  22 +
 drivers/net/e1000/igb_ethdev.c             | 338 ++++++++++++-
 drivers/net/i40e/i40e_ethdev.c             | 214 +++++++-
 drivers/net/i40e/i40e_ethdev.h             |  18 +
 drivers/net/ixgbe/ixgbe_ethdev.c           | 313 +++++++++++-
 drivers/net/ixgbe/ixgbe_ethdev.h           |  22 +
 examples/Makefile                          |   1 +
 examples/ptpclient/Makefile                |  57 +++
 examples/ptpclient/ptpclient.c             | 788 +++++++++++++++++++++++++++++
 lib/librte_ether/rte_ethdev.c              |  36 ++
 lib/librte_ether/rte_ethdev.h              |  64 +++
 lib/librte_ether/rte_ether_version.map     |   9 +
 17 files changed, 2699 insertions(+), 41 deletions(-)
 create mode 100644 doc/guides/sample_app_ug/img/ptpclient.svg
 create mode 100644 doc/guides/sample_app_ug/ptpclient.rst
 create mode 100644 examples/ptpclient/Makefile
 create mode 100644 examples/ptpclient/ptpclient.c

-- 
2.1.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v2 1/6] ethdev: add additional ieee1588 support functions
  2015-10-30  9:43 ` [dpdk-dev] [PATCH v2 0/6] add sample ptp slave application Daniel Mrzyglod
@ 2015-10-30  9:43   ` Daniel Mrzyglod
  2015-10-30  9:43   ` [dpdk-dev] [PATCH v2 2/6] ixgbe: " Daniel Mrzyglod
                     ` (5 subsequent siblings)
  6 siblings, 0 replies; 84+ messages in thread
From: Daniel Mrzyglod @ 2015-10-30  9:43 UTC (permalink / raw)
  To: dev

Add additional functions to support the existing IEEE1588
functionality.

* rte_eth_timesync_settime(), function to set the device clock time.
* rte_eth_timesync_gettime, function to get the device clock time.
* rte_eth_timesync_adjust, function to adjust the device clock time.

Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
---
 doc/guides/rel_notes/release_2_2.rst   |  3 ++
 lib/librte_ether/rte_ethdev.c          | 36 +++++++++++++++++++
 lib/librte_ether/rte_ethdev.h          | 64 ++++++++++++++++++++++++++++++++++
 lib/librte_ether/rte_ether_version.map |  9 +++++
 4 files changed, 112 insertions(+)

diff --git a/doc/guides/rel_notes/release_2_2.rst b/doc/guides/rel_notes/release_2_2.rst
index 89e4d58..b83ef7f 100644
--- a/doc/guides/rel_notes/release_2_2.rst
+++ b/doc/guides/rel_notes/release_2_2.rst
@@ -123,6 +123,9 @@ API Changes
 
 * The devargs union field virtual is renamed to virt for C++ compatibility.
 
+* Add new functions in ethdev to support IEEE1588: rte_eth_timesync_time_adjust()
+  rte_eth_timesync_time_get(), rte_eth_timesync_time_set()
+
 
 ABI Changes
 -----------
diff --git a/lib/librte_ether/rte_ethdev.c b/lib/librte_ether/rte_ethdev.c
index f593f6e..d7d2714 100644
--- a/lib/librte_ether/rte_ethdev.c
+++ b/lib/librte_ether/rte_ethdev.c
@@ -3284,6 +3284,42 @@ rte_eth_timesync_read_tx_timestamp(uint8_t port_id, struct timespec *timestamp)
 }
 
 int
+rte_eth_timesync_time_adjust(uint8_t port_id, int64_t delta)
+{
+	struct rte_eth_dev *dev;
+
+	VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	FUNC_PTR_OR_ERR_RET(*dev->dev_ops->timesync_time_adjust, -ENOTSUP);
+	return (*dev->dev_ops->timesync_time_adjust)(dev, delta);
+}
+
+int
+rte_eth_timesync_time_get(uint8_t port_id, struct timespec *timestamp)
+{
+	struct rte_eth_dev *dev;
+
+	VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	FUNC_PTR_OR_ERR_RET(*dev->dev_ops->timesync_time_get, -ENOTSUP);
+	return (*dev->dev_ops->timesync_time_get)(dev, timestamp);
+}
+
+int
+rte_eth_timesync_time_set(uint8_t port_id, struct timespec *timestamp)
+{
+	struct rte_eth_dev *dev;
+
+	VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	FUNC_PTR_OR_ERR_RET(*dev->dev_ops->timesync_time_set, -ENOTSUP);
+	return (*dev->dev_ops->timesync_time_set)(dev, timestamp);
+}
+
+int
 rte_eth_dev_get_reg_length(uint8_t port_id)
 {
 	struct rte_eth_dev *dev;
diff --git a/lib/librte_ether/rte_ethdev.h b/lib/librte_ether/rte_ethdev.h
index 8a8c82b..c639064 100644
--- a/lib/librte_ether/rte_ethdev.h
+++ b/lib/librte_ether/rte_ethdev.h
@@ -1129,6 +1129,17 @@ typedef int (*eth_timesync_read_tx_timestamp_t)(struct rte_eth_dev *dev,
 						struct timespec *timestamp);
 /**< @internal Function used to read a TX IEEE1588/802.1AS timestamp. */
 
+typedef int (*eth_timesync_time_adjust)(struct rte_eth_dev *dev, int64_t);
+/**< @internal Function used to adjust device clock */
+
+typedef int (*eth_timesync_time_get)(struct rte_eth_dev *dev,
+						struct timespec *timestamp);
+/**< @internal Function used to get time from device clock. */
+
+typedef int (*eth_timesync_time_set)(struct rte_eth_dev *dev,
+						struct timespec *timestamp);
+/**< @internal Function used to get time from device clock */
+
 typedef int (*eth_get_reg_length_t)(struct rte_eth_dev *dev);
 /**< @internal Retrieve device register count  */
 
@@ -1312,6 +1323,12 @@ struct eth_dev_ops {
 	eth_timesync_read_rx_timestamp_t timesync_read_rx_timestamp;
 	/** Read the IEEE1588/802.1AS TX timestamp. */
 	eth_timesync_read_tx_timestamp_t timesync_read_tx_timestamp;
+	/** Adjust the device clock */
+	eth_timesync_time_adjust timesync_time_adjust;
+	/** Get the device clock timespec */
+	eth_timesync_time_get timesync_time_get;
+	/** Set the device clock timespec */
+	eth_timesync_time_set timesync_time_set;
 };
 
 /**
@@ -3598,6 +3615,53 @@ extern int rte_eth_timesync_read_rx_timestamp(uint8_t port_id,
 extern int rte_eth_timesync_read_tx_timestamp(uint8_t port_id,
 					      struct timespec *timestamp);
 
+/**
+ * Adjust the timesync clock on an Ethernet device..
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param delta
+ *   The adjustment in nanoseconds
+ *
+ * @return
+ *   - 0: Success.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -ENOTSUP: The function is not supported by the Ethernet driver.
+ */
+extern int rte_eth_timesync_time_adjust(uint8_t port_id, int64_t delta);
+
+/**
+ * Read the time from the timesync clock on an Ethernet device.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param time
+ *   Pointer to the timespec struct.
+ *
+ * @return
+ *   - 0: Success.
+ */
+extern int rte_eth_timesync_time_get(uint8_t port_id,
+	      struct timespec *time);
+
+
+/**
+ * Set the time of the timesync clock on an Ethernet device.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param time
+ *   Pointer to the timespec struct.
+ *
+ * @return
+ *   - 0: Success.
+ *   - -EINVAL: No timestamp is available.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -ENOTSUP: The function is not supported by the Ethernet driver.
+ */
+extern int rte_eth_timesync_time_set(uint8_t port_id,
+	      struct timespec *time);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/librte_ether/rte_ether_version.map b/lib/librte_ether/rte_ether_version.map
index 8345a6c..78b46c1 100644
--- a/lib/librte_ether/rte_ether_version.map
+++ b/lib/librte_ether/rte_ether_version.map
@@ -127,3 +127,12 @@ DPDK_2.1 {
 	rte_eth_timesync_read_tx_timestamp;
 
 } DPDK_2.0;
+
+DPDK_2.2 {
+	global:
+
+	rte_eth_timesync_time_adjust;
+	rte_eth_timesync_time_get;
+	rte_eth_timesync_time_set;
+
+} DPDK_2.1;
-- 
2.1.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v2 2/6] ixgbe: add additional ieee1588 support functions
  2015-10-30  9:43 ` [dpdk-dev] [PATCH v2 0/6] add sample ptp slave application Daniel Mrzyglod
  2015-10-30  9:43   ` [dpdk-dev] [PATCH v2 1/6] ethdev: add additional ieee1588 support functions Daniel Mrzyglod
@ 2015-10-30  9:43   ` Daniel Mrzyglod
  2015-10-30  9:43   ` [dpdk-dev] [PATCH v2 3/6] igb: " Daniel Mrzyglod
                     ` (4 subsequent siblings)
  6 siblings, 0 replies; 84+ messages in thread
From: Daniel Mrzyglod @ 2015-10-30  9:43 UTC (permalink / raw)
  To: dev

Add additional functions to support the existing IEEE1588
functionality and to enable getting, setting and adjusting
the device time.

Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
---
 drivers/net/ixgbe/ixgbe_ethdev.c | 313 +++++++++++++++++++++++++++++++++++++--
 drivers/net/ixgbe/ixgbe_ethdev.h |  22 +++
 2 files changed, 324 insertions(+), 11 deletions(-)

diff --git a/drivers/net/ixgbe/ixgbe_ethdev.c b/drivers/net/ixgbe/ixgbe_ethdev.c
index 4373661..2c39501 100644
--- a/drivers/net/ixgbe/ixgbe_ethdev.c
+++ b/drivers/net/ixgbe/ixgbe_ethdev.c
@@ -126,10 +126,17 @@
 #define IXGBE_HKEY_MAX_INDEX 10
 
 /* Additional timesync values. */
-#define IXGBE_TIMINCA_16NS_SHIFT 24
-#define IXGBE_TIMINCA_INCVALUE   16000000
-#define IXGBE_TIMINCA_INIT       ((0x02 << IXGBE_TIMINCA_16NS_SHIFT) \
-				  | IXGBE_TIMINCA_INCVALUE)
+#define NSEC_PER_SEC             1000000000L
+#define IXGBE_INCVAL_10GB        0x66666666
+#define IXGBE_INCVAL_1GB         0x40000000
+#define IXGBE_INCVAL_100         0x50000000
+#define IXGBE_INCVAL_SHIFT_10GB  28
+#define IXGBE_INCVAL_SHIFT_1GB   24
+#define IXGBE_INCVAL_SHIFT_100   21
+#define IXGBE_INCVAL_SHIFT_82599 7
+#define IXGBE_INCPER_SHIFT_82599 24
+
+#define IXGBE_CYCLECOUNTER_MASK   0xffffffffffffffff
 
 static int eth_ixgbe_dev_init(struct rte_eth_dev *eth_dev);
 static int eth_ixgbe_dev_uninit(struct rte_eth_dev *eth_dev);
@@ -325,6 +332,11 @@ static int ixgbe_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 					    uint32_t flags);
 static int ixgbe_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 					    struct timespec *timestamp);
+static int ixgbe_timesync_time_adjust(struct rte_eth_dev *dev, int64_t delta);
+static int ixgbe_timesync_time_get(struct rte_eth_dev *dev,
+		struct timespec *timestamp);
+static int ixgbe_timesync_time_set(struct rte_eth_dev *dev,
+		struct timespec *timestamp);
 
 /*
  * Define VF Stats MACRO for Non "cleared on read" register
@@ -465,6 +477,9 @@ static const struct eth_dev_ops ixgbe_eth_dev_ops = {
 	.get_eeprom_length    = ixgbe_get_eeprom_length,
 	.get_eeprom           = ixgbe_get_eeprom,
 	.set_eeprom           = ixgbe_set_eeprom,
+	.timesync_time_adjust      = ixgbe_timesync_time_adjust,
+	.timesync_time_get     = ixgbe_timesync_time_get,
+	.timesync_time_set     = ixgbe_timesync_time_set,
 };
 
 /*
@@ -5267,20 +5282,273 @@ ixgbe_dev_set_mc_addr_list(struct rte_eth_dev *dev,
 					 ixgbe_dev_addr_list_itr, TRUE);
 }
 
+static inline uint64_t
+timespec_to_ns(const struct timespec *ts)
+{
+	return ((uint64_t) ts->tv_sec * NSEC_PER_SEC) + ts->tv_nsec;
+}
+
+static struct timespec
+ns_to_timespec(uint64_t nsec)
+{
+	struct timespec ts = {0, 0};
+
+	if (nsec == 0)
+		return ts;
+
+	ts.tv_sec = nsec / NSEC_PER_SEC;
+	ts.tv_nsec = nsec % NSEC_PER_SEC;
+
+	return ts;
+}
+
+/*
+ * Converts cycle counter cycles to nanoseconds.
+ */
+static inline uint64_t
+cyclecounter_cycles_to_ns(const struct cyclecounter *cc,
+		      uint64_t cycles, uint64_t mask, uint64_t *frac)
+{
+	uint64_t ns;
+
+	/* Add fractional nanoseconds */
+	ns = cycles + *frac;
+	*frac = ns & mask;
+
+	/* Shift to get only nanoseconds. */
+	return ns >> cc->shift;
+}
+
+/*
+ * Like cyclecounter_cycles_to_ns(), but this is used when
+ * computing a time previous to the stored in the cycle counter.
+ */
+static uint64_t
+cyclecounter_cycles_to_ns_backwards(const struct cyclecounter *cc,
+			       uint64_t cycles, uint64_t frac)
+{
+	return ((cycles - frac) >> cc->shift);
+}
+
+/*
+ * Register units might not be nanoseconds. This function converts
+ * these units into nanoseconds and adds to the previous time stored.
+ */
+static uint64_t
+timecounter_cycles_to_ns_time(struct timecounter *tc, uint64_t cycle_tstamp)
+{
+	uint64_t delta;
+	uint64_t nsec = tc->nsec, frac = tc->frac;
+
+	delta = (cycle_tstamp - tc->cycle_last) & tc->cc->mask;
+	/*
+	 * Cycle counts that are correctly converted as they
+	 * are between -1/2 max cycle count and +1/2 max cycle count.
+	 */
+	if (delta > (tc->cc->mask / 2)) {
+		delta = (tc->cycle_last - cycle_tstamp) & tc->cc->mask;
+		nsec -= cyclecounter_cycles_to_ns_backwards(tc->cc, delta, frac);
+	} else {
+		nsec += cyclecounter_cycles_to_ns(tc->cc, delta, tc->mask, &frac);
+	}
+
+	return nsec;
+}
+
+static uint64_t
+ixgbe_read_timesync_cyclecounter(struct rte_eth_dev *dev)
+{
+	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	uint64_t systime_cycles = 0;
+
+	switch (hw->mac.type) {
+	case ixgbe_mac_X550:
+		/* SYSTIMEL stores ns and SYSTIMEH stores seconds. */
+		systime_cycles = (uint64_t)IXGBE_READ_REG(hw, IXGBE_SYSTIML);
+		systime_cycles += (uint64_t)IXGBE_READ_REG(hw, IXGBE_SYSTIMH)
+				* NSEC_PER_SEC;
+		break;
+	default:
+		systime_cycles |= (uint64_t)IXGBE_READ_REG(hw, IXGBE_SYSTIML);
+		systime_cycles |= (uint64_t)IXGBE_READ_REG(hw, IXGBE_SYSTIMH)
+				<< 32;
+	}
+
+	return systime_cycles;
+}
+
+/*
+ * Get nanoseconds since the last call of this function.
+ */
+static uint64_t
+timecounter_read_ns_delta(struct rte_eth_dev *dev)
+{
+	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	uint64_t cycle_now, cycle_delta;
+	uint64_t ns_offset;
+	struct ixgbe_adapter *adapter =
+				(struct ixgbe_adapter *)dev->data->dev_private;
+
+	/* Read cycle counter. */
+	cycle_now = adapter->tc.cc->read(dev);
+
+	/* Calculate the delta since the last timecounter_read_delta(). */
+	cycle_delta = (cycle_now - adapter->tc.cycle_last) & adapter->tc.cc->mask;
+
+	/* Convert to nanoseconds. */
+	if (hw->mac.type == ixgbe_mac_X550)
+		/* Registers store directly nanoseconds, no need to convert. */
+		ns_offset = cycle_delta;
+	else
+		ns_offset = cyclecounter_cycles_to_ns(adapter->tc.cc, cycle_delta,
+					adapter->tc.mask, &adapter->tc.frac);
+
+	/* Store current cycle counter for next timecounter_read_ns_delta() call. */
+	adapter->tc.cycle_last = cycle_now;
+
+	return ns_offset;
+}
+
+static uint64_t
+timecounter_read(struct rte_eth_dev *dev)
+{
+	uint64_t nsec;
+	struct ixgbe_adapter *adapter =
+			(struct ixgbe_adapter *)dev->data->dev_private;
+
+	/* Increment time by nanoseconds since last call. */
+	nsec = timecounter_read_ns_delta(dev);
+	nsec += adapter->tc.nsec;
+	adapter->tc.nsec = nsec;
+
+	return nsec;
+}
+
+
+static void
+timecounter_init(struct rte_eth_dev *dev,
+		      uint64_t start_time)
+{
+	struct ixgbe_adapter *adapter =
+				(struct ixgbe_adapter *)dev->data->dev_private;
+	adapter->tc.cc = &adapter->cc;
+	adapter->tc.cycle_last = adapter->tc.cc->read(dev);
+	adapter->tc.nsec = start_time;
+	adapter->tc.mask = (1ULL << adapter->tc.cc->shift) - 1;
+	adapter->tc.frac = 0;
+}
+
+static void
+ixgbe_start_cyclecounter(struct rte_eth_dev *dev)
+{
+	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct ixgbe_adapter *adapter =
+		(struct ixgbe_adapter *)dev->data->dev_private;
+	struct rte_eth_link link;
+	uint32_t incval = 0;
+	uint32_t shift = 0;
+
+	/* Get current link speed. */
+	memset(&link, 0, sizeof(link));
+	ixgbe_dev_link_update(dev, 1);
+	rte_ixgbe_dev_atomic_read_link_status(dev, &link);
+
+	switch (link.link_speed) {
+	case ETH_LINK_SPEED_100:
+		incval = IXGBE_INCVAL_100;
+		shift = IXGBE_INCVAL_SHIFT_100;
+		break;
+	case ETH_LINK_SPEED_1000:
+		incval = IXGBE_INCVAL_1GB;
+		shift = IXGBE_INCVAL_SHIFT_1GB;
+		break;
+	case ETH_LINK_SPEED_10000:
+	default:
+		incval = IXGBE_INCVAL_10GB;
+		shift = IXGBE_INCVAL_SHIFT_10GB;
+		break;
+	}
+
+	switch (hw->mac.type) {
+	case ixgbe_mac_X550:
+		/* Independent of link speed. */
+		incval = 1;
+		/* Cycles read will be interpreted as ns. */
+		shift = 0;
+		/* Fall-through */
+	case ixgbe_mac_X540:
+		IXGBE_WRITE_REG(hw, IXGBE_TIMINCA, incval);
+		break;
+	case ixgbe_mac_82599EB:
+		incval >>= IXGBE_INCVAL_SHIFT_82599;
+		shift -= IXGBE_INCVAL_SHIFT_82599;
+		IXGBE_WRITE_REG(hw, IXGBE_TIMINCA,
+			(1 << IXGBE_INCPER_SHIFT_82599) | incval);
+		break;
+	default:
+		/* Not supported. */
+		return;
+	}
+
+	memset(&adapter->cc, 0, sizeof(struct cyclecounter));
+	adapter->cc.read = ixgbe_read_timesync_cyclecounter;
+	adapter->cc.mask = IXGBE_CYCLECOUNTER_MASK;
+	adapter->cc.shift = shift;
+}
+
+static int
+ixgbe_timesync_time_adjust(struct rte_eth_dev *dev, int64_t delta)
+{
+	struct ixgbe_adapter *adapter =
+			(struct ixgbe_adapter *)dev->data->dev_private;
+
+	adapter->tc.nsec += delta;
+
+	return 0;
+}
+
+static int
+ixgbe_timesync_time_set(struct rte_eth_dev *dev, struct timespec *ts)
+{
+	uint64_t ns;
+
+	ns = timespec_to_ns(ts);
+	/* Reset the timecounter. */
+	timecounter_init(dev, ns);
+
+	return 0;
+}
+
+static int
+ixgbe_timesync_time_get(struct rte_eth_dev *dev, struct timespec *ts)
+{
+	uint64_t ns;
+
+	ns = timecounter_read(dev);
+	*ts = ns_to_timespec(ns);
+
+	return 0;
+}
+
 static int
 ixgbe_timesync_enable(struct rte_eth_dev *dev)
 {
 	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
 	uint32_t tsync_ctl;
 	uint32_t tsauxc;
+	uint64_t ns;
+	struct timespec zerotime = {0, 0};
 
 	/* Enable system time for platforms where it isn't on by default. */
 	tsauxc = IXGBE_READ_REG(hw, IXGBE_TSAUXC);
 	tsauxc &= ~IXGBE_TSAUXC_DISABLE_SYSTIME;
-	IXGBE_WRITE_REG(hw, IXGBE_TSAUXC, tsauxc);
 
-	/* Start incrementing the register used to timestamp PTP packets. */
-	IXGBE_WRITE_REG(hw, IXGBE_TIMINCA, IXGBE_TIMINCA_INIT);
+	/* Set 0.0 epoch time to initialize timecounter. */
+	ns = timespec_to_ns(&zerotime);
+	ixgbe_start_cyclecounter(dev);
+	timecounter_init(dev, ns);
+
+	IXGBE_WRITE_REG(hw, IXGBE_TSAUXC, tsauxc);
 
 	/* Enable L2 filtering of IEEE1588/802.1AS Ethernet frame types. */
 	IXGBE_WRITE_REG(hw, IXGBE_ETQF(IXGBE_ETQF_FILTER_1588),
@@ -5298,6 +5566,9 @@ ixgbe_timesync_enable(struct rte_eth_dev *dev)
 	tsync_ctl |= IXGBE_TSYNCTXCTL_ENABLED;
 	IXGBE_WRITE_REG(hw, IXGBE_TSYNCTXCTL, tsync_ctl);
 
+	/* After writing to registers should be flush. */
+	IXGBE_WRITE_FLUSH(hw);
+
 	return 0;
 }
 
@@ -5332,9 +5603,13 @@ ixgbe_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 				 uint32_t flags __rte_unused)
 {
 	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct ixgbe_adapter *adapter =
+		(struct ixgbe_adapter *)dev->data->dev_private;
+
 	uint32_t tsync_rxctl;
 	uint32_t rx_stmpl;
 	uint32_t rx_stmph;
+	uint64_t regival = 0;
 
 	tsync_rxctl = IXGBE_READ_REG(hw, IXGBE_TSYNCRXCTL);
 	if ((tsync_rxctl & IXGBE_TSYNCRXCTL_VALID) == 0)
@@ -5342,9 +5617,15 @@ ixgbe_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 
 	rx_stmpl = IXGBE_READ_REG(hw, IXGBE_RXSTMPL);
 	rx_stmph = IXGBE_READ_REG(hw, IXGBE_RXSTMPH);
+	timecounter_read(dev);
 
-	timestamp->tv_sec = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
-	timestamp->tv_nsec = 0;
+	if (hw->mac.type == ixgbe_mac_X550)
+		regival = (uint64_t)((uint64_t) rx_stmph * NSEC_PER_SEC
+			+ rx_stmpl);
+	else
+		regival = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
+	regival = timecounter_cycles_to_ns_time(&adapter->tc, regival);
+	*timestamp = ns_to_timespec(regival);
 
 	return  0;
 }
@@ -5354,9 +5635,13 @@ ixgbe_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 				 struct timespec *timestamp)
 {
 	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct ixgbe_adapter *adapter =
+		(struct ixgbe_adapter *)dev->data->dev_private;
+
 	uint32_t tsync_txctl;
 	uint32_t tx_stmpl;
 	uint32_t tx_stmph;
+	uint64_t regival = 0;
 
 	tsync_txctl = IXGBE_READ_REG(hw, IXGBE_TSYNCTXCTL);
 	if ((tsync_txctl & IXGBE_TSYNCTXCTL_VALID) == 0)
@@ -5364,9 +5649,15 @@ ixgbe_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 
 	tx_stmpl = IXGBE_READ_REG(hw, IXGBE_TXSTMPL);
 	tx_stmph = IXGBE_READ_REG(hw, IXGBE_TXSTMPH);
+	timecounter_read(dev);
 
-	timestamp->tv_sec = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
-	timestamp->tv_nsec = 0;
+	if (hw->mac.type == ixgbe_mac_X550)
+		regival = (uint64_t)((uint64_t) tx_stmph * NSEC_PER_SEC
+			+ tx_stmpl);
+	else
+		regival = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
+	regival = timecounter_cycles_to_ns_time(&adapter->tc, regival);
+	*timestamp = ns_to_timespec(regival);
 
 	return  0;
 }
diff --git a/drivers/net/ixgbe/ixgbe_ethdev.h b/drivers/net/ixgbe/ixgbe_ethdev.h
index f75c6dd..2ace8c0 100644
--- a/drivers/net/ixgbe/ixgbe_ethdev.h
+++ b/drivers/net/ixgbe/ixgbe_ethdev.h
@@ -255,6 +255,26 @@ struct ixgbe_filter_info {
 };
 
 /*
+ * Structure for cyclecounter IEEE1588 functionality.
+ */
+struct cyclecounter {
+	uint64_t (*read)(struct rte_eth_dev *dev);
+	uint64_t mask;
+	uint32_t shift;
+};
+
+/*
+ * Structure to hold and calculate Unix epoch time.
+ */
+struct timecounter {
+	struct cyclecounter *cc;
+	uint64_t cycle_last;
+	uint64_t nsec;
+	uint64_t mask;
+	uint64_t frac;
+};
+
+/*
  * Structure to store private data for each driver instance (for each port).
  */
 struct ixgbe_adapter {
@@ -276,6 +296,8 @@ struct ixgbe_adapter {
 
 	bool rx_bulk_alloc_allowed;
 	bool rx_vec_allowed;
+	struct cyclecounter cc;
+	struct timecounter tc;
 };
 
 #define IXGBE_DEV_PRIVATE_TO_HW(adapter)\
-- 
2.1.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v2 3/6] igb: add additional ieee1588 support functions
  2015-10-30  9:43 ` [dpdk-dev] [PATCH v2 0/6] add sample ptp slave application Daniel Mrzyglod
  2015-10-30  9:43   ` [dpdk-dev] [PATCH v2 1/6] ethdev: add additional ieee1588 support functions Daniel Mrzyglod
  2015-10-30  9:43   ` [dpdk-dev] [PATCH v2 2/6] ixgbe: " Daniel Mrzyglod
@ 2015-10-30  9:43   ` Daniel Mrzyglod
  2015-10-30  9:43   ` [dpdk-dev] [PATCH v2 4/6] i40e: " Daniel Mrzyglod
                     ` (3 subsequent siblings)
  6 siblings, 0 replies; 84+ messages in thread
From: Daniel Mrzyglod @ 2015-10-30  9:43 UTC (permalink / raw)
  To: dev

From: Pablo de Lara <pablo.de.lara.guarch@intel.com>

Add additional functions to support the existing IEEE1588
functionality and to enable getting, setting and adjusting
the device time.

Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
---
 drivers/net/e1000/e1000_ethdev.h |  22 +++
 drivers/net/e1000/igb_ethdev.c   | 338 +++++++++++++++++++++++++++++++++++++--
 2 files changed, 350 insertions(+), 10 deletions(-)

diff --git a/drivers/net/e1000/e1000_ethdev.h b/drivers/net/e1000/e1000_ethdev.h
index 4e69e44..c8772b7 100644
--- a/drivers/net/e1000/e1000_ethdev.h
+++ b/drivers/net/e1000/e1000_ethdev.h
@@ -220,6 +220,26 @@ struct e1000_filter_info {
 };
 
 /*
+ * Structure for cyclecounter IEEE1588 functionality.
+ */
+struct cyclecounter {
+	uint64_t (*read)(struct rte_eth_dev *dev);
+	uint64_t mask;
+	uint32_t shift;
+};
+
+/*
+ * Structure to hold and calculate Unix epoch time.
+ */
+struct timecounter {
+	struct cyclecounter *cc;
+	uint64_t cycle_last;
+	uint64_t nsec;
+	uint64_t mask;
+	uint64_t frac;
+};
+
+/*
  * Structure to store private data for each driver instance (for each port).
  */
 struct e1000_adapter {
@@ -230,6 +250,8 @@ struct e1000_adapter {
 	struct e1000_vf_info    *vfdata;
 	struct e1000_filter_info filter;
 	bool stopped;
+	struct cyclecounter cc;
+	struct timecounter tc;
 };
 
 #define E1000_DEV_PRIVATE(adapter) \
diff --git a/drivers/net/e1000/igb_ethdev.c b/drivers/net/e1000/igb_ethdev.c
index 3ab082e..fea11f8 100644
--- a/drivers/net/e1000/igb_ethdev.c
+++ b/drivers/net/e1000/igb_ethdev.c
@@ -78,10 +78,12 @@
 #define IGB_8_BIT_MASK   UINT8_MAX
 
 /* Additional timesync values. */
-#define E1000_ETQF_FILTER_1588 3
-#define E1000_TIMINCA_INCVALUE 16000000
-#define E1000_TIMINCA_INIT     ((0x02 << E1000_TIMINCA_16NS_SHIFT) \
-				| E1000_TIMINCA_INCVALUE)
+#define NSEC_PER_SEC                 1000000000L
+#define E1000_CYCLECOUNTER_MASK      0xffffffffffffffff
+#define E1000_ETQF_FILTER_1588       3
+#define IGB_82576_TSYNC_SHIFT        16
+#define E1000_INCPERIOD_82576        (1 << E1000_TIMINCA_16NS_SHIFT)
+#define E1000_INCVALUE_82576         (16 << IGB_82576_TSYNC_SHIFT)
 #define E1000_TSAUXC_DISABLE_SYSTIME 0x80000000
 
 static int  eth_igb_configure(struct rte_eth_dev *dev);
@@ -230,6 +232,11 @@ static int igb_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 					  uint32_t flags);
 static int igb_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 					  struct timespec *timestamp);
+static int igb_timesync_time_adjust(struct rte_eth_dev *dev, int64_t delta);
+static int igb_timesync_time_get(struct rte_eth_dev *dev,
+		struct timespec *timestamp);
+static int igb_timesync_time_set(struct rte_eth_dev *dev,
+		struct timespec *timestamp);
 static int eth_igb_rx_queue_intr_enable(struct rte_eth_dev *dev,
 					uint16_t queue_id);
 static int eth_igb_rx_queue_intr_disable(struct rte_eth_dev *dev,
@@ -327,6 +334,9 @@ static const struct eth_dev_ops eth_igb_ops = {
 	.get_eeprom_length    = eth_igb_get_eeprom_length,
 	.get_eeprom           = eth_igb_get_eeprom,
 	.set_eeprom           = eth_igb_set_eeprom,
+	.timesync_time_adjust  = igb_timesync_time_adjust,
+	.timesync_time_get     = igb_timesync_time_get,
+	.timesync_time_set     = igb_timesync_time_set,
 };
 
 /*
@@ -3883,20 +3893,286 @@ eth_igb_set_mc_addr_list(struct rte_eth_dev *dev,
 	return 0;
 }
 
+static inline uint64_t
+timespec_to_ns(const struct timespec *ts)
+{
+	return ((uint64_t) ts->tv_sec * NSEC_PER_SEC) + ts->tv_nsec;
+}
+
+static struct timespec
+ns_to_timespec(uint64_t nsec)
+{
+	struct timespec ts = {0, 0};
+
+	if (nsec == 0)
+		return ts;
+
+	ts.tv_sec = nsec / NSEC_PER_SEC;
+	ts.tv_nsec = nsec % NSEC_PER_SEC;
+
+	return ts;
+}
+
+/*
+ * Converts cycle counter cycles to nanoseconds.
+ */
+static inline uint64_t
+cyclecounter_cycles_to_ns(const struct cyclecounter *cc, uint64_t cycles,
+			uint64_t mask, uint64_t *frac)
+{
+	uint64_t ns;
+
+	/* Add fractional nanoseconds. */
+	ns = cycles + *frac;
+	*frac = ns & mask;
+
+	/* Shift to get only nanoseconds. */
+	return (ns >> cc->shift);
+}
+
+/*
+ * Like cyclecounter_cycles_to_ns(), but this is used when
+ * computing a time previous to the stored in the cycle counter.
+ */
+static uint64_t
+cyclecounter_cycles_to_ns_backwards(const struct cyclecounter *cc,
+			uint64_t cycles, uint64_t frac)
+{
+	return ((cycles - frac) >> cc->shift);
+}
+
+/*
+ * Register units might not be nanoseconds. This function converts
+ * these units into nanoseconds and adds to the previous time stored.
+ */
+static uint64_t
+timecounter_cycles_to_ns_time(struct timecounter *tc, uint64_t cycle_tstamp)
+{
+	uint64_t delta;
+	uint64_t nsec = tc->nsec, frac = tc->frac;
+
+	delta = (cycle_tstamp - tc->cycle_last) & tc->cc->mask;
+	/*
+	 * Cycle counts that are correctly converted as they
+	 * are between -1/2 max cycle count and +1/2 max cycle count.
+	 */
+	if (delta > (tc->cc->mask / 2)) {
+		delta = (tc->cycle_last - cycle_tstamp) & tc->cc->mask;
+		nsec -= cyclecounter_cycles_to_ns_backwards(tc->cc, delta, frac);
+	} else {
+		nsec += cyclecounter_cycles_to_ns(tc->cc, delta, tc->mask, &frac);
+	}
+
+	return nsec;
+}
+
+static uint64_t
+igb_read_timesync_cyclecounter(struct rte_eth_dev *dev)
+{
+	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	uint64_t systime_cycles = 0;
+
+	switch (hw->mac.type) {
+	case e1000_i210:
+	case e1000_i211:
+		/*
+		 * Need to read System Time Residue Register to be able
+		 * to read the other two registers.
+		 */
+		E1000_READ_REG(hw, E1000_SYSTIMR);
+		/* SYSTIMEL stores ns and SYSTIMEH stores seconds. */
+		systime_cycles = (uint64_t)E1000_READ_REG(hw, E1000_SYSTIML);
+		systime_cycles += (uint64_t)E1000_READ_REG(hw, E1000_SYSTIMH)
+				* NSEC_PER_SEC;
+		break;
+	case e1000_82580:
+	case e1000_i350:
+	case e1000_i354:
+		/*
+		 * Need to read System Time Residue Register to be able
+		 * to read the other two registers.
+		 */
+		E1000_READ_REG(hw, E1000_SYSTIMR);
+		systime_cycles |= (uint64_t)E1000_READ_REG(hw, E1000_SYSTIML);
+		/* Only the 8 LSB are valid. */
+		systime_cycles |= (uint64_t)(E1000_READ_REG(hw, E1000_SYSTIMH)
+				& 0xff) << 32;
+		break;
+	default:
+		systime_cycles |= (uint64_t)E1000_READ_REG(hw, E1000_SYSTIML);
+		systime_cycles |= (uint64_t)E1000_READ_REG(hw, E1000_SYSTIMH)
+				<< 32;
+		break;
+	}
+
+	return systime_cycles;
+}
+
+/*
+ * Get nanoseconds since the last call of this function.
+ */
+static uint64_t
+timecounter_read_ns_delta(struct rte_eth_dev *dev)
+{
+	uint64_t cycle_now, cycle_delta;
+	uint64_t ns_offset;
+	struct e1000_adapter *adapter =
+				(struct e1000_adapter *)dev->data->dev_private;
+	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+
+	/* Read cycle counter. */
+	cycle_now = adapter->tc.cc->read(dev);
+
+	/* Calculate the delta since the last timecounter_read_delta(). */
+	cycle_delta = (cycle_now - adapter->tc.cycle_last) & adapter->tc.cc->mask;
+
+	/* Convert to nanoseconds. */
+	switch (hw->mac.type) {
+	case e1000_i210:
+	case e1000_i211:
+	case e1000_82580:
+	case e1000_i350:
+	case e1000_i354:
+		/* Registers store directly nanoseconds, no need to convert. */
+		ns_offset = cycle_delta;
+		break;
+	default:
+		ns_offset = cyclecounter_cycles_to_ns(adapter->tc.cc, cycle_delta,
+					adapter->tc.mask, &adapter->tc.frac);
+	}
+
+	/* Store current cycle counter for next timecounter_read_ns_delta() call. */
+	adapter->tc.cycle_last = cycle_now;
+
+	return ns_offset;
+}
+
+static uint64_t
+timecounter_read(struct rte_eth_dev *dev)
+{
+	uint64_t nsec;
+	struct e1000_adapter *adapter =
+			(struct e1000_adapter *)dev->data->dev_private;
+
+	/* increment time by nanoseconds since last call */
+	nsec = timecounter_read_ns_delta(dev);
+	nsec += adapter->tc.nsec;
+	adapter->tc.nsec = nsec;
+
+	return nsec;
+}
+
+
+static void
+timecounter_init(struct rte_eth_dev *dev,
+		      uint64_t start_time)
+{
+	struct e1000_adapter *adapter =
+				(struct e1000_adapter *)dev->data->dev_private;
+	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+
+	adapter->tc.cc = &adapter->cc;
+	adapter->tc.cycle_last = adapter->tc.cc->read(dev);
+	adapter->tc.nsec = start_time;
+	switch (hw->mac.type) {
+	case e1000_82580:
+	case e1000_i350:
+	case e1000_i354:
+		/* 32 LSB bits + 8 MSB bits = 40 bits */
+		adapter->tc.mask = (1ULL << 40) - 1;
+	default:
+		adapter->tc.mask = (1ULL << adapter->tc.cc->shift) - 1;
+	}
+	adapter->tc.frac = 0;
+}
+
+static void
+igb_start_cyclecounter(struct rte_eth_dev *dev)
+{
+	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct e1000_adapter *adapter =
+		(struct e1000_adapter *)dev->data->dev_private;
+	uint32_t incval = 1;
+	uint32_t shift = 0;
+
+	switch (hw->mac.type) {
+	case e1000_i210:
+	case e1000_i211:
+	case e1000_82580:
+	case e1000_i350:
+	case e1000_i354:
+		/* Start incrementing the register used to timestamp PTP packets. */
+		E1000_WRITE_REG(hw, E1000_TIMINCA, incval);
+		break;
+	case e1000_82576:
+		incval = E1000_INCVALUE_82576;
+		shift = IGB_82576_TSYNC_SHIFT;
+		E1000_WRITE_REG(hw, E1000_TIMINCA, E1000_INCPERIOD_82576 | incval);
+		break;
+	default:
+		/* Not supported */
+		return;
+	}
+
+	memset(&adapter->cc, 0, sizeof(struct cyclecounter));
+	adapter->cc.read = igb_read_timesync_cyclecounter;
+	adapter->cc.mask = E1000_CYCLECOUNTER_MASK;
+	adapter->cc.shift = shift;
+}
+
+static int
+igb_timesync_time_adjust(struct rte_eth_dev *dev, int64_t delta)
+{
+	struct e1000_adapter *adapter =
+			(struct e1000_adapter *)dev->data->dev_private;
+
+	adapter->tc.nsec += delta;
+
+	return 0;
+}
+
+static int
+igb_timesync_time_set(struct rte_eth_dev *dev, struct timespec *ts)
+{
+	uint64_t ns;
+
+	ns = timespec_to_ns(ts);
+
+	/* Reset the timecounter. */
+	timecounter_init(dev, ns);
+
+	return 0;
+}
+
+static int
+igb_timesync_time_get(struct rte_eth_dev *dev, struct timespec *ts)
+{
+	uint64_t ns;
+
+	ns = timecounter_read(dev);
+	*ts = ns_to_timespec(ns);
+
+	return 0;
+}
+
 static int
 igb_timesync_enable(struct rte_eth_dev *dev)
 {
 	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
 	uint32_t tsync_ctl;
 	uint32_t tsauxc;
+	uint64_t ns;
+	struct timespec zerotime = {0, 0};
 
 	/* Enable system time for it isn't on by default. */
 	tsauxc = E1000_READ_REG(hw, E1000_TSAUXC);
 	tsauxc &= ~E1000_TSAUXC_DISABLE_SYSTIME;
 	E1000_WRITE_REG(hw, E1000_TSAUXC, tsauxc);
 
-	/* Start incrementing the register used to timestamp PTP packets. */
-	E1000_WRITE_REG(hw, E1000_TIMINCA, E1000_TIMINCA_INIT);
+	/* Set 0.0 epoch time to initialize timecounter. */
+	ns = timespec_to_ns(&zerotime);
+	igb_start_cyclecounter(dev);
+	timecounter_init(dev, ns);
 
 	/* Enable L2 filtering of IEEE1588/802.1AS Ethernet frame types. */
 	E1000_WRITE_REG(hw, E1000_ETQF(E1000_ETQF_FILTER_1588),
@@ -3948,9 +4224,13 @@ igb_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 			       uint32_t flags __rte_unused)
 {
 	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct e1000_adapter *adapter =
+			(struct e1000_adapter *)dev->data->dev_private;
+
 	uint32_t tsync_rxctl;
 	uint32_t rx_stmpl;
 	uint32_t rx_stmph;
+	uint64_t regival = 0;
 
 	tsync_rxctl = E1000_READ_REG(hw, E1000_TSYNCRXCTL);
 	if ((tsync_rxctl & E1000_TSYNCRXCTL_VALID) == 0)
@@ -3958,9 +4238,26 @@ igb_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 
 	rx_stmpl = E1000_READ_REG(hw, E1000_RXSTMPL);
 	rx_stmph = E1000_READ_REG(hw, E1000_RXSTMPH);
+	timecounter_read(dev);
+
+	switch (hw->mac.type) {
+	case e1000_82580:
+	case e1000_i350:
+	case e1000_i354:
+		regival = (uint64_t)((((uint64_t)(rx_stmph & 0xff)) << 32)
+			| rx_stmpl);
+		break;
+	case e1000_i210:
+	case e1000_i211:
+		regival = (uint64_t)((uint64_t)rx_stmph * NSEC_PER_SEC
+			+ rx_stmpl);
+		break;
+	default:
+		regival = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
+	}
 
-	timestamp->tv_sec = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
-	timestamp->tv_nsec = 0;
+	regival = timecounter_cycles_to_ns_time(&adapter->tc, regival);
+	*timestamp = ns_to_timespec(regival);
 
 	return  0;
 }
@@ -3970,6 +4267,10 @@ igb_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 			       struct timespec *timestamp)
 {
 	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct e1000_adapter *adapter =
+			(struct e1000_adapter *)dev->data->dev_private;
+	uint64_t regival = 0;
+
 	uint32_t tsync_txctl;
 	uint32_t tx_stmpl;
 	uint32_t tx_stmph;
@@ -3980,9 +4281,26 @@ igb_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 
 	tx_stmpl = E1000_READ_REG(hw, E1000_TXSTMPL);
 	tx_stmph = E1000_READ_REG(hw, E1000_TXSTMPH);
+	timecounter_read(dev);
+
+	switch (hw->mac.type) {
+	case e1000_82580:
+	case e1000_i350:
+	case e1000_i354:
+		regival = (uint64_t)((((uint64_t)(tx_stmph & 0xff)) << 32)
+			| tx_stmpl);
+		break;
+	case e1000_i210:
+	case e1000_i211:
+		regival = (uint64_t)((uint64_t)tx_stmph * NSEC_PER_SEC
+			+ tx_stmpl);
+		break;
+	default:
+		regival = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
+	}
 
-	timestamp->tv_sec = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
-	timestamp->tv_nsec = 0;
+	regival = timecounter_cycles_to_ns_time(&adapter->tc, regival);
+	*timestamp = ns_to_timespec(regival);
 
 	return  0;
 }
-- 
2.1.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v2 4/6] i40e: add additional ieee1588 support functions
  2015-10-30  9:43 ` [dpdk-dev] [PATCH v2 0/6] add sample ptp slave application Daniel Mrzyglod
                     ` (2 preceding siblings ...)
  2015-10-30  9:43   ` [dpdk-dev] [PATCH v2 3/6] igb: " Daniel Mrzyglod
@ 2015-10-30  9:43   ` Daniel Mrzyglod
  2015-10-30 11:19     ` Ananyev, Konstantin
  2015-10-30  9:43   ` [dpdk-dev] [PATCH v2 5/6] example: PTP client slave minimal implementation Daniel Mrzyglod
                     ` (2 subsequent siblings)
  6 siblings, 1 reply; 84+ messages in thread
From: Daniel Mrzyglod @ 2015-10-30  9:43 UTC (permalink / raw)
  To: dev

From: Pablo de Lara <pablo.de.lara.guarch@intel.com>

Add additional functions to support the existing IEEE1588
functionality and to enable getting, setting and adjusting
the device time.

Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
---
 drivers/net/i40e/i40e_ethdev.c | 214 +++++++++++++++++++++++++++++++++++++----
 drivers/net/i40e/i40e_ethdev.h |  18 ++++
 2 files changed, 212 insertions(+), 20 deletions(-)

diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 2dd9fdc..985ab24 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -107,11 +107,14 @@
 	(1UL << RTE_ETH_FLOW_NONFRAG_IPV6_OTHER) | \
 	(1UL << RTE_ETH_FLOW_L2_PAYLOAD))
 
-#define I40E_PTP_40GB_INCVAL  0x0199999999ULL
-#define I40E_PTP_10GB_INCVAL  0x0333333333ULL
-#define I40E_PTP_1GB_INCVAL   0x2000000000ULL
-#define I40E_PRTTSYN_TSYNENA  0x80000000
-#define I40E_PRTTSYN_TSYNTYPE 0x0e000000
+/* Additional timesync values. */
+#define NSEC_PER_SEC             1000000000L
+#define I40E_PTP_40GB_INCVAL     0x0199999999ULL
+#define I40E_PTP_10GB_INCVAL     0x0333333333ULL
+#define I40E_PTP_1GB_INCVAL      0x2000000000ULL
+#define I40E_PRTTSYN_TSYNENA     0x80000000
+#define I40E_PRTTSYN_TSYNTYPE    0x0e000000
+#define I40E_CYCLECOUNTER_MASK   0xffffffffffffffff
 
 static int eth_i40e_dev_init(struct rte_eth_dev *eth_dev);
 static int eth_i40e_dev_uninit(struct rte_eth_dev *eth_dev);
@@ -232,6 +235,11 @@ static int i40e_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 					   uint32_t flags);
 static int i40e_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 					   struct timespec *timestamp);
+static int i40e_timesync_time_adjust(struct rte_eth_dev *dev, int64_t delta);
+static int i40e_timesync_time_get(struct rte_eth_dev *dev,
+		struct timespec *timestamp);
+static int i40e_timesync_time_set(struct rte_eth_dev *dev,
+		struct timespec *timestamp);
 
 static const struct rte_pci_id pci_id_i40e_map[] = {
 #define RTE_PCI_DEV_ID_DECL_I40E(vend, dev) {RTE_PCI_DEVICE(vend, dev)},
@@ -289,6 +297,9 @@ static const struct eth_dev_ops i40e_eth_dev_ops = {
 	.timesync_disable             = i40e_timesync_disable,
 	.timesync_read_rx_timestamp   = i40e_timesync_read_rx_timestamp,
 	.timesync_read_tx_timestamp   = i40e_timesync_read_tx_timestamp,
+	.timesync_time_adjust         = i40e_timesync_time_adjust,
+	.timesync_time_get            = i40e_timesync_time_get,
+	.timesync_time_set            = i40e_timesync_time_set,
 };
 
 static struct eth_driver rte_i40e_pmd = {
@@ -6175,17 +6186,116 @@ i40e_mirror_rule_reset(struct rte_eth_dev *dev, uint8_t sw_id)
 	return 0;
 }
 
-static int
-i40e_timesync_enable(struct rte_eth_dev *dev)
+static inline uint64_t
+timespec_to_ns(const struct timespec *ts)
+{
+	return ((uint64_t) ts->tv_sec * NSEC_PER_SEC) + ts->tv_nsec;
+}
+
+static struct timespec
+ns_to_timespec(uint64_t nsec)
+{
+	struct timespec ts = {0, 0};
+
+	if (nsec == 0)
+		return ts;
+
+	ts.tv_sec = nsec / NSEC_PER_SEC;
+	ts.tv_nsec = nsec % NSEC_PER_SEC;
+
+	return ts;
+}
+
+/*
+ * Adds the new cycles (in nanoseconds) to the previous time stored.
+ */
+static uint64_t
+timecounter_cycles_to_ns_time(struct timecounter *tc, uint64_t cycle_tstamp)
+{
+	uint64_t delta = (cycle_tstamp - tc->cycle_last);
+	uint64_t nsec = tc->nsec;
+
+	nsec += delta;
+
+	return nsec;
+}
+
+static uint64_t
+i40e_read_timesync_cyclecounter(struct rte_eth_dev *dev)
 {
 	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
-	struct rte_eth_link *link = &dev->data->dev_link;
-	uint32_t tsync_ctl_l;
-	uint32_t tsync_ctl_h;
+	uint64_t systim_cycles = 0;
+
+	systim_cycles |= (uint64_t)I40E_READ_REG(hw, I40E_PRTTSYN_TIME_L);
+	systim_cycles |= (uint64_t)I40E_READ_REG(hw, I40E_PRTTSYN_TIME_H)
+			<< 32;
+
+	return systim_cycles;
+}
+
+static uint64_t
+timecounter_read_ns_delta(struct rte_eth_dev *dev)
+{
+	uint64_t cycle_now, cycle_delta;
+	struct i40e_adapter *adapter =
+			(struct i40e_adapter *)dev->data->dev_private;
+
+	/* Read cycle counter. */
+	cycle_now = adapter->tc.cc->read(dev);
+
+	/* Calculate the delta since the last timecounter_read_delta(). */
+	cycle_delta = (cycle_now - adapter->tc.cycle_last);
+
+	/* Update time stamp of timecounter_read_delta() call. */
+	adapter->tc.cycle_last = cycle_now;
+
+	/* Delta already in nanoseconds. */
+	return cycle_delta;
+}
+
+static uint64_t
+timecounter_read(struct rte_eth_dev *dev)
+{
+	uint64_t nsec;
+	struct i40e_adapter *adapter =
+			(struct i40e_adapter *)dev->data->dev_private;
+
+	/* Increment time by nanoseconds since last call. */
+	nsec = timecounter_read_ns_delta(dev);
+	nsec += adapter->tc.nsec;
+	adapter->tc.nsec = nsec;
+
+	return nsec;
+}
+
+
+static void
+timecounter_init(struct rte_eth_dev *dev,
+		      uint64_t start_time)
+{
+	struct i40e_adapter *adapter =
+			(struct i40e_adapter *)dev->data->dev_private;
+	adapter->tc.cc = &adapter->cc;
+	adapter->tc.cycle_last = adapter->tc.cc->read(dev);
+	adapter->tc.nsec = start_time;
+}
+
+static void
+i40e_start_cyclecounter(struct rte_eth_dev *dev)
+{
+	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct i40e_adapter *adapter =
+			(struct i40e_adapter *)dev->data->dev_private;
+	struct rte_eth_link link;
 	uint32_t tsync_inc_l;
 	uint32_t tsync_inc_h;
 
-	switch (link->link_speed) {
+	/* Get current link speed. */
+	memset(&link, 0, sizeof(link));
+	i40e_dev_link_update(dev, 1);
+	rte_i40e_dev_atomic_read_link_status(dev, &link);
+
+	switch (link.link_speed) {
 	case ETH_LINK_SPEED_40G:
 		tsync_inc_l = I40E_PTP_40GB_INCVAL & 0xFFFFFFFF;
 		tsync_inc_h = I40E_PTP_40GB_INCVAL >> 32;
@@ -6203,6 +6313,63 @@ i40e_timesync_enable(struct rte_eth_dev *dev)
 		tsync_inc_h = 0x0;
 	}
 
+	/* Set the timesync increment value. */
+	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_L, tsync_inc_l);
+	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_H, tsync_inc_h);
+
+	memset(&adapter->cc, 0, sizeof(struct cyclecounter));
+	adapter->cc.read = i40e_read_timesync_cyclecounter;
+}
+
+static int
+i40e_timesync_time_adjust(struct rte_eth_dev *dev, int64_t delta)
+{
+	struct i40e_adapter *adapter =
+			(struct i40e_adapter *)dev->data->dev_private;
+
+	adapter->tc.nsec += delta;
+
+	return 0;
+}
+
+static int
+i40e_timesync_time_set(struct rte_eth_dev *dev, struct timespec *ts)
+{
+	uint64_t ns;
+
+	ns = timespec_to_ns(ts);
+
+	/* Reset the timecounter. */
+	timecounter_init(dev, ns);
+
+	return 0;
+}
+
+static int
+i40e_timesync_time_get(struct rte_eth_dev *dev, struct timespec *ts)
+{
+	uint64_t ns;
+
+	ns = timecounter_read(dev);
+	*ts = ns_to_timespec(ns);
+
+	return 0;
+}
+
+static int
+i40e_timesync_enable(struct rte_eth_dev *dev)
+{
+	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	uint32_t tsync_ctl_l;
+	uint32_t tsync_ctl_h;
+	uint64_t ns;
+	struct timespec zerotime = {0, 0};
+
+	/* Set 0.0 epoch time to initialize timecounter. */
+	ns = timespec_to_ns(&zerotime);
+	i40e_start_cyclecounter(dev);
+	timecounter_init(dev, ns);
+
 	/* Clear timesync registers. */
 	I40E_READ_REG(hw, I40E_PRTTSYN_STAT_0);
 	I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_H);
@@ -6212,10 +6379,6 @@ i40e_timesync_enable(struct rte_eth_dev *dev)
 	I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_L(3));
 	I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_H);
 
-	/* Set the timesync increment value. */
-	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_L, tsync_inc_l);
-	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_H, tsync_inc_h);
-
 	/* Enable timestamping of PTP packets. */
 	tsync_ctl_l = I40E_READ_REG(hw, I40E_PRTTSYN_CTL0);
 	tsync_ctl_l |= I40E_PRTTSYN_TSYNENA;
@@ -6247,7 +6410,7 @@ i40e_timesync_disable(struct rte_eth_dev *dev)
 	I40E_WRITE_REG(hw, I40E_PRTTSYN_CTL0, tsync_ctl_l);
 	I40E_WRITE_REG(hw, I40E_PRTTSYN_CTL1, tsync_ctl_h);
 
-	/* Set the timesync increment value. */
+	/* Reset the timesync increment value. */
 	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_L, 0x0);
 	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_H, 0x0);
 
@@ -6259,10 +6422,14 @@ i40e_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 				struct timespec *timestamp, uint32_t flags)
 {
 	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct i40e_adapter *adapter =
+		(struct i40e_adapter *)dev->data->dev_private;
+
 	uint32_t sync_status;
 	uint32_t rx_stmpl;
 	uint32_t rx_stmph;
 	uint32_t index = flags & 0x03;
+	uint64_t regival = 0;
 
 	sync_status = I40E_READ_REG(hw, I40E_PRTTSYN_STAT_1);
 	if ((sync_status & (1 << index)) == 0)
@@ -6270,9 +6437,11 @@ i40e_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 
 	rx_stmpl = I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_L(index));
 	rx_stmph = I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_H(index));
+	timecounter_read(dev);
 
-	timestamp->tv_sec = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
-	timestamp->tv_nsec = 0;
+	regival = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
+	regival = timecounter_cycles_to_ns_time(&adapter->tc, regival);
+	*timestamp = ns_to_timespec(regival);
 
 	return  0;
 }
@@ -6282,9 +6451,13 @@ i40e_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 				struct timespec *timestamp)
 {
 	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct i40e_adapter *adapter =
+		(struct i40e_adapter *)dev->data->dev_private;
+
 	uint32_t sync_status;
 	uint32_t tx_stmpl;
 	uint32_t tx_stmph;
+	uint64_t regival = 0;
 
 	sync_status = I40E_READ_REG(hw, I40E_PRTTSYN_STAT_0);
 	if ((sync_status & I40E_PRTTSYN_STAT_0_TXTIME_MASK) == 0)
@@ -6293,8 +6466,9 @@ i40e_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 	tx_stmpl = I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_L);
 	tx_stmph = I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_H);
 
-	timestamp->tv_sec = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
-	timestamp->tv_nsec = 0;
+	regival = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
+	regival = timecounter_cycles_to_ns_time(&adapter->tc, regival);
+	*timestamp = ns_to_timespec(regival);
 
 	return  0;
 }
diff --git a/drivers/net/i40e/i40e_ethdev.h b/drivers/net/i40e/i40e_ethdev.h
index 6185657..7f2d79b 100644
--- a/drivers/net/i40e/i40e_ethdev.h
+++ b/drivers/net/i40e/i40e_ethdev.h
@@ -450,6 +450,22 @@ struct i40e_vf {
 };
 
 /*
+ * Structure for cyclecounter IEEE1588 functionality.
+ */
+struct cyclecounter {
+	uint64_t (*read)(struct rte_eth_dev *dev);
+};
+
+/*
+ * Structure to hold and calculate Unix epoch time.
+ */
+struct timecounter {
+	struct cyclecounter *cc;
+	uint64_t cycle_last;
+	uint64_t nsec;
+};
+
+/*
  * Structure to store private data for each PF/VF instance.
  */
 struct i40e_adapter {
@@ -462,6 +478,8 @@ struct i40e_adapter {
 		struct i40e_pf pf;
 		struct i40e_vf vf;
 	};
+	struct cyclecounter cc;
+	struct timecounter tc;
 };
 
 int i40e_dev_switch_queues(struct i40e_pf *pf, bool on);
-- 
2.1.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v2 5/6] example: PTP client slave minimal implementation
  2015-10-30  9:43 ` [dpdk-dev] [PATCH v2 0/6] add sample ptp slave application Daniel Mrzyglod
                     ` (3 preceding siblings ...)
  2015-10-30  9:43   ` [dpdk-dev] [PATCH v2 4/6] i40e: " Daniel Mrzyglod
@ 2015-10-30  9:43   ` Daniel Mrzyglod
  2015-10-30  9:43   ` [dpdk-dev] [PATCH v2 6/6] doc: add a PTPCLIENT sample guide Daniel Mrzyglod
  2015-10-30 11:23   ` [dpdk-dev] [PATCH v2 0/6] add sample ptp slave application Mcnamara, John
  6 siblings, 0 replies; 84+ messages in thread
From: Daniel Mrzyglod @ 2015-10-30  9:43 UTC (permalink / raw)
  To: dev

Add a sample application that acts as a PTP slave using the
DPDK ieee1588 functions.

Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
---
 MAINTAINERS                          |   3 +
 doc/guides/rel_notes/release_2_2.rst |   5 +
 examples/Makefile                    |   1 +
 examples/ptpclient/Makefile          |  57 +++
 examples/ptpclient/ptpclient.c       | 788 +++++++++++++++++++++++++++++++++++
 5 files changed, 854 insertions(+)
 create mode 100644 examples/ptpclient/Makefile
 create mode 100644 examples/ptpclient/ptpclient.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 080a8e8..3e446d5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -514,3 +514,6 @@ F: examples/tep_termination/
 F: examples/vmdq/
 F: examples/vmdq_dcb/
 F: doc/guides/sample_app_ug/vmdq_dcb_forwarding.rst
+
+M: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
+F: examples/ptpclient
diff --git a/doc/guides/rel_notes/release_2_2.rst b/doc/guides/rel_notes/release_2_2.rst
index b83ef7f..840d8ea 100644
--- a/doc/guides/rel_notes/release_2_2.rst
+++ b/doc/guides/rel_notes/release_2_2.rst
@@ -92,6 +92,11 @@ Libraries
 Examples
 ~~~~~~~~
 
+* **ptpclient: simple PTP slave client.**
+
+  Add a sample application that acts as a PTP slave using the
+  DPDK ieee1588 functions.
+
 
 Other
 ~~~~~
diff --git a/examples/Makefile b/examples/Makefile
index b4eddbd..4672534 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -74,5 +74,6 @@ DIRS-$(CONFIG_RTE_LIBRTE_XEN_DOM0) += vhost_xen
 DIRS-y += vmdq
 DIRS-y += vmdq_dcb
 DIRS-$(CONFIG_RTE_LIBRTE_POWER) += vm_power_manager
+DIRS-$(CONFIG_RTE_LIBRTE_IEEE1588) += ptpclient
 
 include $(RTE_SDK)/mk/rte.extsubdir.mk
diff --git a/examples/ptpclient/Makefile b/examples/ptpclient/Makefile
new file mode 100644
index 0000000..1a8efb3
--- /dev/null
+++ b/examples/ptpclient/Makefile
@@ -0,0 +1,57 @@
+#   BSD LICENSE
+#
+#   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+#   All rights reserved.
+#
+#   Redistribution and use in source and binary forms, with or without
+#   modification, are permitted provided that the following conditions
+#   are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in
+#       the documentation and/or other materials provided with the
+#       distribution.
+#     * Neither the name of Intel Corporation nor the names of its
+#       contributors may be used to endorse or promote products derived
+#       from this software without specific prior written permission.
+#
+#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+
+# Default target, can be overriddegitn by command line or environment
+RTE_TARGET ?= x86_64-native-linuxapp-gcc
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# binary name
+APP = ptpclient
+
+# all source are stored in SRCS-y
+SRCS-y := ptpclient.c
+
+CFLAGS += $(WERROR_FLAGS)
+
+# workaround for a gcc bug with noreturn attribute
+# http://gcc.gnu.org/bugzilla/show_bug.cgi?id=12603
+ifeq ($(CONFIG_RTE_TOOLCHAIN_GCC),y)
+CFLAGS_main.o += -Wno-return-type
+endif
+
+EXTRA_CFLAGS += -O3
+
+include $(RTE_SDK)/mk/rte.extapp.mk
diff --git a/examples/ptpclient/ptpclient.c b/examples/ptpclient/ptpclient.c
new file mode 100644
index 0000000..453c20b
--- /dev/null
+++ b/examples/ptpclient/ptpclient.c
@@ -0,0 +1,788 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This Application is a simple Layer 2 PTP v2 client
+ * IT shows T1-T4 values wchich are used to synchronize PHC clock.
+ * if -T 1 parameter is used Linux kernel Clock is sychronized with ptp clock
+ */
+
+#include <stdint.h>
+#include <inttypes.h>
+#include <rte_eal.h>
+#include <rte_ethdev.h>
+#include <rte_cycles.h>
+#include <rte_lcore.h>
+#include <rte_mbuf.h>
+#include <rte_ip.h>
+#include <limits.h>
+#include <sys/time.h>
+#include <getopt.h>
+
+#define RX_RING_SIZE 128
+#define TX_RING_SIZE 512
+
+#define NUM_MBUFS            8191
+#define MBUF_CACHE_SIZE       250
+
+/* Values for the PTP messageType field */
+#define SYNC                  0x0
+#define DELAY_REQ             0x1
+#define PDELAY_REQ            0x2
+#define PDELAY_RESP           0x3
+#define FOLLOW_UP             0x8
+#define DELAY_RESP            0x9
+#define PDELAY_RESP_FOLLOW_UP 0xA
+#define ANNOUNCE              0xB
+#define SIGNALING             0xC
+#define MANAGEMENT            0xD
+
+#define NSEC_PER_SEC  1000000000L
+#define PTP_PROTOCOL       0x88F7
+#define KERNEL_UPDATE         100
+#define DATE_IN_2015   1444924130
+#define MAX_PORT               64
+
+
+
+struct rte_mempool *mbuf_pool;
+uint32_t ptp_enabled_port_mask;
+uint8_t ptp_enabled_port_nb;
+static uint8_t ptp_enabled_ports[MAX_PORT];
+
+static const struct rte_eth_conf port_conf_default = {
+	.rxmode = { .max_rx_pkt_len = ETHER_MAX_LEN }
+};
+
+static const struct ether_addr ether_multicast = {
+	.addr_bytes = {0x01, 0x1b, 0x19, 0x0, 0x0, 0x0}
+};
+
+/* Structs used for PTP handling */
+struct tstamp {
+	uint16_t   sec_msb;
+	uint32_t   sec_lsb;
+	uint32_t   ns;
+}  __attribute__((packed));
+
+struct clock_identity {
+	uint8_t id[8];
+};
+
+struct port_identity {
+	struct clock_identity  clock_identity;
+	uint16_t               port_number;
+}  __attribute__((packed));
+
+struct ptp_header {
+	uint8_t              msgtype;
+	uint8_t              ver;
+	uint16_t             message_length;
+	uint8_t              domain_number;
+	uint8_t              reserved1;
+	uint8_t              flag_field[2];
+	int64_t              correction;
+	uint32_t             reserved2;
+	struct port_identity source_port_identity;
+	uint16_t             sequenceId;
+	uint8_t              control;
+	int8_t               log_message_interval;
+} __attribute__((packed));
+
+struct sync_msg {
+	struct ptp_header   hdr;
+	struct tstamp       origin_timestamp;
+} __attribute__((packed));
+
+struct follow_up_msg {
+	struct ptp_header   hdr;
+	struct tstamp       precise_origin_timestamp;
+	uint8_t             suffix[0];
+} __attribute__((packed));
+
+struct delay_req_msg {
+	struct ptp_header   hdr;
+	struct tstamp       origin_timestamp;
+} __attribute__((packed));
+
+struct delay_resp_msg {
+	struct ptp_header    hdr;
+	struct tstamp        rx_tstamp;
+	struct port_identity requesting_port_identity;
+	uint8_t              suffix[0];
+} __attribute__((packed));
+
+struct ptp_message {
+	union {
+		struct ptp_header          header;
+		struct sync_msg            sync;
+		struct delay_req_msg       delay_req;
+		struct follow_up_msg       follow_up;
+		struct delay_resp_msg      delay_resp;
+	} __attribute__((packed));
+};
+
+struct ptpv2_data_slave_ordinary {
+	struct rte_mbuf *m;
+	struct timespec timestamp;
+	struct timespec timestamp1;
+	struct timespec timestamp2;
+	struct timespec timestamp3;
+	struct timespec timestamp4;
+	struct timespec systime;
+	struct clock_identity client_clock_id;
+	struct clock_identity master_clock_id;
+	struct timeval new_adj;
+	struct timeval old_adj;
+	int64_t delta;
+	uint8_t portid;
+	uint8_t clock_portid;
+	uint16_t seqID_SYNC;
+	uint16_t seqID_FOLLOWUP;
+	uint8_t ptpset;
+	uint8_t kernel_time_set;
+	uint8_t current_ptp_port;
+};
+
+static struct ptpv2_data_slave_ordinary ptp_data;
+
+static inline uint64_t timespec64_to_ns(const struct timespec *ts)
+{
+	return ((uint64_t) ts->tv_sec * NSEC_PER_SEC) + ts->tv_nsec;
+}
+
+static struct timeval
+ns_to_timeval(int64_t nsec)
+{
+	struct timespec t_spec = {0, 0};
+	struct timeval t_eval = {0, 0};
+	int32_t rem;
+
+	if (nsec == 0)
+		return t_eval;
+	rem = nsec % NSEC_PER_SEC;
+	t_spec.tv_sec = nsec / NSEC_PER_SEC;
+
+	if (rem < 0) {
+		t_spec.tv_sec--;
+		rem += NSEC_PER_SEC;
+	}
+
+	t_spec.tv_nsec = rem;
+	t_eval.tv_sec = t_spec.tv_sec;
+	t_eval.tv_usec = t_spec.tv_nsec / 1000;
+
+	return t_eval;
+}
+
+/*
+ * Initializes a given port using global settings and with the RX buffers
+ * coming from the mbuf_pool passed as a parameter.
+ */
+static inline int
+port_init(uint8_t port, struct rte_mempool *mbuf_pool)
+{
+	struct rte_eth_dev_info dev_info;
+	struct rte_eth_conf port_conf = port_conf_default;
+	const uint16_t rx_rings = 1;
+	const uint16_t tx_rings = 1;
+	int retval;
+	uint16_t q;
+
+	if (port >= rte_eth_dev_count())
+		return -1;
+
+	/* Configure the Ethernet device. */
+	retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf);
+	if (retval != 0)
+		return retval;
+
+	/* Allocate and set up 1 RX queue per Ethernet port. */
+	for (q = 0; q < rx_rings; q++) {
+		retval = rte_eth_rx_queue_setup(port, q, RX_RING_SIZE,
+				rte_eth_dev_socket_id(port), NULL, mbuf_pool);
+
+		if (retval < 0)
+			return retval;
+	}
+
+	/* Allocate and set up 1 TX queue per Ethernet port. */
+	for (q = 0; q < tx_rings; q++) {
+		/* Setup txq_flags */
+		struct rte_eth_txconf *txconf;
+
+		rte_eth_dev_info_get(q, &dev_info);
+		txconf = &dev_info.default_txconf;
+		txconf->txq_flags = 0;
+
+		retval = rte_eth_tx_queue_setup(port, q, TX_RING_SIZE,
+				rte_eth_dev_socket_id(port), txconf);
+		if (retval < 0)
+			return retval;
+	}
+
+	/* Start the Ethernet port. */
+	retval = rte_eth_dev_start(port);
+	if (retval < 0)
+		return retval;
+
+	/* Enable timesync timestamping for the Ethernet device */
+	rte_eth_timesync_enable(port);
+
+	/* Enable RX in promiscuous mode for the Ethernet device. */
+	rte_eth_promiscuous_enable(port);
+
+	return 0;
+}
+
+static void
+print_clock_info(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	int64_t nsec;
+
+	printf("Master Clock id: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
+		ptp_data->master_clock_id.id[0],
+		ptp_data->master_clock_id.id[1],
+		ptp_data->master_clock_id.id[2],
+		ptp_data->master_clock_id.id[3],
+		ptp_data->master_clock_id.id[4],
+		ptp_data->master_clock_id.id[5],
+		ptp_data->master_clock_id.id[6],
+		ptp_data->master_clock_id.id[7]);
+
+	printf("\nT2 - Slave  Clock.  %lds %ldns",
+			(ptp_data->timestamp2.tv_sec),
+			(ptp_data->timestamp2.tv_nsec));
+
+	printf("\nT1 - Master Clock.  %lds %ldns ",
+			ptp_data->timestamp1.tv_sec,
+			(ptp_data->timestamp1.tv_nsec));
+
+	printf("\nT3 - Slave  Clock.  %lds %ldns",
+			ptp_data->timestamp3.tv_sec,
+			(ptp_data->timestamp3.tv_nsec));
+
+	printf("\nT4 - Master Clock.  %lds %ldns ",
+			ptp_data->timestamp4.tv_sec,
+			(ptp_data->timestamp4.tv_nsec));
+
+	printf("\nDelta between master and slave clocks:%"PRId64"ns\n",
+			ptp_data->delta);
+
+	clock_gettime(CLOCK_REALTIME, &ptp_data->systime);
+	rte_eth_timesync_time_get(ptp_data->clock_portid,
+			&ptp_data->timestamp);
+	time_t ts = ptp_data->timestamp.tv_sec;
+
+	printf("\n\nComparison between Linux kernel Time and PTP:");
+
+	printf("\nCurrent PTP Time: %.24s %.9ld ns",
+			ctime(&ts), ptp_data->timestamp.tv_nsec);
+
+	nsec = (int64_t)timespec64_to_ns(&ptp_data->timestamp) -
+			(int64_t)timespec64_to_ns(&ptp_data->systime);
+	ptp_data->new_adj = ns_to_timeval(nsec);
+
+	if (ptp_data->kernel_time_set == 1)
+		adjtime(&ptp_data->new_adj, NULL);
+
+	gettimeofday(&ptp_data->new_adj, NULL);
+	time_t tp = ptp_data->new_adj.tv_sec;
+
+	printf("\nCurrent SYS Time: %.24s %.6ld ns",
+				ctime(&tp), ptp_data->new_adj.tv_usec);
+
+	printf("\nDelta between PTP and Linux Kernel time:%"PRId64"ns\n",
+				nsec);
+	printf("[Ctrl+C to quit]\n");
+
+	/* Clear screen and put cursor in column 1, row 1 */
+	printf("\033[2J\033[1;1H");
+}
+
+static int64_t
+delta_eval(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	int64_t delta;
+	uint64_t t1 = 0;
+	uint64_t t2 = 0;
+	uint64_t t3 = 0;
+	uint64_t t4 = 0;
+
+	t1 = timespec64_to_ns(&ptp_data->timestamp1);
+	t2 = timespec64_to_ns(&ptp_data->timestamp2);
+	t3 = timespec64_to_ns(&ptp_data->timestamp3);
+	t4 = timespec64_to_ns(&ptp_data->timestamp4);
+
+	/* delta = -[(T2-T1) - (T4-T3)]/2 */
+	delta = -((int64_t)((t2 - t1) - (t4 - t3))) / 2;
+
+	return delta;
+}
+
+/*
+ * Parse PTP SYNC message
+ */
+static void
+parse_sync(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	struct ptp_header *ptp_hdr;
+
+	ptp_hdr = (struct ptp_header *)(rte_pktmbuf_mtod(ptp_data->m, char *)
+			+ sizeof(struct ether_hdr));
+	ptp_data->seqID_SYNC = rte_be_to_cpu_16(ptp_hdr->sequenceId);
+
+	if (ptp_data->ptpset == 0) {
+		rte_memcpy(&ptp_data->master_clock_id,
+				&ptp_hdr->source_port_identity.clock_identity,
+				sizeof(struct clock_identity));
+		ptp_data->ptpset = 1;
+	}
+
+	if (memcmp(&ptp_hdr->source_port_identity.clock_identity,
+			&ptp_hdr->source_port_identity.clock_identity,
+			sizeof(struct clock_identity)) == 0) {
+
+		if (ptp_data->ptpset == 1)
+			rte_eth_timesync_read_rx_timestamp(ptp_data->portid,
+					&ptp_data->timestamp2, 0);
+	}
+
+}
+
+/*
+ * Parse PTP FOLLOWUP message & Send DELAY_REQ to master clock.
+ */
+static void
+parse_fup(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	struct ether_hdr *eth_hdr;
+	struct ptp_header *ptp_hdr;
+	struct clock_identity *client_clkid;
+	struct ptp_message *ptp_msg;
+	struct rte_mbuf *created_pkt;
+	struct ether_addr eth_multicast = ether_multicast;
+	size_t pkt_size;
+	int wait_us;
+	struct rte_mbuf *m = ptp_data->m;
+
+	eth_hdr = rte_pktmbuf_mtod(m, struct ether_hdr *);
+	ptp_hdr = (struct ptp_header *)(rte_pktmbuf_mtod(m, char *)
+			+ sizeof(struct ether_hdr));
+	if (memcmp(&ptp_data->master_clock_id,
+			&ptp_hdr->source_port_identity.clock_identity,
+			sizeof(struct clock_identity)) != 0)
+		return;
+
+	ptp_data->seqID_FOLLOWUP = rte_be_to_cpu_16(ptp_hdr->sequenceId);
+	ptp_msg = (struct ptp_message *)
+			(rte_pktmbuf_mtod(m, char *) + sizeof(struct ether_hdr));
+	ptp_data->timestamp1.tv_nsec =
+		ntohl(ptp_msg->follow_up.precise_origin_timestamp.ns);
+	ptp_data->timestamp1.tv_sec =
+		((uint64_t)ntohl
+				(ptp_msg->follow_up.precise_origin_timestamp.sec_lsb)) |
+		(((uint64_t)ntohs
+				(ptp_msg->follow_up.precise_origin_timestamp.sec_msb))
+				<< 32);
+
+	if (ptp_data->seqID_FOLLOWUP == ptp_data->seqID_SYNC) {
+
+		created_pkt = rte_pktmbuf_alloc(mbuf_pool);
+		pkt_size = sizeof(struct ether_hdr) + sizeof(struct ptp_message);
+		created_pkt->data_len = pkt_size;
+		created_pkt->pkt_len = pkt_size;
+		eth_hdr = rte_pktmbuf_mtod(created_pkt, struct ether_hdr *);
+		rte_eth_macaddr_get(ptp_data->portid, &eth_hdr->s_addr);
+
+		/* Set multicast address 01-1B-19-00-00-00 */
+		ether_addr_copy(&eth_multicast, &eth_hdr->d_addr);
+
+		eth_hdr->ether_type = htons(PTP_PROTOCOL);
+		ptp_msg = (struct ptp_message *)
+			(rte_pktmbuf_mtod(created_pkt, char *) +
+			sizeof(struct ether_hdr));
+
+		ptp_msg->delay_req.hdr.sequenceId = htons(ptp_data->seqID_SYNC);
+		ptp_msg->delay_req.hdr.msgtype = DELAY_REQ;
+		ptp_msg->delay_req.hdr.ver = 2;
+		ptp_msg->delay_req.hdr.control = 1;
+		ptp_msg->delay_req.hdr.log_message_interval = 127;
+
+		/* Set up clock id */
+		client_clkid =
+			&ptp_msg->delay_req.hdr.source_port_identity.clock_identity;
+
+		client_clkid->id[0] = eth_hdr->s_addr.addr_bytes[0];
+		client_clkid->id[1] = eth_hdr->s_addr.addr_bytes[1];
+		client_clkid->id[2] = eth_hdr->s_addr.addr_bytes[2];
+		client_clkid->id[3] = 0xFF;
+		client_clkid->id[4] = 0xFE;
+		client_clkid->id[5] = eth_hdr->s_addr.addr_bytes[3];
+		client_clkid->id[6] = eth_hdr->s_addr.addr_bytes[4];
+		client_clkid->id[7] = eth_hdr->s_addr.addr_bytes[5];
+
+		rte_memcpy(&ptp_data->client_clock_id,
+				client_clkid,
+				sizeof(struct clock_identity));
+
+		/* Enable Flag for Hardware Timestamping */
+		created_pkt->ol_flags |= PKT_TX_IEEE1588_TMST;
+
+		/* We read value from NIC to prevent latching with old value */
+		rte_eth_timesync_read_tx_timestamp(ptp_data->portid,
+				&ptp_data->timestamp3);
+
+		/* the packet is being transmitted */
+		rte_eth_tx_burst(ptp_data->portid, 0, &created_pkt, 1);
+
+		wait_us = 0;
+		ptp_data->timestamp3.tv_nsec = 0;
+		ptp_data->timestamp3.tv_sec = 0;
+
+		/* we must wait at least 1us to read TX Timestamp */
+		while ((rte_eth_timesync_read_tx_timestamp(ptp_data->portid,
+				&ptp_data->timestamp3) < 0) && (wait_us < 1000)) {
+			rte_delay_us(1);
+			wait_us++;
+		}
+	}
+}
+
+/*
+ * Parse DELAY_RESP message
+ */
+static void
+parse_drsp(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	struct rte_mbuf *m = ptp_data->m;
+	struct ptp_message *ptp_msg;
+	uint16_t seqid;
+	int64_t nsec;
+	struct timespec systime;
+	struct timespec nettime;
+
+	ptp_msg = (struct ptp_message *) (rte_pktmbuf_mtod(m, char *) +
+					sizeof(struct ether_hdr));
+	seqid =  rte_be_to_cpu_16(ptp_msg->delay_resp.hdr.sequenceId);
+	if (memcmp(&ptp_data->client_clock_id,
+			&ptp_msg->delay_resp.requesting_port_identity.clock_identity,
+			sizeof(struct clock_identity)) == 0) {
+
+		if (seqid == ptp_data->seqID_FOLLOWUP) {
+			ptp_data->timestamp4.tv_nsec =
+					ntohl(ptp_msg->delay_resp.rx_tstamp.ns);
+			ptp_data->timestamp4.tv_sec =
+				((uint64_t)ntohl
+					(ptp_msg->delay_resp.rx_tstamp.sec_lsb)) |
+				(((uint64_t)ntohs
+					(ptp_msg->delay_resp.rx_tstamp.sec_msb)) << 32);
+
+			/* Evaluate delta for adjustment */
+			ptp_data->delta = delta_eval(ptp_data);
+			rte_eth_timesync_time_adjust(ptp_data->portid, ptp_data->delta);
+			ptp_data->clock_portid = ptp_data->portid;
+			ptp_data->current_ptp_port = ptp_data->clock_portid;
+
+			if (ptp_data->kernel_time_set == 1) {
+				clock_gettime(CLOCK_REALTIME, &systime);
+				rte_eth_timesync_time_get(ptp_data->current_ptp_port,
+						&nettime);
+				nsec = (int64_t)timespec64_to_ns(&nettime) -
+						(int64_t)timespec64_to_ns(&systime);
+
+				if (nsec > 2*NSEC_PER_SEC || nsec < -2*NSEC_PER_SEC) {
+
+					/*
+					 * This epoch time is set for first pass when
+					 * right clock is not setup and values
+					 * take us back to '70s
+					 */
+					if (likely(nettime.tv_sec > DATE_IN_2015))
+						clock_settime(CLOCK_REALTIME, &nettime);
+				}
+			}
+		}
+	}
+}
+
+/* This function processes PTP packets, implementing
+ * slave PTP IEEE1588 L2 functionality
+ */
+static void
+parse_ptp_frames(uint8_t portid, struct rte_mbuf *m) {
+	struct ptp_header *ptp_hdr;
+	struct ether_hdr *eth_hdr;
+	uint16_t eth_type;
+
+	eth_hdr = rte_pktmbuf_mtod(m, struct ether_hdr *);
+	eth_type = rte_be_to_cpu_16(eth_hdr->ether_type);
+
+	if (eth_type == PTP_PROTOCOL) {
+		ptp_data.m = m;
+		ptp_data.portid = portid;
+		ptp_hdr = (struct ptp_header *)(rte_pktmbuf_mtod(m, char *)
+					+ sizeof(struct ether_hdr));
+
+		switch (ptp_hdr->msgtype) {
+		case SYNC:
+			parse_sync(&ptp_data);
+			break;
+		case FOLLOW_UP:
+			parse_fup(&ptp_data);
+			break;
+		case DELAY_RESP:
+			parse_drsp(&ptp_data);
+			print_clock_info(&ptp_data);
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+/*
+ * The lcore main. This is the main thread that does the work, reading from
+ * an input port and writing to an output port.
+ */
+static __attribute__((noreturn)) void
+lcore_main(void)
+{
+	uint8_t portid;
+	unsigned nb_rx;
+	struct rte_mbuf *m;
+	int64_t i = 0;
+	int64_t nsec = 0;
+
+	/*
+	 * Check that the port is on the same NUMA node as the polling thread
+	 * for best performance.
+	 */
+	printf("\nCore %u Waiting for SYNC packets. [Ctrl+C to quit]\n",
+			rte_lcore_id());
+
+	/* Run until the application is quit or killed. */
+
+	while (1) {
+		/*
+		 * Read packet from RX queues
+		 */
+		for (portid = 0; portid < ptp_enabled_port_nb; portid++) {
+
+			portid = ptp_enabled_ports[portid];
+			nb_rx = rte_eth_rx_burst(portid, 0, &m, 1);
+
+			if (ptp_data.kernel_time_set == 1) {
+				/* Update every KERNEL_UPDATE cycle */
+				if (i % KERNEL_UPDATE == 0) {
+					clock_gettime(CLOCK_REALTIME,
+							&ptp_data.systime);
+
+					rte_eth_timesync_time_get(ptp_data.current_ptp_port,
+							&ptp_data.timestamp);
+
+					nsec = (int64_t)timespec64_to_ns(&ptp_data.timestamp) -
+						(int64_t)timespec64_to_ns(&ptp_data.systime);
+
+					ptp_data.new_adj = ns_to_timeval(nsec);
+					adjtime(&ptp_data.new_adj, 0);
+				}
+				i++;
+			}
+
+			if (likely(nb_rx == 0))
+				continue;
+
+			if (m->ol_flags & PKT_RX_IEEE1588_PTP)
+				parse_ptp_frames(portid, m);
+
+			rte_pktmbuf_free(m);
+		}
+	}
+}
+
+static void
+print_usage(const char *prgname)
+{
+	printf("%s [EAL options] -- -p PORTMASK -T VALUE\n"
+		"  -T VALUE: 0 - Disable, 1 - Enable Linux Clock Synchronization"
+		" (0 default)\n"
+		"  -p PORTMASK: hexadecimal bitmask of ports to configure\n",
+			prgname);
+}
+
+static int
+ptp_parse_portmask(const char *portmask)
+{
+	char *end = NULL;
+	unsigned long pm;
+
+	/* parse hexadecimal string */
+	pm = strtoul(portmask, &end, 16);
+	if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
+		return -1;
+
+	if (pm == 0)
+		return -1;
+
+	return pm;
+}
+
+static int
+parse_ptp_kernel(const char *param)
+{
+	char *end = NULL;
+	unsigned long pm;
+
+	pm = strtoul(param, &end, 16);
+	if ((param[0] == '\0') || (end == NULL) || (*end != '\0'))
+		return -1;
+	if (pm == 0)
+		return 0;
+
+	return 1;
+}
+
+/* Parse the argument given in the command line of the application */
+static int
+ptp_parse_args(int argc, char **argv)
+{
+	int opt, ret;
+	char **argvopt;
+	int option_index;
+	char *prgname = argv[0];
+	static struct option lgopts[] = { {NULL, 0, 0, 0} };
+
+	argvopt = argv;
+
+	while ((opt = getopt_long(argc, argvopt, "p:T:",
+				  lgopts, &option_index)) != EOF) {
+
+		switch (opt) {
+
+		/* portmask */
+		case 'p':
+			ptp_enabled_port_mask = ptp_parse_portmask(optarg);
+			if (ptp_enabled_port_mask == 0) {
+				printf("invalid portmask\n");
+				print_usage(prgname);
+				return -1;
+			}
+			break;
+		/* time synchronization */
+		case 'T':
+			ret = parse_ptp_kernel(optarg);
+			if (ret < 0) {
+				print_usage(prgname);
+				return -1;
+			}
+
+			ptp_data.kernel_time_set = ret;
+			break;
+
+		default:
+			print_usage(prgname);
+			return -1;
+		}
+	}
+
+	argv[optind-1] = prgname;
+
+	optind = 0; /* reset getopt lib */
+	return 0;
+}
+
+/*
+ * The main function, which does initialization and calls the per-lcore
+ * functions.
+ */
+int
+main(int argc, char *argv[])
+{
+	unsigned nb_ports;
+
+	uint8_t portid;
+
+	/* Initialize the Environment Abstraction Layer (EAL). */
+	int ret = rte_eal_init(argc, argv);
+
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
+
+	memset(&ptp_data, '\0', sizeof(struct ptpv2_data_slave_ordinary));
+
+	argc -= ret;
+	argv += ret;
+
+	ret = ptp_parse_args(argc, argv);
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "Error with PTP initialization\n");
+
+	/* Check that there is an even number of ports to send/receive on. */
+	nb_ports = rte_eth_dev_count();
+
+	/* Creates a new mempool in memory to hold the mbufs. */
+	mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", NUM_MBUFS * nb_ports,
+		MBUF_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
+
+	if (mbuf_pool == NULL)
+		rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
+
+	/* Initialize all ports. */
+	for (portid = 0; portid < nb_ports; portid++) {
+		if ((ptp_enabled_port_mask & (1 << portid)) != 0) {
+
+			if (port_init(portid, mbuf_pool) == 0) {
+				ptp_enabled_ports[ptp_enabled_port_nb] = portid;
+				ptp_enabled_port_nb++;
+			} else {
+				rte_exit(EXIT_FAILURE, "Cannot init port %"PRIu8 "\n",
+						portid);
+			}
+		} else
+			printf("Skipping disabled port %u\n", portid);
+	}
+
+	if (ptp_enabled_port_nb == 0) {
+		rte_exit(EXIT_FAILURE,
+			"All available ports are disabled. Please set portmask.\n");
+	}
+
+	if (rte_lcore_count() > 1)
+		printf("\nWARNING: Too many lcores enabled. Only 1 used.\n");
+
+	/* Call lcore_main on the master core only. */
+	lcore_main();
+
+	return 0;
+}
-- 
2.1.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v2 6/6] doc: add a PTPCLIENT sample guide
  2015-10-30  9:43 ` [dpdk-dev] [PATCH v2 0/6] add sample ptp slave application Daniel Mrzyglod
                     ` (4 preceding siblings ...)
  2015-10-30  9:43   ` [dpdk-dev] [PATCH v2 5/6] example: PTP client slave minimal implementation Daniel Mrzyglod
@ 2015-10-30  9:43   ` Daniel Mrzyglod
  2015-10-30 11:23   ` [dpdk-dev] [PATCH v2 0/6] add sample ptp slave application Mcnamara, John
  6 siblings, 0 replies; 84+ messages in thread
From: Daniel Mrzyglod @ 2015-10-30  9:43 UTC (permalink / raw)
  To: dev

It includes:
 - Add the ptpclient picture with svg format.
 - Add the ptpclient.rst file
 - Change the index.rst file for the above pictures index.

Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
---
 doc/guides/sample_app_ug/img/ptpclient.svg | 520 +++++++++++++++++++++++++++++
 doc/guides/sample_app_ug/index.rst         |   3 +
 doc/guides/sample_app_ug/ptpclient.rst     | 324 ++++++++++++++++++
 3 files changed, 847 insertions(+)
 create mode 100644 doc/guides/sample_app_ug/img/ptpclient.svg
 create mode 100644 doc/guides/sample_app_ug/ptpclient.rst

diff --git a/doc/guides/sample_app_ug/img/ptpclient.svg b/doc/guides/sample_app_ug/img/ptpclient.svg
new file mode 100644
index 0000000..55c134e
--- /dev/null
+++ b/doc/guides/sample_app_ug/img/ptpclient.svg
@@ -0,0 +1,520 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="105mm"
+   height="148mm"
+   viewBox="0 0 372.04724 524.40945"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="drawing3.svg">
+  <defs
+     id="defs4">
+    <marker
+       inkscape:stockid="Arrow2Mend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow2Mend"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4256"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="scale(-0.6,-0.6)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Lstart"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow2Lstart"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4247"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="matrix(1.1,0,0,1.1,1.1,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible"
+       id="marker6746"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="Arrow2Lend">
+      <path
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         id="path6748"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker6526"
+       style="overflow:visible"
+       inkscape:isstock="true"
+       inkscape:collect="always">
+      <path
+         id="path6528"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker6276"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path6278"
+         d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(-0.8,0,0,-0.8,-10,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible"
+       id="marker6128"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="DotM">
+      <path
+         transform="matrix(0.4,0,0,0.4,2.96,0.4)"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
+         id="path6130"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible"
+       id="marker6022"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="Arrow2Lend"
+       inkscape:collect="always">
+      <path
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         id="path6024"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible"
+       id="marker5922"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="DotM">
+      <path
+         transform="matrix(0.4,0,0,0.4,2.96,0.4)"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
+         id="path5924"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow2Lend"
+       style="overflow:visible"
+       inkscape:isstock="true"
+       inkscape:collect="always">
+      <path
+         id="path4250"
+         style="fill:#0000ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="DotM"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker5676"
+       style="overflow:visible"
+       inkscape:isstock="true"
+       inkscape:collect="always">
+      <path
+         id="path5678"
+         d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
+         style="fill:#0000ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(0.4,0,0,0.4,2.96,0.4)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lstart"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker5600"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path5602"
+         d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(0.8,0,0,0.8,10,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible"
+       id="marker5510"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="DotM"
+       inkscape:collect="always">
+      <path
+         transform="matrix(0.4,0,0,0.4,2.96,0.4)"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
+         id="path5512"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker4651"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4653"
+         d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(-0.8,0,0,-0.8,-10,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker4605"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4607"
+         d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(-0.8,0,0,-0.8,-10,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow1Lend"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4232"
+         d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(-0.8,0,0,-0.8,-10,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="DotM"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="DotM"
+       style="overflow:visible"
+       inkscape:isstock="true"
+       inkscape:collect="always">
+      <path
+         id="path4293"
+         d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(0.4,0,0,0.4,2.96,0.4)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lstart"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow1Lstart"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4229"
+         d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(0.8,0,0,0.8,10,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3402"
+       is_visible="true" />
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3398"
+       is_visible="true" />
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3394"
+       is_visible="true" />
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3378"
+       is_visible="true" />
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3374"
+       is_visible="true" />
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3370"
+       is_visible="true" />
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3366"
+       is_visible="true" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1.06"
+     inkscape:cx="280.7353"
+     inkscape:cy="266.181"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     showguides="false"
+     inkscape:window-width="1920"
+     inkscape:window-height="1053"
+     inkscape:window-x="1920"
+     inkscape:window-y="103"
+     inkscape:window-maximized="0" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-527.95276)">
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.44085264;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="m 64.094156,600.99321 c 0,60.57637 0,393.74649 0,393.74649"
+       id="path3388"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.63210523;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="m 291.64401,600.59617 0,394.60083"
+       id="path3390"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.28789771;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#DotM);marker-end:url(#marker6746)"
+       d="M 64.094156,623.68474 289.7829,665.38811"
+       id="path4223"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.28789771;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#marker5510);marker-end:url(#marker6526)"
+       d="M 63.199944,683.08006 290.44233,725.29448"
+       id="path5274"
+       inkscape:connector-curvature="0" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="295.22058"
+       y="665.10974"
+       id="text5424"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan5426"
+         x="295.22058"
+         y="665.10974">T2</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#800080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="242.44649"
+       y="657.08087"
+       id="text5428"
+       sodipodi:linespacing="100%"
+       transform="matrix(0.98289795,0.18415108,-0.18415108,0.98289795,0,0)"><tspan
+         sodipodi:role="line"
+         id="tspan5430"
+         x="242.44649"
+         y="657.08087">FOLLOW UP:T1</tspan></text>
+    <path
+       style="fill:#0000ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.28789771;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#marker5676);marker-end:url(#Arrow2Lend)"
+       d="M 291.5001,774.36878 64.400801,816.4401"
+       id="path5586"
+       inkscape:connector-curvature="0" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="-44.967201"
+       y="809.64362"
+       id="text5898"
+       sodipodi:linespacing="100%"
+       transform="matrix(0.98291896,-0.18403889,0.18403889,0.98291896,0,0)"><tspan
+         sodipodi:role="line"
+         id="tspan5900"
+         x="-44.967201"
+         y="809.64362">DELAY REQUEST</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="297.08099"
+       y="774.7981"
+       id="text5902"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan5904"
+         x="297.08099"
+         y="774.7981">T3</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#800080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="35.20845"
+       y="817.29871"
+       id="text5906"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan5908"
+         x="35.20845"
+         y="817.29871">T4</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#800080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="33.920555"
+       y="623.68475"
+       id="text5910"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan5912"
+         x="33.920555"
+         y="623.68475">T1</tspan></text>
+    <path
+       style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.28789771;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#marker6128);marker-end:url(#marker6022)"
+       d="M 63.971502,838.62056 290.6415,881.55049"
+       id="path5914"
+       inkscape:connector-curvature="0" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#800080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="223.59686"
+       y="811.73932"
+       id="text6858"
+       sodipodi:linespacing="100%"
+       transform="matrix(0.98367106,0.17997568,-0.17997568,0.98367106,0,0)"><tspan
+         sodipodi:role="line"
+         id="tspan6860"
+         x="223.59686"
+         y="811.73932">DELAY RESPONSE:T4</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="813.40204"
+       y="-298.41309"
+       id="text6862"
+       sodipodi:linespacing="100%"
+       transform="matrix(0.0147127,0.99989176,-0.99989176,0.0147127,0,0)"><tspan
+         sodipodi:role="line"
+         id="tspan6864"
+         x="813.40204"
+         y="-298.41309">time</tspan></text>
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2.1464963;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow2Mend)"
+       d="m 316.97185,863.23372 0,66.11208"
+       id="path6866"
+       inkscape:connector-curvature="0" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.42257881px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#800080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="38.764343"
+       y="590.47479"
+       id="text7094"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan7096"
+         x="38.764343"
+         y="590.47479">master</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:47.51625061px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="571.69812"
+       y="61.796165"
+       id="text7098"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan7100"
+         x="571.69812"
+         y="61.796165" /></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.42257881px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="271.23392"
+       y="593.71478"
+       id="text7102"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan7104"
+         x="271.23392"
+         y="593.71478">slave</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.3917141px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#800080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="244.40062"
+       y="602.10443"
+       id="text4317"
+       sodipodi:linespacing="125%"
+       transform="matrix(0.98605316,0.16643065,-0.16643065,0.98605316,0,0)"><tspan
+         sodipodi:role="line"
+         id="tspan4319"
+         x="244.40062"
+         y="602.10443">SYNC</tspan></text>
+  </g>
+</svg>
diff --git a/doc/guides/sample_app_ug/index.rst b/doc/guides/sample_app_ug/index.rst
index 9beedd9..8ae86c0 100644
--- a/doc/guides/sample_app_ug/index.rst
+++ b/doc/guides/sample_app_ug/index.rst
@@ -73,6 +73,7 @@ Sample Applications User Guide
     vm_power_management
     tep_termination
     proc_info
+    ptpclient
 
 **Figures**
 
@@ -136,6 +137,8 @@ Sample Applications User Guide
 :numref:`figure_overlay_networking` :ref:`figure_overlay_networking`
 :numref:`figure_tep_termination_arch` :ref:`figure_tep_termination_arch`
 
+:numref:`figure_ptpclient_highlevel` :ref:`figure_ptpclient_highlevel`
+
 **Tables**
 
 :numref:`table_qos_metering_1` :ref:`table_qos_metering_1`
diff --git a/doc/guides/sample_app_ug/ptpclient.rst b/doc/guides/sample_app_ug/ptpclient.rst
new file mode 100644
index 0000000..31aa505
--- /dev/null
+++ b/doc/guides/sample_app_ug/ptpclient.rst
@@ -0,0 +1,324 @@
+..  BSD LICENSE
+    Copyright(c) 2015 Intel Corporation. All rights reserved.
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions
+    are met:
+
+    * Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.
+    * Neither the name of Intel Corporation nor the names of its
+    contributors may be used to endorse or promote products derived
+    from this software without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+    OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+PTP Client Sample Application
+=============================
+
+The PTP (Precision Time Protocol) client sample application is a simple
+example of using the DPDK IEEE1588 API to communicate with a PTP master clock
+to synchronize the time on the NIC and, optionally, on the Linux system.
+
+Note, PTP is a time syncing protocol and cannot be used within DPDK as a
+time-stamping mechanism. See the following for an explanation of the protocol:
+`Precision Time Protocol
+<https://en.wikipedia.org/wiki/Precision_Time_Protocol>`_.
+
+
+Limitations
+-----------
+
+The PTP sample application is intended as a simple reference implementation of
+a PTP client using the DPDK IEEE1588 API.
+In order to keep the application simple the following assumptions are made:
+
+* The first discovered master is the master for the session.
+* Only L2 PTP packets are supported.
+* Only the PTP v2 protocol is supported.
+* Only the slave clock is implemented.
+
+
+How the Application Works
+-------------------------
+
+.. _figure_ptpclient_highlevel:
+
+.. figure:: img/ptpclient.*
+
+   PTP Synchronization Protocol
+
+The PTP synchronization in the sample application works as follows:
+
+* Master sends *Sync* message - the slave saves it as T2.
+* Master sends *Follow Up* message and sends time of T1.
+* Slave sends *Delay Request* frame to PTP Master and stores T3.
+* Master sends *Delay Response* T4 time which is time of received T3.
+
+The adjustment for slave can be represented as:
+
+   adj = -[(T2-T1)-(T4 - T3)]/2
+
+If the command line parameter ``-T 1`` is used the application also
+synchronizes the PTP PHC clock with the Linux kernel clock.
+
+
+Compiling the Application
+-------------------------
+
+To compile the application, export the path to the DPDK source tree and edit
+the ``config/common_linuxapp`` configuration file to enable IEEE1588:
+
+.. code-block:: console
+
+    export RTE_SDK=/path/to/rte_sdk
+
+    # Edit  common_linuxapp and set the following options:
+    CONFIG_RTE_LIBRTE_IEEE1588=y
+
+Set the target, for example:
+
+.. code-block:: console
+
+    export RTE_TARGET=x86_64-native-linuxapp-gcc
+
+See the *DPDK Getting Started* Guide for possible ``RTE_TARGET`` values.
+
+Build the application as follows:
+
+.. code-block:: console
+
+    # Recompile DPDK.
+    make install T=$RTE_TARGET
+
+    # Compile the application.
+    cd ${RTE_SDK}/examples/ptpclient
+    make
+
+
+Running the Application
+-----------------------
+
+To run the example in a ``linuxapp`` environment:
+
+.. code-block:: console
+
+    ./build/ptpclient -c 2 -n 4 -- -p 0x1 -T 0
+
+Refer to *DPDK Getting Started Guide* for general information on running
+applications and the Environment Abstraction Layer (EAL) options.
+
+* ``-p portmask``: Hexadecimal portmask.
+* ``-T 0``: Update only the PTP slave clock.
+* ``-T 1``: Update the PTP slave clock and synchronize the Linux Kernel to the PTP clock.
+
+
+Code Explanation
+----------------
+
+The following sections provide an explanation of the main components of the
+code.
+
+All DPDK library functions used in the sample code are prefixed with ``rte_``
+and are explained in detail in the *DPDK API Documentation*.
+
+
+The Main Function
+~~~~~~~~~~~~~~~~~
+
+The ``main()`` function performs the initialization and calls the execution
+threads for each lcore.
+
+The first task is to initialize the Environment Abstraction Layer (EAL).  The
+``argc`` and ``argv`` arguments are provided to the ``rte_eal_init()``
+function. The value returned is the number of parsed arguments:
+
+.. code-block:: c
+
+    int ret = rte_eal_init(argc, argv);
+    if (ret < 0)
+        rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
+
+And than we parse application specific arguments
+
+.. code-block:: c
+
+    argc -= ret;
+    argv += ret;
+
+    ret = ptp_parse_args(argc, argv);
+    if (ret < 0)
+        rte_exit(EXIT_FAILURE, "Error with PTP initialization\n");
+
+The ``main()`` also allocates a mempool to hold the mbufs (Message Buffers)
+used by the application:
+
+.. code-block:: c
+
+    mbuf_pool = rte_mempool_create("MBUF_POOL",
+                                   NUM_MBUFS * nb_ports,
+                                   MBUF_SIZE,
+                                   MBUF_CACHE_SIZE,
+                                   sizeof(struct rte_pktmbuf_pool_private),
+                                   rte_pktmbuf_pool_init, NULL,
+                                   rte_pktmbuf_init,      NULL,
+                                   rte_socket_id(),
+                                   0);
+
+Mbufs are the packet buffer structure used by DPDK. They are explained in
+detail in the "Mbuf Library" section of the *DPDK Programmer's Guide*.
+
+The ``main()`` function also initializes all the ports using the user defined
+``port_init()`` function with portmask provided by user:
+
+.. code-block:: c
+
+    for (portid = 0; portid < nb_ports; portid++)
+        if ((ptp_enabled_port_mask & (1 << portid)) != 0) {
+
+            if (port_init(portid, mbuf_pool) == 0) {
+                ptp_enabled_ports[ptp_enabled_port_nb] = portid;
+                ptp_enabled_port_nb++;
+            } else {
+                rte_exit(EXIT_FAILURE, "Cannot init port %"PRIu8 "\n",
+                        portid);
+            }
+        }
+
+
+Once the initialization is complete, the application is ready to launch a
+function on an lcore. In this example ``lcore_main()`` is called on a single
+lcore.
+
+.. code-block:: c
+
+	lcore_main();
+
+The ``lcore_main()`` function is explained below.
+
+
+The Lcores Main
+~~~~~~~~~~~~~~~
+
+As we saw above the ``main()`` function calls an application function on the
+available lcores.
+
+The main work of the application is done within the loop:
+
+.. code-block:: c
+
+        for (portid = 0; portid < ptp_enabled_port_nb; portid++) {
+
+            portid = ptp_enabled_ports[portid];
+            nb_rx = rte_eth_rx_burst(portid, 0, &m, 1);
+
+            if (ptp_data.kernel_time_set == 1) {
+                /* Update every KERNEL_UPDATE cycle */
+                if (i % KERNEL_UPDATE == 0) {
+                    clock_gettime(CLOCK_REALTIME, &ptp_data.systime);
+
+                    rte_eth_timesync_gettime(ptp_data.current_ptp_port,
+                            &ptp_data.timestamp);
+
+                    nsec = (int64_t)timespec64_to_ns(&ptp_data.timestamp) -
+                            (int64_t)timespec64_to_ns(&ptp_data.systime);
+
+                    ptp_data.new_adj = ns_to_timeval(nsec);
+                    adjtime(&ptp_data.new_adj, 0);
+                }
+                i++;
+            }
+
+            if (likely(nb_rx == 0))
+                continue;
+
+            if (m->ol_flags & PKT_RX_IEEE1588_PTP)
+                parse_ptp_frames(portid, m);
+
+            rte_pktmbuf_free(m);
+
+        }
+
+Packets are received one by one on the RX ports and, if required, PTP response
+packets are transmitted on the TX ports.
+
+If the offload flags in the mbuf indicate that the packet is a PTP packet then
+the packet is parsed to determine which type:
+
+.. code-block:: c
+
+            if (m->ol_flags & PKT_RX_IEEE1588_PTP)
+                 parse_ptp_frames(portid, m);
+
+
+All packets are freed explicitly using ``rte_pktmbuf_free()``.
+
+The forwarding loop can be interrupted and the application closed using
+``Ctrl-C``.
+
+
+PTP parsing
+~~~~~~~~~~~
+
+The ``parse_ptp_frames()`` function processes PTP packets, implementing slave
+PTP IEEE1588 L2 functionality.
+
+.. code-block:: c
+
+    void
+    parse_ptp_frames(uint8_t portid, struct rte_mbuf *m) {
+        struct ptp_header *ptp_hdr;
+        struct ether_hdr *eth_hdr;
+        uint16_t eth_type;
+
+        eth_hdr = rte_pktmbuf_mtod(m, struct ether_hdr *);
+        eth_type = rte_be_to_cpu_16(eth_hdr->ether_type);
+
+        if (eth_type == PTP_PROTOCOL) {
+            ptp_data.m = m;
+            ptp_data.portid = portid;
+            ptp_hdr = (struct ptp_header *)(rte_pktmbuf_mtod(m, char *)
+                        + sizeof(struct ether_hdr));
+
+            switch (ptp_hdr->msgtype) {
+            case SYNC:
+                parse_sync(&ptp_data);
+                break;
+            case FOLLOW_UP:
+                parse_fup(&ptp_data);
+                break;
+            case DELAY_RESP:
+                parse_drsp(&ptp_data);
+                print_clock_info(&ptp_data);
+                break;
+            default:
+                break;
+            }
+        }
+    }
+
+There are 3 types of packets on the RX path which we must parse to create a minimal
+implementation of the PTP slave client:
+
+* SYNC packet.
+* FOLLOW UP packet
+* DELAY RESPONSE packet.
+
+When we parse the *FOLLOW UP* packet we also create and send a *DELAY_REQUEST* packet.
+Also when we parse the *DELAY RESPONSE* packet, and all conditions are met we adjust the PTP slave clock.
-- 
2.1.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [dpdk-dev] [PATCH v2 4/6] i40e: add additional ieee1588 support functions
  2015-10-30  9:43   ` [dpdk-dev] [PATCH v2 4/6] i40e: " Daniel Mrzyglod
@ 2015-10-30 11:19     ` Ananyev, Konstantin
  2015-10-30 11:33       ` De Lara Guarch, Pablo
  0 siblings, 1 reply; 84+ messages in thread
From: Ananyev, Konstantin @ 2015-10-30 11:19 UTC (permalink / raw)
  To: Mrzyglod, DanielX T, dev

Hi Daniel,

> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Daniel Mrzyglod
> Sent: Friday, October 30, 2015 9:43 AM
> To: dev@dpdk.org
> Subject: [dpdk-dev] [PATCH v2 4/6] i40e: add additional ieee1588 support functions
> 
> From: Pablo de Lara <pablo.de.lara.guarch@intel.com>
> 
> Add additional functions to support the existing IEEE1588
> functionality and to enable getting, setting and adjusting
> the device time.
> 
> Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
> Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
> ---
>  drivers/net/i40e/i40e_ethdev.c | 214 +++++++++++++++++++++++++++++++++++++----
>  drivers/net/i40e/i40e_ethdev.h |  18 ++++
>  2 files changed, 212 insertions(+), 20 deletions(-)
> 
> diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
> index 2dd9fdc..985ab24 100644
> --- a/drivers/net/i40e/i40e_ethdev.c
> +++ b/drivers/net/i40e/i40e_ethdev.c
> @@ -107,11 +107,14 @@
>  	(1UL << RTE_ETH_FLOW_NONFRAG_IPV6_OTHER) | \
>  	(1UL << RTE_ETH_FLOW_L2_PAYLOAD))
> 
> -#define I40E_PTP_40GB_INCVAL  0x0199999999ULL
> -#define I40E_PTP_10GB_INCVAL  0x0333333333ULL
> -#define I40E_PTP_1GB_INCVAL   0x2000000000ULL
> -#define I40E_PRTTSYN_TSYNENA  0x80000000
> -#define I40E_PRTTSYN_TSYNTYPE 0x0e000000
> +/* Additional timesync values. */
> +#define NSEC_PER_SEC             1000000000L
> +#define I40E_PTP_40GB_INCVAL     0x0199999999ULL
> +#define I40E_PTP_10GB_INCVAL     0x0333333333ULL
> +#define I40E_PTP_1GB_INCVAL      0x2000000000ULL
> +#define I40E_PRTTSYN_TSYNENA     0x80000000
> +#define I40E_PRTTSYN_TSYNTYPE    0x0e000000
> +#define I40E_CYCLECOUNTER_MASK   0xffffffffffffffff
> 
>  static int eth_i40e_dev_init(struct rte_eth_dev *eth_dev);
>  static int eth_i40e_dev_uninit(struct rte_eth_dev *eth_dev);
> @@ -232,6 +235,11 @@ static int i40e_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
>  					   uint32_t flags);
>  static int i40e_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
>  					   struct timespec *timestamp);
> +static int i40e_timesync_time_adjust(struct rte_eth_dev *dev, int64_t delta);
> +static int i40e_timesync_time_get(struct rte_eth_dev *dev,
> +		struct timespec *timestamp);
> +static int i40e_timesync_time_set(struct rte_eth_dev *dev,
> +		struct timespec *timestamp);
> 
>  static const struct rte_pci_id pci_id_i40e_map[] = {
>  #define RTE_PCI_DEV_ID_DECL_I40E(vend, dev) {RTE_PCI_DEVICE(vend, dev)},
> @@ -289,6 +297,9 @@ static const struct eth_dev_ops i40e_eth_dev_ops = {
>  	.timesync_disable             = i40e_timesync_disable,
>  	.timesync_read_rx_timestamp   = i40e_timesync_read_rx_timestamp,
>  	.timesync_read_tx_timestamp   = i40e_timesync_read_tx_timestamp,
> +	.timesync_time_adjust         = i40e_timesync_time_adjust,
> +	.timesync_time_get            = i40e_timesync_time_get,
> +	.timesync_time_set            = i40e_timesync_time_set,
>  };
> 
>  static struct eth_driver rte_i40e_pmd = {
> @@ -6175,17 +6186,116 @@ i40e_mirror_rule_reset(struct rte_eth_dev *dev, uint8_t sw_id)
>  	return 0;
>  }
> 
> -static int
> -i40e_timesync_enable(struct rte_eth_dev *dev)
> +static inline uint64_t
> +timespec_to_ns(const struct timespec *ts)
> +{
> +	return ((uint64_t) ts->tv_sec * NSEC_PER_SEC) + ts->tv_nsec;
> +}
> +
> +static struct timespec
> +ns_to_timespec(uint64_t nsec)
> +{
> +	struct timespec ts = {0, 0};
> +
> +	if (nsec == 0)
> +		return ts;
> +
> +	ts.tv_sec = nsec / NSEC_PER_SEC;
> +	ts.tv_nsec = nsec % NSEC_PER_SEC;
> +
> +	return ts;
> +}
> +
> +/*
> + * Adds the new cycles (in nanoseconds) to the previous time stored.
> + */
> +static uint64_t
> +timecounter_cycles_to_ns_time(struct timecounter *tc, uint64_t cycle_tstamp)
> +{
> +	uint64_t delta = (cycle_tstamp - tc->cycle_last);
> +	uint64_t nsec = tc->nsec;
> +
> +	nsec += delta;
> +
> +	return nsec;
> +}

These 3 functions above are redefined for each PMD in your patch.
I think it is worth to move it to the upper layer (rte_ethdev, rte_net) to avoid code duplication.
Thanks
Kinstantin

> +
> +static uint64_t
> +i40e_read_timesync_cyclecounter(struct rte_eth_dev *dev)
>  {
>  	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
> -	struct rte_eth_link *link = &dev->data->dev_link;
> -	uint32_t tsync_ctl_l;
> -	uint32_t tsync_ctl_h;
> +	uint64_t systim_cycles = 0;
> +
> +	systim_cycles |= (uint64_t)I40E_READ_REG(hw, I40E_PRTTSYN_TIME_L);
> +	systim_cycles |= (uint64_t)I40E_READ_REG(hw, I40E_PRTTSYN_TIME_H)
> +			<< 32;
> +
> +	return systim_cycles;
> +}
> +
> +static uint64_t
> +timecounter_read_ns_delta(struct rte_eth_dev *dev)
> +{
> +	uint64_t cycle_now, cycle_delta;
> +	struct i40e_adapter *adapter =
> +			(struct i40e_adapter *)dev->data->dev_private;
> +
> +	/* Read cycle counter. */
> +	cycle_now = adapter->tc.cc->read(dev);
> +
> +	/* Calculate the delta since the last timecounter_read_delta(). */
> +	cycle_delta = (cycle_now - adapter->tc.cycle_last);
> +
> +	/* Update time stamp of timecounter_read_delta() call. */
> +	adapter->tc.cycle_last = cycle_now;
> +
> +	/* Delta already in nanoseconds. */
> +	return cycle_delta;
> +}
> +
> +static uint64_t
> +timecounter_read(struct rte_eth_dev *dev)
> +{
> +	uint64_t nsec;
> +	struct i40e_adapter *adapter =
> +			(struct i40e_adapter *)dev->data->dev_private;
> +
> +	/* Increment time by nanoseconds since last call. */
> +	nsec = timecounter_read_ns_delta(dev);
> +	nsec += adapter->tc.nsec;
> +	adapter->tc.nsec = nsec;
> +
> +	return nsec;
> +}
> +
> +
> +static void
> +timecounter_init(struct rte_eth_dev *dev,
> +		      uint64_t start_time)
> +{
> +	struct i40e_adapter *adapter =
> +			(struct i40e_adapter *)dev->data->dev_private;
> +	adapter->tc.cc = &adapter->cc;
> +	adapter->tc.cycle_last = adapter->tc.cc->read(dev);
> +	adapter->tc.nsec = start_time;
> +}
> +
> +static void
> +i40e_start_cyclecounter(struct rte_eth_dev *dev)
> +{
> +	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
> +	struct i40e_adapter *adapter =
> +			(struct i40e_adapter *)dev->data->dev_private;
> +	struct rte_eth_link link;
>  	uint32_t tsync_inc_l;
>  	uint32_t tsync_inc_h;
> 
> -	switch (link->link_speed) {
> +	/* Get current link speed. */
> +	memset(&link, 0, sizeof(link));
> +	i40e_dev_link_update(dev, 1);
> +	rte_i40e_dev_atomic_read_link_status(dev, &link);
> +
> +	switch (link.link_speed) {
>  	case ETH_LINK_SPEED_40G:
>  		tsync_inc_l = I40E_PTP_40GB_INCVAL & 0xFFFFFFFF;
>  		tsync_inc_h = I40E_PTP_40GB_INCVAL >> 32;
> @@ -6203,6 +6313,63 @@ i40e_timesync_enable(struct rte_eth_dev *dev)
>  		tsync_inc_h = 0x0;
>  	}
> 
> +	/* Set the timesync increment value. */
> +	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_L, tsync_inc_l);
> +	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_H, tsync_inc_h);
> +
> +	memset(&adapter->cc, 0, sizeof(struct cyclecounter));
> +	adapter->cc.read = i40e_read_timesync_cyclecounter;
> +}
> +
> +static int
> +i40e_timesync_time_adjust(struct rte_eth_dev *dev, int64_t delta)
> +{
> +	struct i40e_adapter *adapter =
> +			(struct i40e_adapter *)dev->data->dev_private;
> +
> +	adapter->tc.nsec += delta;
> +
> +	return 0;
> +}
> +
> +static int
> +i40e_timesync_time_set(struct rte_eth_dev *dev, struct timespec *ts)
> +{
> +	uint64_t ns;
> +
> +	ns = timespec_to_ns(ts);
> +
> +	/* Reset the timecounter. */
> +	timecounter_init(dev, ns);
> +
> +	return 0;
> +}
> +
> +static int
> +i40e_timesync_time_get(struct rte_eth_dev *dev, struct timespec *ts)
> +{
> +	uint64_t ns;
> +
> +	ns = timecounter_read(dev);
> +	*ts = ns_to_timespec(ns);
> +
> +	return 0;
> +}
> +
> +static int
> +i40e_timesync_enable(struct rte_eth_dev *dev)
> +{
> +	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
> +	uint32_t tsync_ctl_l;
> +	uint32_t tsync_ctl_h;
> +	uint64_t ns;
> +	struct timespec zerotime = {0, 0};
> +
> +	/* Set 0.0 epoch time to initialize timecounter. */
> +	ns = timespec_to_ns(&zerotime);
> +	i40e_start_cyclecounter(dev);
> +	timecounter_init(dev, ns);
> +
>  	/* Clear timesync registers. */
>  	I40E_READ_REG(hw, I40E_PRTTSYN_STAT_0);
>  	I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_H);
> @@ -6212,10 +6379,6 @@ i40e_timesync_enable(struct rte_eth_dev *dev)
>  	I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_L(3));
>  	I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_H);
> 
> -	/* Set the timesync increment value. */
> -	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_L, tsync_inc_l);
> -	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_H, tsync_inc_h);
> -
>  	/* Enable timestamping of PTP packets. */
>  	tsync_ctl_l = I40E_READ_REG(hw, I40E_PRTTSYN_CTL0);
>  	tsync_ctl_l |= I40E_PRTTSYN_TSYNENA;
> @@ -6247,7 +6410,7 @@ i40e_timesync_disable(struct rte_eth_dev *dev)
>  	I40E_WRITE_REG(hw, I40E_PRTTSYN_CTL0, tsync_ctl_l);
>  	I40E_WRITE_REG(hw, I40E_PRTTSYN_CTL1, tsync_ctl_h);
> 
> -	/* Set the timesync increment value. */
> +	/* Reset the timesync increment value. */
>  	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_L, 0x0);
>  	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_H, 0x0);
> 
> @@ -6259,10 +6422,14 @@ i40e_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
>  				struct timespec *timestamp, uint32_t flags)
>  {
>  	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
> +	struct i40e_adapter *adapter =
> +		(struct i40e_adapter *)dev->data->dev_private;
> +
>  	uint32_t sync_status;
>  	uint32_t rx_stmpl;
>  	uint32_t rx_stmph;
>  	uint32_t index = flags & 0x03;
> +	uint64_t regival = 0;
> 
>  	sync_status = I40E_READ_REG(hw, I40E_PRTTSYN_STAT_1);
>  	if ((sync_status & (1 << index)) == 0)
> @@ -6270,9 +6437,11 @@ i40e_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
> 
>  	rx_stmpl = I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_L(index));
>  	rx_stmph = I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_H(index));
> +	timecounter_read(dev);
> 
> -	timestamp->tv_sec = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
> -	timestamp->tv_nsec = 0;
> +	regival = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
> +	regival = timecounter_cycles_to_ns_time(&adapter->tc, regival);
> +	*timestamp = ns_to_timespec(regival);
> 
>  	return  0;
>  }
> @@ -6282,9 +6451,13 @@ i40e_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
>  				struct timespec *timestamp)
>  {
>  	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
> +	struct i40e_adapter *adapter =
> +		(struct i40e_adapter *)dev->data->dev_private;
> +
>  	uint32_t sync_status;
>  	uint32_t tx_stmpl;
>  	uint32_t tx_stmph;
> +	uint64_t regival = 0;
> 
>  	sync_status = I40E_READ_REG(hw, I40E_PRTTSYN_STAT_0);
>  	if ((sync_status & I40E_PRTTSYN_STAT_0_TXTIME_MASK) == 0)
> @@ -6293,8 +6466,9 @@ i40e_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
>  	tx_stmpl = I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_L);
>  	tx_stmph = I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_H);
> 
> -	timestamp->tv_sec = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
> -	timestamp->tv_nsec = 0;
> +	regival = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
> +	regival = timecounter_cycles_to_ns_time(&adapter->tc, regival);
> +	*timestamp = ns_to_timespec(regival);
> 
>  	return  0;
>  }
> diff --git a/drivers/net/i40e/i40e_ethdev.h b/drivers/net/i40e/i40e_ethdev.h
> index 6185657..7f2d79b 100644
> --- a/drivers/net/i40e/i40e_ethdev.h
> +++ b/drivers/net/i40e/i40e_ethdev.h
> @@ -450,6 +450,22 @@ struct i40e_vf {
>  };
> 
>  /*
> + * Structure for cyclecounter IEEE1588 functionality.
> + */
> +struct cyclecounter {
> +	uint64_t (*read)(struct rte_eth_dev *dev);
> +};
> +
> +/*
> + * Structure to hold and calculate Unix epoch time.
> + */
> +struct timecounter {
> +	struct cyclecounter *cc;
> +	uint64_t cycle_last;
> +	uint64_t nsec;
> +};
> +
> +/*
>   * Structure to store private data for each PF/VF instance.
>   */
>  struct i40e_adapter {
> @@ -462,6 +478,8 @@ struct i40e_adapter {
>  		struct i40e_pf pf;
>  		struct i40e_vf vf;
>  	};
> +	struct cyclecounter cc;
> +	struct timecounter tc;
>  };
> 
>  int i40e_dev_switch_queues(struct i40e_pf *pf, bool on);
> --
> 2.1.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [dpdk-dev] [PATCH v2 0/6] add sample ptp slave application
  2015-10-30  9:43 ` [dpdk-dev] [PATCH v2 0/6] add sample ptp slave application Daniel Mrzyglod
                     ` (5 preceding siblings ...)
  2015-10-30  9:43   ` [dpdk-dev] [PATCH v2 6/6] doc: add a PTPCLIENT sample guide Daniel Mrzyglod
@ 2015-10-30 11:23   ` Mcnamara, John
  6 siblings, 0 replies; 84+ messages in thread
From: Mcnamara, John @ 2015-10-30 11:23 UTC (permalink / raw)
  To: Mrzyglod, DanielX T, dev

> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Daniel Mrzyglod
> Sent: Friday, October 30, 2015 9:43 AM
> To: dev@dpdk.org
> Subject: [dpdk-dev] [PATCH v2 0/6] add sample ptp slave application
> 
> Add a sample application that acts as a PTP slave using the DPDK IEEE1588
> functions.

Series Acked-by: John McNamara <john.mcnamara@intel.com>

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [dpdk-dev] [PATCH v2 4/6] i40e: add additional ieee1588 support functions
  2015-10-30 11:19     ` Ananyev, Konstantin
@ 2015-10-30 11:33       ` De Lara Guarch, Pablo
  2015-10-30 11:36         ` Thomas Monjalon
  2015-10-30 11:38         ` Ananyev, Konstantin
  0 siblings, 2 replies; 84+ messages in thread
From: De Lara Guarch, Pablo @ 2015-10-30 11:33 UTC (permalink / raw)
  To: Ananyev, Konstantin, Mrzyglod, DanielX T, dev

Hi Konstantin,

> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Ananyev,
> Konstantin
> Sent: Friday, October 30, 2015 11:20 AM
> To: Mrzyglod, DanielX T; dev@dpdk.org
> Subject: Re: [dpdk-dev] [PATCH v2 4/6] i40e: add additional ieee1588 support
> functions
> 
> Hi Daniel,
> 
> > -----Original Message-----
> > From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Daniel Mrzyglod
> > Sent: Friday, October 30, 2015 9:43 AM
> > To: dev@dpdk.org
> > Subject: [dpdk-dev] [PATCH v2 4/6] i40e: add additional ieee1588 support
> functions
> >
> > From: Pablo de Lara <pablo.de.lara.guarch@intel.com>
> >
> > Add additional functions to support the existing IEEE1588
> > functionality and to enable getting, setting and adjusting
> > the device time.
> >
> > Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
> > Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>

[...]

> 
> These 3 functions above are redefined for each PMD in your patch.
> I think it is worth to move it to the upper layer (rte_ethdev, rte_net) to avoid
> code duplication.
> Thanks
> Kinstantin

We thought about that, but we didn't know what the best place was for them.
Looking at the functions in rte_ethdev, they look quite different compared to these ones,
so we thought it wasn't the appropriate place. Do you think that is the best place?

Thanks,
Pablo

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [dpdk-dev] [PATCH v2 4/6] i40e: add additional ieee1588 support functions
  2015-10-30 11:33       ` De Lara Guarch, Pablo
@ 2015-10-30 11:36         ` Thomas Monjalon
  2015-10-30 11:38         ` Ananyev, Konstantin
  1 sibling, 0 replies; 84+ messages in thread
From: Thomas Monjalon @ 2015-10-30 11:36 UTC (permalink / raw)
  To: De Lara Guarch, Pablo, Ananyev, Konstantin; +Cc: dev

2015-10-30 11:33, De Lara Guarch, Pablo:
> > These 3 functions above are redefined for each PMD in your patch.
> > I think it is worth to move it to the upper layer (rte_ethdev, rte_net) to avoid
> > code duplication.
> > Thanks
> > Kinstantin
> 
> We thought about that, but we didn't know what the best place was for them.
> Looking at the functions in rte_ethdev, they look quite different compared to these ones,
> so we thought it wasn't the appropriate place. Do you think that is the best place?

You are talking about the timer conversions, right?
It looks appropriate for EAL.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [dpdk-dev] [PATCH v2 4/6] i40e: add additional ieee1588 support functions
  2015-10-30 11:33       ` De Lara Guarch, Pablo
  2015-10-30 11:36         ` Thomas Monjalon
@ 2015-10-30 11:38         ` Ananyev, Konstantin
  1 sibling, 0 replies; 84+ messages in thread
From: Ananyev, Konstantin @ 2015-10-30 11:38 UTC (permalink / raw)
  To: De Lara Guarch, Pablo, Mrzyglod, DanielX T, dev



> -----Original Message-----
> From: De Lara Guarch, Pablo
> Sent: Friday, October 30, 2015 11:33 AM
> To: Ananyev, Konstantin; Mrzyglod, DanielX T; dev@dpdk.org
> Subject: RE: [dpdk-dev] [PATCH v2 4/6] i40e: add additional ieee1588 support functions
> 
> Hi Konstantin,
> 
> > -----Original Message-----
> > From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Ananyev,
> > Konstantin
> > Sent: Friday, October 30, 2015 11:20 AM
> > To: Mrzyglod, DanielX T; dev@dpdk.org
> > Subject: Re: [dpdk-dev] [PATCH v2 4/6] i40e: add additional ieee1588 support
> > functions
> >
> > Hi Daniel,
> >
> > > -----Original Message-----
> > > From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Daniel Mrzyglod
> > > Sent: Friday, October 30, 2015 9:43 AM
> > > To: dev@dpdk.org
> > > Subject: [dpdk-dev] [PATCH v2 4/6] i40e: add additional ieee1588 support
> > functions
> > >
> > > From: Pablo de Lara <pablo.de.lara.guarch@intel.com>
> > >
> > > Add additional functions to support the existing IEEE1588
> > > functionality and to enable getting, setting and adjusting
> > > the device time.
> > >
> > > Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
> > > Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
> 
> [...]
> 
> >
> > These 3 functions above are redefined for each PMD in your patch.
> > I think it is worth to move it to the upper layer (rte_ethdev, rte_net) to avoid
> > code duplication.
> > Thanks
> > Kinstantin
> 
> We thought about that, but we didn't know what the best place was for them.
> Looking at the functions in rte_ethdev, they look quite different compared to these ones,
> so we thought it wasn't the appropriate place. Do you think that is the best place

Inisde librte_net might be?
They are sort of helper functions for PTP right?
So might be create a librte_net/rte_ptp.h and put it here?
Konstantin

> 
> Thanks,
> Pablo

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v3 0/7] add sample ptp slave application
  2015-10-02 15:20 [dpdk-dev] [PATCH 0/3] add sample ptp slave application Daniel Mrzyglod
                   ` (3 preceding siblings ...)
  2015-10-30  9:43 ` [dpdk-dev] [PATCH v2 0/6] add sample ptp slave application Daniel Mrzyglod
@ 2015-11-03 16:38 ` Daniel Mrzyglod
  2015-11-03 16:38   ` [dpdk-dev] [PATCH v3 1/7] ethdev: add additional ieee1588 support functions Daniel Mrzyglod
                     ` (6 more replies)
  2015-11-04 10:06 ` [dpdk-dev] [PATCH v4 0/7] add sample ptp slave application Daniel Mrzyglod
  2015-11-05 14:05 ` [dpdk-dev] [PATCH v5 " Daniel Mrzyglod
  6 siblings, 7 replies; 84+ messages in thread
From: Daniel Mrzyglod @ 2015-11-03 16:38 UTC (permalink / raw)
  To: dev

Add a sample application that acts as a PTP slave using the DPDK IEEE1588
functions.

Also add some additional IEEE1588 support functions to enable getting,
setting and adjusting the device time.

V2->V3:
PMD:
 - move common structures and functions for PTP protocol to librte_net/rte_ptp.h

V1->V2:
PMDs:
 - add support for e1000
 - add support for ixgbe
 - add support for i40
ethdev:
 - change function names to more proper.
Doc:
 - add documentation for ptpclient
sample:
 - add kernel adjustment option
 - add portmask option to provide portmask to aplication


Daniel Mrzyglod (5):
  ethdev: add additional ieee1588 support functions
  net: Add common PTP structures and functions
  ixgbe: add additional ieee1588 support functions
  example: PTP client slave minimal implementation
  doc: add a PTPCLIENT sample guide

Pablo de Lara (2):
  igb: add additional ieee1588 support functions
  i40e: add additional ieee1588 support functions

 MAINTAINERS                                |   3 +
 doc/guides/rel_notes/release_2_2.rst       |   8 +
 doc/guides/sample_app_ug/img/ptpclient.svg | 520 +++++++++++++++++++
 doc/guides/sample_app_ug/index.rst         |   3 +
 doc/guides/sample_app_ug/ptpclient.rst     | 324 ++++++++++++
 drivers/net/e1000/e1000_ethdev.h           |   3 +
 drivers/net/e1000/igb_ethdev.c             | 299 ++++++++++-
 drivers/net/i40e/i40e_ethdev.c             | 192 ++++++-
 drivers/net/i40e/i40e_ethdev.h             |   5 +
 drivers/net/ixgbe/ixgbe_ethdev.c           | 272 +++++++++-
 drivers/net/ixgbe/ixgbe_ethdev.h           |   3 +
 examples/Makefile                          |   1 +
 examples/ptpclient/Makefile                |  56 +++
 examples/ptpclient/ptpclient.c             | 779 +++++++++++++++++++++++++++++
 lib/librte_ether/rte_ethdev.c              |  36 ++
 lib/librte_ether/rte_ethdev.h              |  64 +++
 lib/librte_ether/rte_ether_version.map     |   3 +
 lib/librte_net/Makefile                    |   2 +-
 lib/librte_net/rte_ptp.h                   | 105 ++++
 19 files changed, 2636 insertions(+), 42 deletions(-)
 create mode 100644 doc/guides/sample_app_ug/img/ptpclient.svg
 create mode 100644 doc/guides/sample_app_ug/ptpclient.rst
 create mode 100644 examples/ptpclient/Makefile
 create mode 100644 examples/ptpclient/ptpclient.c
 create mode 100644 lib/librte_net/rte_ptp.h

-- 
2.1.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v3 1/7] ethdev: add additional ieee1588 support functions
  2015-11-03 16:38 ` [dpdk-dev] [PATCH v3 0/7] " Daniel Mrzyglod
@ 2015-11-03 16:38   ` Daniel Mrzyglod
  2015-11-03 16:38   ` [dpdk-dev] [PATCH v3 2/7] net: Add common PTP structures and functions Daniel Mrzyglod
                     ` (5 subsequent siblings)
  6 siblings, 0 replies; 84+ messages in thread
From: Daniel Mrzyglod @ 2015-11-03 16:38 UTC (permalink / raw)
  To: dev

Add additional functions to support the existing IEEE1588
functionality.

* rte_eth_timesync_settime(), function to set the device clock time.
* rte_eth_timesync_gettime, function to get the device clock time.
* rte_eth_timesync_adjust, function to adjust the device clock time.

Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
---
 doc/guides/rel_notes/release_2_2.rst   |  3 ++
 lib/librte_ether/rte_ethdev.c          | 36 +++++++++++++++++++
 lib/librte_ether/rte_ethdev.h          | 64 ++++++++++++++++++++++++++++++++++
 lib/librte_ether/rte_ether_version.map |  3 ++
 4 files changed, 106 insertions(+)

diff --git a/doc/guides/rel_notes/release_2_2.rst b/doc/guides/rel_notes/release_2_2.rst
index 16fcc89..09fd642 100644
--- a/doc/guides/rel_notes/release_2_2.rst
+++ b/doc/guides/rel_notes/release_2_2.rst
@@ -176,6 +176,9 @@ API Changes
 
 * The devargs union field virtual is renamed to virt for C++ compatibility.
 
+* Add new functions in ethdev to support IEEE1588: rte_eth_timesync_time_adjust()
+  rte_eth_timesync_time_get(), rte_eth_timesync_time_set()
+
 
 ABI Changes
 -----------
diff --git a/lib/librte_ether/rte_ethdev.c b/lib/librte_ether/rte_ethdev.c
index 58aaeb2..a1e7eac 100644
--- a/lib/librte_ether/rte_ethdev.c
+++ b/lib/librte_ether/rte_ethdev.c
@@ -3171,6 +3171,42 @@ rte_eth_timesync_read_tx_timestamp(uint8_t port_id, struct timespec *timestamp)
 }
 
 int
+rte_eth_timesync_time_adjust(uint8_t port_id, int64_t delta)
+{
+	struct rte_eth_dev *dev;
+
+	VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	FUNC_PTR_OR_ERR_RET(*dev->dev_ops->timesync_time_adjust, -ENOTSUP);
+	return (*dev->dev_ops->timesync_time_adjust)(dev, delta);
+}
+
+int
+rte_eth_timesync_time_get(uint8_t port_id, struct timespec *timestamp)
+{
+	struct rte_eth_dev *dev;
+
+	VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	FUNC_PTR_OR_ERR_RET(*dev->dev_ops->timesync_time_get, -ENOTSUP);
+	return (*dev->dev_ops->timesync_time_get)(dev, timestamp);
+}
+
+int
+rte_eth_timesync_time_set(uint8_t port_id, struct timespec *timestamp)
+{
+	struct rte_eth_dev *dev;
+
+	VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	FUNC_PTR_OR_ERR_RET(*dev->dev_ops->timesync_time_set, -ENOTSUP);
+	return (*dev->dev_ops->timesync_time_set)(dev, timestamp);
+}
+
+int
 rte_eth_dev_get_reg_length(uint8_t port_id)
 {
 	struct rte_eth_dev *dev;
diff --git a/lib/librte_ether/rte_ethdev.h b/lib/librte_ether/rte_ethdev.h
index 7cf4af8..75cc742 100644
--- a/lib/librte_ether/rte_ethdev.h
+++ b/lib/librte_ether/rte_ethdev.h
@@ -1200,6 +1200,17 @@ typedef int (*eth_timesync_read_tx_timestamp_t)(struct rte_eth_dev *dev,
 						struct timespec *timestamp);
 /**< @internal Function used to read a TX IEEE1588/802.1AS timestamp. */
 
+typedef int (*eth_timesync_time_adjust)(struct rte_eth_dev *dev, int64_t);
+/**< @internal Function used to adjust device clock */
+
+typedef int (*eth_timesync_time_get)(struct rte_eth_dev *dev,
+						struct timespec *timestamp);
+/**< @internal Function used to get time from device clock. */
+
+typedef int (*eth_timesync_time_set)(struct rte_eth_dev *dev,
+						struct timespec *timestamp);
+/**< @internal Function used to get time from device clock */
+
 typedef int (*eth_get_reg_length_t)(struct rte_eth_dev *dev);
 /**< @internal Retrieve device register count  */
 
@@ -1394,6 +1405,12 @@ struct eth_dev_ops {
 
 	/** Get DCB information */
 	eth_get_dcb_info get_dcb_info;
+	/** Adjust the device clock */
+	eth_timesync_time_adjust timesync_time_adjust;
+	/** Get the device clock timespec */
+	eth_timesync_time_get timesync_time_get;
+	/** Set the device clock timespec */
+	eth_timesync_time_set timesync_time_set;
 };
 
 /**
@@ -3735,6 +3752,53 @@ extern int rte_eth_timesync_read_rx_timestamp(uint8_t port_id,
 extern int rte_eth_timesync_read_tx_timestamp(uint8_t port_id,
 					      struct timespec *timestamp);
 
+/**
+ * Adjust the timesync clock on an Ethernet device..
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param delta
+ *   The adjustment in nanoseconds
+ *
+ * @return
+ *   - 0: Success.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -ENOTSUP: The function is not supported by the Ethernet driver.
+ */
+extern int rte_eth_timesync_time_adjust(uint8_t port_id, int64_t delta);
+
+/**
+ * Read the time from the timesync clock on an Ethernet device.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param time
+ *   Pointer to the timespec struct.
+ *
+ * @return
+ *   - 0: Success.
+ */
+extern int rte_eth_timesync_time_get(uint8_t port_id,
+	      struct timespec *time);
+
+
+/**
+ * Set the time of the timesync clock on an Ethernet device.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param time
+ *   Pointer to the timespec struct.
+ *
+ * @return
+ *   - 0: Success.
+ *   - -EINVAL: No timestamp is available.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -ENOTSUP: The function is not supported by the Ethernet driver.
+ */
+extern int rte_eth_timesync_time_set(uint8_t port_id,
+	      struct timespec *time);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/librte_ether/rte_ether_version.map b/lib/librte_ether/rte_ether_version.map
index 7b04e95..24d5e0a 100644
--- a/lib/librte_ether/rte_ether_version.map
+++ b/lib/librte_ether/rte_ether_version.map
@@ -134,5 +134,8 @@ DPDK_2.2 {
 	rte_eth_dev_get_dcb_info;
 	rte_eth_rx_queue_info_get;
 	rte_eth_tx_queue_info_get;
+	rte_eth_timesync_time_adjust;
+	rte_eth_timesync_time_get;
+	rte_eth_timesync_time_set;
 
 } DPDK_2.1;
-- 
2.1.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v3 2/7] net: Add common PTP structures and functions
  2015-11-03 16:38 ` [dpdk-dev] [PATCH v3 0/7] " Daniel Mrzyglod
  2015-11-03 16:38   ` [dpdk-dev] [PATCH v3 1/7] ethdev: add additional ieee1588 support functions Daniel Mrzyglod
@ 2015-11-03 16:38   ` Daniel Mrzyglod
  2015-11-03 16:38   ` [dpdk-dev] [PATCH v3 3/7] ixgbe: add additional ieee1588 support functions Daniel Mrzyglod
                     ` (4 subsequent siblings)
  6 siblings, 0 replies; 84+ messages in thread
From: Daniel Mrzyglod @ 2015-11-03 16:38 UTC (permalink / raw)
  To: dev

This patch add common functions and structures used for PTP processing.

Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
---
 lib/librte_net/Makefile  |   2 +-
 lib/librte_net/rte_ptp.h | 105 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 106 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_net/rte_ptp.h

diff --git a/lib/librte_net/Makefile b/lib/librte_net/Makefile
index ad2e482..1d33618 100644
--- a/lib/librte_net/Makefile
+++ b/lib/librte_net/Makefile
@@ -34,7 +34,7 @@ include $(RTE_SDK)/mk/rte.vars.mk
 CFLAGS += $(WERROR_FLAGS) -I$(SRCDIR) -O3
 
 # install includes
-SYMLINK-$(CONFIG_RTE_LIBRTE_NET)-include := rte_ip.h rte_tcp.h rte_udp.h rte_sctp.h rte_icmp.h rte_arp.h
+SYMLINK-$(CONFIG_RTE_LIBRTE_NET)-include := rte_ip.h rte_tcp.h rte_udp.h rte_sctp.h rte_icmp.h rte_arp.h rte_ptp.h
 
 
 include $(RTE_SDK)/mk/rte.install.mk
diff --git a/lib/librte_net/rte_ptp.h b/lib/librte_net/rte_ptp.h
new file mode 100644
index 0000000..8a4c83c
--- /dev/null
+++ b/lib/librte_net/rte_ptp.h
@@ -0,0 +1,105 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define NSEC_PER_SEC             1000000000L
+
+/*
+ * Structure for cyclecounter IEEE1588 functionality.
+ */
+struct cyclecounter {
+	uint64_t (*read)(struct rte_eth_dev *dev);
+	uint64_t mask;
+	uint32_t shift;
+};
+
+/*
+ * Structure to hold and calculate Unix epoch time.
+ */
+struct timecounter {
+	struct cyclecounter *cc;
+	uint64_t cycle_last;
+	uint64_t nsec;
+	uint64_t mask;
+	uint64_t frac;
+};
+
+
+/* Utility functions for PTP/IEEE1588 support. */
+
+static inline uint64_t
+timespec_to_ns(const struct timespec *ts)
+{
+	return ((uint64_t) ts->tv_sec * NSEC_PER_SEC) + ts->tv_nsec;
+}
+
+static inline struct timespec
+ns_to_timespec(uint64_t nsec)
+{
+	struct timespec ts = {0, 0};
+
+	if (nsec == 0)
+		return ts;
+
+	ts.tv_sec = nsec / NSEC_PER_SEC;
+	ts.tv_nsec = nsec % NSEC_PER_SEC;
+
+	return ts;
+}
+
+/*
+ * Converts cycle counter cycles to nanoseconds.
+ */
+static inline uint64_t
+cyclecounter_cycles_to_ns(const struct cyclecounter *cc,
+		      uint64_t cycles, uint64_t mask, uint64_t *frac)
+{
+	uint64_t ns;
+
+	/* Add fractional nanoseconds */
+	ns = cycles + *frac;
+	*frac = ns & mask;
+
+	/* Shift to get only nanoseconds. */
+	return ns >> cc->shift;
+}
+
+/*
+ * Like cyclecounter_cycles_to_ns(), but this is used when
+ * computing a time previous to the stored in the cycle counter.
+ */
+static inline uint64_t
+cyclecounter_cycles_to_ns_backwards(const struct cyclecounter *cc,
+			       uint64_t cycles, uint64_t frac)
+{
+	return ((cycles - frac) >> cc->shift);
+}
-- 
2.1.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v3 3/7] ixgbe: add additional ieee1588 support functions
  2015-11-03 16:38 ` [dpdk-dev] [PATCH v3 0/7] " Daniel Mrzyglod
  2015-11-03 16:38   ` [dpdk-dev] [PATCH v3 1/7] ethdev: add additional ieee1588 support functions Daniel Mrzyglod
  2015-11-03 16:38   ` [dpdk-dev] [PATCH v3 2/7] net: Add common PTP structures and functions Daniel Mrzyglod
@ 2015-11-03 16:38   ` Daniel Mrzyglod
  2015-11-03 16:38   ` [dpdk-dev] [PATCH v3 4/7] igb: " Daniel Mrzyglod
                     ` (3 subsequent siblings)
  6 siblings, 0 replies; 84+ messages in thread
From: Daniel Mrzyglod @ 2015-11-03 16:38 UTC (permalink / raw)
  To: dev

Add additional functions to support the existing IEEE1588
functionality and to enable getting, setting and adjusting
the device time.

Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
---
 drivers/net/ixgbe/ixgbe_ethdev.c | 272 +++++++++++++++++++++++++++++++++++++--
 drivers/net/ixgbe/ixgbe_ethdev.h |   3 +
 2 files changed, 264 insertions(+), 11 deletions(-)

diff --git a/drivers/net/ixgbe/ixgbe_ethdev.c b/drivers/net/ixgbe/ixgbe_ethdev.c
index 52c2fdb..595d75c 100644
--- a/drivers/net/ixgbe/ixgbe_ethdev.c
+++ b/drivers/net/ixgbe/ixgbe_ethdev.c
@@ -126,10 +126,17 @@
 #define IXGBE_HKEY_MAX_INDEX 10
 
 /* Additional timesync values. */
-#define IXGBE_TIMINCA_16NS_SHIFT 24
-#define IXGBE_TIMINCA_INCVALUE   16000000
-#define IXGBE_TIMINCA_INIT       ((0x02 << IXGBE_TIMINCA_16NS_SHIFT) \
-				  | IXGBE_TIMINCA_INCVALUE)
+#define NSEC_PER_SEC             1000000000L
+#define IXGBE_INCVAL_10GB        0x66666666
+#define IXGBE_INCVAL_1GB         0x40000000
+#define IXGBE_INCVAL_100         0x50000000
+#define IXGBE_INCVAL_SHIFT_10GB  28
+#define IXGBE_INCVAL_SHIFT_1GB   24
+#define IXGBE_INCVAL_SHIFT_100   21
+#define IXGBE_INCVAL_SHIFT_82599 7
+#define IXGBE_INCPER_SHIFT_82599 24
+
+#define IXGBE_CYCLECOUNTER_MASK   0xffffffffffffffff
 
 static int eth_ixgbe_dev_init(struct rte_eth_dev *eth_dev);
 static int eth_ixgbe_dev_uninit(struct rte_eth_dev *eth_dev);
@@ -329,6 +336,11 @@ static int ixgbe_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 					    uint32_t flags);
 static int ixgbe_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 					    struct timespec *timestamp);
+static int ixgbe_timesync_time_adjust(struct rte_eth_dev *dev, int64_t delta);
+static int ixgbe_timesync_time_get(struct rte_eth_dev *dev,
+				   struct timespec *timestamp);
+static int ixgbe_timesync_time_set(struct rte_eth_dev *dev,
+				   struct timespec *timestamp);
 
 /*
  * Define VF Stats MACRO for Non "cleared on read" register
@@ -484,6 +496,9 @@ static const struct eth_dev_ops ixgbe_eth_dev_ops = {
 	.get_eeprom           = ixgbe_get_eeprom,
 	.set_eeprom           = ixgbe_set_eeprom,
 	.get_dcb_info         = ixgbe_dev_get_dcb_info,
+	.timesync_time_adjust = ixgbe_timesync_time_adjust,
+	.timesync_time_get    = ixgbe_timesync_time_get,
+	.timesync_time_set    = ixgbe_timesync_time_set,
 };
 
 /*
@@ -5650,20 +5665,232 @@ ixgbe_dev_set_mc_addr_list(struct rte_eth_dev *dev,
 					 ixgbe_dev_addr_list_itr, TRUE);
 }
 
+/*
+ * Register units might not be nanoseconds. This function converts
+ * these units into nanoseconds and adds to the previous time stored.
+ */
+static uint64_t
+timecounter_cycles_to_ns_time(struct timecounter *tc, uint64_t cycle_tstamp)
+{
+	uint64_t delta;
+	uint64_t nsec = tc->nsec, frac = tc->frac;
+
+	delta = (cycle_tstamp - tc->cycle_last) & tc->cc->mask;
+	/*
+	 * Cycle counts that are correctly converted as they
+	 * are between -1/2 max cycle count and +1/2 max cycle count.
+	 */
+	if (delta > (tc->cc->mask / 2)) {
+		delta = (tc->cycle_last - cycle_tstamp) & tc->cc->mask;
+		nsec -= cyclecounter_cycles_to_ns_backwards(tc->cc,
+							    delta, frac);
+	} else {
+		nsec += cyclecounter_cycles_to_ns(tc->cc, delta, tc->mask,
+						  &frac);
+	}
+
+	return nsec;
+}
+
+static uint64_t
+ixgbe_read_timesync_cyclecounter(struct rte_eth_dev *dev)
+{
+	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	uint64_t systime_cycles = 0;
+
+	switch (hw->mac.type) {
+	case ixgbe_mac_X550:
+		/* SYSTIMEL stores ns and SYSTIMEH stores seconds. */
+		systime_cycles = (uint64_t)IXGBE_READ_REG(hw, IXGBE_SYSTIML);
+		systime_cycles += (uint64_t)IXGBE_READ_REG(hw, IXGBE_SYSTIMH)
+				* NSEC_PER_SEC;
+		break;
+	default:
+		systime_cycles |= (uint64_t)IXGBE_READ_REG(hw, IXGBE_SYSTIML);
+		systime_cycles |= (uint64_t)IXGBE_READ_REG(hw, IXGBE_SYSTIMH)
+				<< 32;
+	}
+
+	return systime_cycles;
+}
+
+/*
+ * Get nanoseconds since the last call of this function.
+ */
+static uint64_t
+timecounter_read_ns_delta(struct rte_eth_dev *dev)
+{
+	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	uint64_t cycle_now, cycle_delta;
+	uint64_t ns_offset;
+	struct ixgbe_adapter *adapter =
+				(struct ixgbe_adapter *)dev->data->dev_private;
+
+	/* Read cycle counter. */
+	cycle_now = adapter->tc.cc->read(dev);
+
+	/* Calculate the delta since the last timecounter_read_delta(). */
+	cycle_delta = (cycle_now - adapter->tc.cycle_last) &
+		       adapter->tc.cc->mask;
+
+	/* Convert to nanoseconds. */
+	if (hw->mac.type == ixgbe_mac_X550)
+		/* Registers store directly nanoseconds, no need to convert. */
+		ns_offset = cycle_delta;
+	else
+		ns_offset = cyclecounter_cycles_to_ns(adapter->tc.cc,
+						      cycle_delta,
+						      adapter->tc.mask,
+						      &adapter->tc.frac);
+
+	/*
+	 * Store current cycle counter for next
+	 * timecounter_read_ns_delta() call.
+	 */
+	adapter->tc.cycle_last = cycle_now;
+
+	return ns_offset;
+}
+
+static uint64_t
+timecounter_read(struct rte_eth_dev *dev)
+{
+	uint64_t nsec;
+	struct ixgbe_adapter *adapter =
+			(struct ixgbe_adapter *)dev->data->dev_private;
+
+	/* Increment time by nanoseconds since last call. */
+	nsec = timecounter_read_ns_delta(dev);
+	nsec += adapter->tc.nsec;
+	adapter->tc.nsec = nsec;
+
+	return nsec;
+}
+
+static void
+timecounter_init(struct rte_eth_dev *dev, uint64_t start_time)
+{
+	struct ixgbe_adapter *adapter =
+				(struct ixgbe_adapter *)dev->data->dev_private;
+
+	adapter->tc.cc = &adapter->cc;
+	adapter->tc.cycle_last = adapter->tc.cc->read(dev);
+	adapter->tc.nsec = start_time;
+	adapter->tc.mask = (1ULL << adapter->tc.cc->shift) - 1;
+	adapter->tc.frac = 0;
+}
+
+static void
+ixgbe_start_cyclecounter(struct rte_eth_dev *dev)
+{
+	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct ixgbe_adapter *adapter =
+		(struct ixgbe_adapter *)dev->data->dev_private;
+	struct rte_eth_link link;
+	uint32_t incval = 0;
+	uint32_t shift = 0;
+
+	/* Get current link speed. */
+	memset(&link, 0, sizeof(link));
+	ixgbe_dev_link_update(dev, 1);
+	rte_ixgbe_dev_atomic_read_link_status(dev, &link);
+
+	switch (link.link_speed) {
+	case ETH_LINK_SPEED_100:
+		incval = IXGBE_INCVAL_100;
+		shift = IXGBE_INCVAL_SHIFT_100;
+		break;
+	case ETH_LINK_SPEED_1000:
+		incval = IXGBE_INCVAL_1GB;
+		shift = IXGBE_INCVAL_SHIFT_1GB;
+		break;
+	case ETH_LINK_SPEED_10000:
+	default:
+		incval = IXGBE_INCVAL_10GB;
+		shift = IXGBE_INCVAL_SHIFT_10GB;
+		break;
+	}
+
+	switch (hw->mac.type) {
+	case ixgbe_mac_X550:
+		/* Independent of link speed. */
+		incval = 1;
+		/* Cycles read will be interpreted as ns. */
+		shift = 0;
+		/* Fall-through */
+	case ixgbe_mac_X540:
+		IXGBE_WRITE_REG(hw, IXGBE_TIMINCA, incval);
+		break;
+	case ixgbe_mac_82599EB:
+		incval >>= IXGBE_INCVAL_SHIFT_82599;
+		shift -= IXGBE_INCVAL_SHIFT_82599;
+		IXGBE_WRITE_REG(hw, IXGBE_TIMINCA,
+				(1 << IXGBE_INCPER_SHIFT_82599) | incval);
+		break;
+	default:
+		/* Not supported. */
+		return;
+	}
+
+	memset(&adapter->cc, 0, sizeof(struct cyclecounter));
+	adapter->cc.read = ixgbe_read_timesync_cyclecounter;
+	adapter->cc.mask = IXGBE_CYCLECOUNTER_MASK;
+	adapter->cc.shift = shift;
+}
+
+static int
+ixgbe_timesync_time_adjust(struct rte_eth_dev *dev, int64_t delta)
+{
+	struct ixgbe_adapter *adapter =
+			(struct ixgbe_adapter *)dev->data->dev_private;
+
+	adapter->tc.nsec += delta;
+
+	return 0;
+}
+
+static int
+ixgbe_timesync_time_set(struct rte_eth_dev *dev, struct timespec *ts)
+{
+	uint64_t ns;
+
+	ns = timespec_to_ns(ts);
+	/* Reset the timecounter. */
+	timecounter_init(dev, ns);
+
+	return 0;
+}
+
+static int
+ixgbe_timesync_time_get(struct rte_eth_dev *dev, struct timespec *ts)
+{
+	uint64_t ns;
+
+	ns = timecounter_read(dev);
+	*ts = ns_to_timespec(ns);
+
+	return 0;
+}
+
 static int
 ixgbe_timesync_enable(struct rte_eth_dev *dev)
 {
 	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
 	uint32_t tsync_ctl;
 	uint32_t tsauxc;
+	uint64_t ns;
+	struct timespec zerotime = {0, 0};
 
 	/* Enable system time for platforms where it isn't on by default. */
 	tsauxc = IXGBE_READ_REG(hw, IXGBE_TSAUXC);
 	tsauxc &= ~IXGBE_TSAUXC_DISABLE_SYSTIME;
-	IXGBE_WRITE_REG(hw, IXGBE_TSAUXC, tsauxc);
 
-	/* Start incrementing the register used to timestamp PTP packets. */
-	IXGBE_WRITE_REG(hw, IXGBE_TIMINCA, IXGBE_TIMINCA_INIT);
+	/* Set 0.0 epoch time to initialize timecounter. */
+	ns = timespec_to_ns(&zerotime);
+	ixgbe_start_cyclecounter(dev);
+	timecounter_init(dev, ns);
+
+	IXGBE_WRITE_REG(hw, IXGBE_TSAUXC, tsauxc);
 
 	/* Enable L2 filtering of IEEE1588/802.1AS Ethernet frame types. */
 	IXGBE_WRITE_REG(hw, IXGBE_ETQF(IXGBE_ETQF_FILTER_1588),
@@ -5681,6 +5908,9 @@ ixgbe_timesync_enable(struct rte_eth_dev *dev)
 	tsync_ctl |= IXGBE_TSYNCTXCTL_ENABLED;
 	IXGBE_WRITE_REG(hw, IXGBE_TSYNCTXCTL, tsync_ctl);
 
+	/* After writing to registers should be flush. */
+	IXGBE_WRITE_FLUSH(hw);
+
 	return 0;
 }
 
@@ -5715,9 +5945,13 @@ ixgbe_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 				 uint32_t flags __rte_unused)
 {
 	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct ixgbe_adapter *adapter =
+		(struct ixgbe_adapter *)dev->data->dev_private;
+
 	uint32_t tsync_rxctl;
 	uint32_t rx_stmpl;
 	uint32_t rx_stmph;
+	uint64_t regival = 0;
 
 	tsync_rxctl = IXGBE_READ_REG(hw, IXGBE_TSYNCRXCTL);
 	if ((tsync_rxctl & IXGBE_TSYNCRXCTL_VALID) == 0)
@@ -5725,9 +5959,15 @@ ixgbe_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 
 	rx_stmpl = IXGBE_READ_REG(hw, IXGBE_RXSTMPL);
 	rx_stmph = IXGBE_READ_REG(hw, IXGBE_RXSTMPH);
+	timecounter_read(dev);
 
-	timestamp->tv_sec = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
-	timestamp->tv_nsec = 0;
+	if (hw->mac.type == ixgbe_mac_X550)
+		regival = (uint64_t)((uint64_t)rx_stmph * NSEC_PER_SEC
+			+ rx_stmpl);
+	else
+		regival = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
+	regival = timecounter_cycles_to_ns_time(&adapter->tc, regival);
+	*timestamp = ns_to_timespec(regival);
 
 	return  0;
 }
@@ -5737,9 +5977,13 @@ ixgbe_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 				 struct timespec *timestamp)
 {
 	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct ixgbe_adapter *adapter =
+		(struct ixgbe_adapter *)dev->data->dev_private;
+
 	uint32_t tsync_txctl;
 	uint32_t tx_stmpl;
 	uint32_t tx_stmph;
+	uint64_t regival = 0;
 
 	tsync_txctl = IXGBE_READ_REG(hw, IXGBE_TSYNCTXCTL);
 	if ((tsync_txctl & IXGBE_TSYNCTXCTL_VALID) == 0)
@@ -5747,9 +5991,15 @@ ixgbe_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 
 	tx_stmpl = IXGBE_READ_REG(hw, IXGBE_TXSTMPL);
 	tx_stmph = IXGBE_READ_REG(hw, IXGBE_TXSTMPH);
+	timecounter_read(dev);
 
-	timestamp->tv_sec = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
-	timestamp->tv_nsec = 0;
+	if (hw->mac.type == ixgbe_mac_X550)
+		regival = (uint64_t)((uint64_t)tx_stmph * NSEC_PER_SEC
+			+ tx_stmpl);
+	else
+		regival = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
+	regival = timecounter_cycles_to_ns_time(&adapter->tc, regival);
+	*timestamp = ns_to_timespec(regival);
 
 	return  0;
 }
diff --git a/drivers/net/ixgbe/ixgbe_ethdev.h b/drivers/net/ixgbe/ixgbe_ethdev.h
index 569d678..701efde 100644
--- a/drivers/net/ixgbe/ixgbe_ethdev.h
+++ b/drivers/net/ixgbe/ixgbe_ethdev.h
@@ -37,6 +37,7 @@
 #include "base/ixgbe_dcb_82599.h"
 #include "base/ixgbe_dcb_82598.h"
 #include "ixgbe_bypass.h"
+#include <rte_ptp.h>
 
 /* need update link, bit flag */
 #define IXGBE_FLAG_NEED_LINK_UPDATE (uint32_t)(1 << 0)
@@ -279,6 +280,8 @@ struct ixgbe_adapter {
 
 	bool rx_bulk_alloc_allowed;
 	bool rx_vec_allowed;
+	struct cyclecounter cc;
+	struct timecounter tc;
 };
 
 #define IXGBE_DEV_PRIVATE_TO_HW(adapter)\
-- 
2.1.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v3 4/7] igb: add additional ieee1588 support functions
  2015-11-03 16:38 ` [dpdk-dev] [PATCH v3 0/7] " Daniel Mrzyglod
                     ` (2 preceding siblings ...)
  2015-11-03 16:38   ` [dpdk-dev] [PATCH v3 3/7] ixgbe: add additional ieee1588 support functions Daniel Mrzyglod
@ 2015-11-03 16:38   ` Daniel Mrzyglod
  2015-11-03 16:38   ` [dpdk-dev] [PATCH v3 5/7] i40e: " Daniel Mrzyglod
                     ` (2 subsequent siblings)
  6 siblings, 0 replies; 84+ messages in thread
From: Daniel Mrzyglod @ 2015-11-03 16:38 UTC (permalink / raw)
  To: dev

From: Pablo de Lara <pablo.de.lara.guarch@intel.com>

Add additional functions to support the existing IEEE1588
functionality and to enable getting, setting and adjusting
the device time.

Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
---
 drivers/net/e1000/e1000_ethdev.h |   3 +
 drivers/net/e1000/igb_ethdev.c   | 299 +++++++++++++++++++++++++++++++++++++--
 2 files changed, 292 insertions(+), 10 deletions(-)

diff --git a/drivers/net/e1000/e1000_ethdev.h b/drivers/net/e1000/e1000_ethdev.h
index 3c6f613..247002a 100644
--- a/drivers/net/e1000/e1000_ethdev.h
+++ b/drivers/net/e1000/e1000_ethdev.h
@@ -33,6 +33,7 @@
 
 #ifndef _E1000_ETHDEV_H_
 #define _E1000_ETHDEV_H_
+#include <rte_ptp.h>
 
 /* need update link, bit flag */
 #define E1000_FLAG_NEED_LINK_UPDATE (uint32_t)(1 << 0)
@@ -254,6 +255,8 @@ struct e1000_adapter {
 	struct e1000_vf_info    *vfdata;
 	struct e1000_filter_info filter;
 	bool stopped;
+	struct cyclecounter cc;
+	struct timecounter tc;
 };
 
 #define E1000_DEV_PRIVATE(adapter) \
diff --git a/drivers/net/e1000/igb_ethdev.c b/drivers/net/e1000/igb_ethdev.c
index 97ca3c0..d4c0bcb 100644
--- a/drivers/net/e1000/igb_ethdev.c
+++ b/drivers/net/e1000/igb_ethdev.c
@@ -78,10 +78,11 @@
 #define IGB_8_BIT_MASK   UINT8_MAX
 
 /* Additional timesync values. */
-#define E1000_ETQF_FILTER_1588 3
-#define E1000_TIMINCA_INCVALUE 16000000
-#define E1000_TIMINCA_INIT     ((0x02 << E1000_TIMINCA_16NS_SHIFT) \
-				| E1000_TIMINCA_INCVALUE)
+#define E1000_CYCLECOUNTER_MASK      0xffffffffffffffff
+#define E1000_ETQF_FILTER_1588       3
+#define IGB_82576_TSYNC_SHIFT        16
+#define E1000_INCPERIOD_82576        (1 << E1000_TIMINCA_16NS_SHIFT)
+#define E1000_INCVALUE_82576         (16 << IGB_82576_TSYNC_SHIFT)
 #define E1000_TSAUXC_DISABLE_SYSTIME 0x80000000
 
 static int  eth_igb_configure(struct rte_eth_dev *dev);
@@ -236,6 +237,11 @@ static int igb_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 					  uint32_t flags);
 static int igb_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 					  struct timespec *timestamp);
+static int igb_timesync_time_adjust(struct rte_eth_dev *dev, int64_t delta);
+static int igb_timesync_time_get(struct rte_eth_dev *dev,
+				 struct timespec *timestamp);
+static int igb_timesync_time_set(struct rte_eth_dev *dev,
+				 struct timespec *timestamp);
 static int eth_igb_rx_queue_intr_enable(struct rte_eth_dev *dev,
 					uint16_t queue_id);
 static int eth_igb_rx_queue_intr_disable(struct rte_eth_dev *dev,
@@ -349,6 +355,9 @@ static const struct eth_dev_ops eth_igb_ops = {
 	.get_eeprom_length    = eth_igb_get_eeprom_length,
 	.get_eeprom           = eth_igb_get_eeprom,
 	.set_eeprom           = eth_igb_set_eeprom,
+	.timesync_time_adjust  = igb_timesync_time_adjust,
+	.timesync_time_get     = igb_timesync_time_get,
+	.timesync_time_set     = igb_timesync_time_set,
 };
 
 /*
@@ -4160,20 +4169,248 @@ eth_igb_set_mc_addr_list(struct rte_eth_dev *dev,
 	return 0;
 }
 
+/*
+ * Register units might not be nanoseconds. This function converts
+ * these units into nanoseconds and adds to the previous time stored.
+ */
+static uint64_t
+timecounter_cycles_to_ns_time(struct timecounter *tc, uint64_t cycle_tstamp)
+{
+	uint64_t delta;
+	uint64_t nsec = tc->nsec, frac = tc->frac;
+
+	delta = (cycle_tstamp - tc->cycle_last) & tc->cc->mask;
+	/*
+	 * Cycle counts that are correctly converted as they
+	 * are between -1/2 max cycle count and +1/2 max cycle count.
+	 */
+	if (delta > (tc->cc->mask / 2)) {
+		delta = (tc->cycle_last - cycle_tstamp) & tc->cc->mask;
+		nsec -= cyclecounter_cycles_to_ns_backwards(tc->cc, delta,
+							    frac);
+	} else {
+		nsec += cyclecounter_cycles_to_ns(tc->cc, delta, tc->mask,
+						  &frac);
+	}
+
+	return nsec;
+}
+
+static uint64_t
+igb_read_timesync_cyclecounter(struct rte_eth_dev *dev)
+{
+	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	uint64_t systime_cycles = 0;
+
+	switch (hw->mac.type) {
+	case e1000_i210:
+	case e1000_i211:
+		/*
+		 * Need to read System Time Residue Register to be able
+		 * to read the other two registers.
+		 */
+		E1000_READ_REG(hw, E1000_SYSTIMR);
+		/* SYSTIMEL stores ns and SYSTIMEH stores seconds. */
+		systime_cycles = (uint64_t)E1000_READ_REG(hw, E1000_SYSTIML);
+		systime_cycles += (uint64_t)E1000_READ_REG(hw, E1000_SYSTIMH)
+				* NSEC_PER_SEC;
+		break;
+	case e1000_82580:
+	case e1000_i350:
+	case e1000_i354:
+		/*
+		 * Need to read System Time Residue Register to be able
+		 * to read the other two registers.
+		 */
+		E1000_READ_REG(hw, E1000_SYSTIMR);
+		systime_cycles |= (uint64_t)E1000_READ_REG(hw, E1000_SYSTIML);
+		/* Only the 8 LSB are valid. */
+		systime_cycles |= (uint64_t)(E1000_READ_REG(hw, E1000_SYSTIMH)
+				& 0xff) << 32;
+		break;
+	default:
+		systime_cycles |= (uint64_t)E1000_READ_REG(hw, E1000_SYSTIML);
+		systime_cycles |= (uint64_t)E1000_READ_REG(hw, E1000_SYSTIMH)
+				<< 32;
+		break;
+	}
+
+	return systime_cycles;
+}
+
+/*
+ * Get nanoseconds since the last call of this function.
+ */
+static uint64_t
+timecounter_read_ns_delta(struct rte_eth_dev *dev)
+{
+	uint64_t cycle_now, cycle_delta;
+	uint64_t ns_offset;
+	struct e1000_adapter *adapter =
+				(struct e1000_adapter *)dev->data->dev_private;
+	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+
+	/* Read cycle counter. */
+	cycle_now = adapter->tc.cc->read(dev);
+
+	/* Calculate the delta since the last timecounter_read_delta(). */
+	cycle_delta = (cycle_now - adapter->tc.cycle_last) &
+		       adapter->tc.cc->mask;
+
+	/* Convert to nanoseconds. */
+	switch (hw->mac.type) {
+	case e1000_i210:
+	case e1000_i211:
+	case e1000_82580:
+	case e1000_i350:
+	case e1000_i354:
+		/* Registers store directly nanoseconds, no need to convert. */
+		ns_offset = cycle_delta;
+		break;
+	default:
+		ns_offset = cyclecounter_cycles_to_ns(adapter->tc.cc,
+						      cycle_delta,
+						      adapter->tc.mask,
+						      &adapter->tc.frac);
+	}
+
+	/*
+	 * Store current cycle counter for next
+	 * timecounter_read_ns_delta() call.
+	 */
+	adapter->tc.cycle_last = cycle_now;
+
+	return ns_offset;
+}
+
+static uint64_t
+timecounter_read(struct rte_eth_dev *dev)
+{
+	uint64_t nsec;
+	struct e1000_adapter *adapter =
+			(struct e1000_adapter *)dev->data->dev_private;
+
+	/* increment time by nanoseconds since last call */
+	nsec = timecounter_read_ns_delta(dev);
+	nsec += adapter->tc.nsec;
+	adapter->tc.nsec = nsec;
+
+	return nsec;
+}
+
+static void
+timecounter_init(struct rte_eth_dev *dev, uint64_t start_time)
+{
+	struct e1000_adapter *adapter =
+				(struct e1000_adapter *)dev->data->dev_private;
+	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+
+	adapter->tc.cc = &adapter->cc;
+	adapter->tc.cycle_last = adapter->tc.cc->read(dev);
+	adapter->tc.nsec = start_time;
+	switch (hw->mac.type) {
+	case e1000_82580:
+	case e1000_i350:
+	case e1000_i354:
+		/* 32 LSB bits + 8 MSB bits = 40 bits */
+		adapter->tc.mask = (1ULL << 40) - 1;
+	default:
+		adapter->tc.mask = (1ULL << adapter->tc.cc->shift) - 1;
+	}
+	adapter->tc.frac = 0;
+}
+
+static void
+igb_start_cyclecounter(struct rte_eth_dev *dev)
+{
+	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct e1000_adapter *adapter =
+		(struct e1000_adapter *)dev->data->dev_private;
+	uint32_t incval = 1;
+	uint32_t shift = 0;
+
+	switch (hw->mac.type) {
+	case e1000_i210:
+	case e1000_i211:
+	case e1000_82580:
+	case e1000_i350:
+	case e1000_i354:
+		/*
+		 * Start incrementing the register
+		 * used to timestamp PTP packets.
+		 */
+		E1000_WRITE_REG(hw, E1000_TIMINCA, incval);
+		break;
+	case e1000_82576:
+		incval = E1000_INCVALUE_82576;
+		shift = IGB_82576_TSYNC_SHIFT;
+		E1000_WRITE_REG(hw, E1000_TIMINCA,
+				E1000_INCPERIOD_82576 | incval);
+		break;
+	default:
+		/* Not supported */
+		return;
+	}
+
+	memset(&adapter->cc, 0, sizeof(struct cyclecounter));
+	adapter->cc.read = igb_read_timesync_cyclecounter;
+	adapter->cc.mask = E1000_CYCLECOUNTER_MASK;
+	adapter->cc.shift = shift;
+}
+
+static int
+igb_timesync_time_adjust(struct rte_eth_dev *dev, int64_t delta)
+{
+	struct e1000_adapter *adapter =
+			(struct e1000_adapter *)dev->data->dev_private;
+
+	adapter->tc.nsec += delta;
+
+	return 0;
+}
+
+static int
+igb_timesync_time_set(struct rte_eth_dev *dev, struct timespec *ts)
+{
+	uint64_t ns;
+
+	ns = timespec_to_ns(ts);
+
+	/* Reset the timecounter. */
+	timecounter_init(dev, ns);
+
+	return 0;
+}
+
+static int
+igb_timesync_time_get(struct rte_eth_dev *dev, struct timespec *ts)
+{
+	uint64_t ns;
+
+	ns = timecounter_read(dev);
+	*ts = ns_to_timespec(ns);
+
+	return 0;
+}
+
 static int
 igb_timesync_enable(struct rte_eth_dev *dev)
 {
 	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
 	uint32_t tsync_ctl;
 	uint32_t tsauxc;
+	uint64_t ns;
+	struct timespec zerotime = {0, 0};
 
 	/* Enable system time for it isn't on by default. */
 	tsauxc = E1000_READ_REG(hw, E1000_TSAUXC);
 	tsauxc &= ~E1000_TSAUXC_DISABLE_SYSTIME;
 	E1000_WRITE_REG(hw, E1000_TSAUXC, tsauxc);
 
-	/* Start incrementing the register used to timestamp PTP packets. */
-	E1000_WRITE_REG(hw, E1000_TIMINCA, E1000_TIMINCA_INIT);
+	/* Set 0.0 epoch time to initialize timecounter. */
+	ns = timespec_to_ns(&zerotime);
+	igb_start_cyclecounter(dev);
+	timecounter_init(dev, ns);
 
 	/* Enable L2 filtering of IEEE1588/802.1AS Ethernet frame types. */
 	E1000_WRITE_REG(hw, E1000_ETQF(E1000_ETQF_FILTER_1588),
@@ -4225,9 +4462,13 @@ igb_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 			       uint32_t flags __rte_unused)
 {
 	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct e1000_adapter *adapter =
+			(struct e1000_adapter *)dev->data->dev_private;
+
 	uint32_t tsync_rxctl;
 	uint32_t rx_stmpl;
 	uint32_t rx_stmph;
+	uint64_t regival = 0;
 
 	tsync_rxctl = E1000_READ_REG(hw, E1000_TSYNCRXCTL);
 	if ((tsync_rxctl & E1000_TSYNCRXCTL_VALID) == 0)
@@ -4235,9 +4476,26 @@ igb_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 
 	rx_stmpl = E1000_READ_REG(hw, E1000_RXSTMPL);
 	rx_stmph = E1000_READ_REG(hw, E1000_RXSTMPH);
+	timecounter_read(dev);
+
+	switch (hw->mac.type) {
+	case e1000_82580:
+	case e1000_i350:
+	case e1000_i354:
+		regival = (uint64_t)((((uint64_t)(rx_stmph & 0xff)) << 32)
+			| rx_stmpl);
+		break;
+	case e1000_i210:
+	case e1000_i211:
+		regival = (uint64_t)((uint64_t)rx_stmph * NSEC_PER_SEC
+			+ rx_stmpl);
+		break;
+	default:
+		regival = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
+	}
 
-	timestamp->tv_sec = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
-	timestamp->tv_nsec = 0;
+	regival = timecounter_cycles_to_ns_time(&adapter->tc, regival);
+	*timestamp = ns_to_timespec(regival);
 
 	return  0;
 }
@@ -4247,6 +4505,10 @@ igb_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 			       struct timespec *timestamp)
 {
 	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct e1000_adapter *adapter =
+			(struct e1000_adapter *)dev->data->dev_private;
+	uint64_t regival = 0;
+
 	uint32_t tsync_txctl;
 	uint32_t tx_stmpl;
 	uint32_t tx_stmph;
@@ -4257,9 +4519,26 @@ igb_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 
 	tx_stmpl = E1000_READ_REG(hw, E1000_TXSTMPL);
 	tx_stmph = E1000_READ_REG(hw, E1000_TXSTMPH);
+	timecounter_read(dev);
+
+	switch (hw->mac.type) {
+	case e1000_82580:
+	case e1000_i350:
+	case e1000_i354:
+		regival = (uint64_t)((((uint64_t)(tx_stmph & 0xff)) << 32)
+			| tx_stmpl);
+		break;
+	case e1000_i210:
+	case e1000_i211:
+		regival = (uint64_t)((uint64_t)tx_stmph * NSEC_PER_SEC
+			+ tx_stmpl);
+		break;
+	default:
+		regival = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
+	}
 
-	timestamp->tv_sec = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
-	timestamp->tv_nsec = 0;
+	regival = timecounter_cycles_to_ns_time(&adapter->tc, regival);
+	*timestamp = ns_to_timespec(regival);
 
 	return  0;
 }
-- 
2.1.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v3 5/7] i40e: add additional ieee1588 support functions
  2015-11-03 16:38 ` [dpdk-dev] [PATCH v3 0/7] " Daniel Mrzyglod
                     ` (3 preceding siblings ...)
  2015-11-03 16:38   ` [dpdk-dev] [PATCH v3 4/7] igb: " Daniel Mrzyglod
@ 2015-11-03 16:38   ` Daniel Mrzyglod
  2015-11-03 16:38   ` [dpdk-dev] [PATCH v3 6/7] example: PTP client slave minimal implementation Daniel Mrzyglod
  2015-11-03 16:38   ` [dpdk-dev] [PATCH v3 7/7] doc: add a PTPCLIENT sample guide Daniel Mrzyglod
  6 siblings, 0 replies; 84+ messages in thread
From: Daniel Mrzyglod @ 2015-11-03 16:38 UTC (permalink / raw)
  To: dev

From: Pablo de Lara <pablo.de.lara.guarch@intel.com>

Add additional functions to support the existing IEEE1588
functionality and to enable getting, setting and adjusting
the device time.

Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
---
 drivers/net/i40e/i40e_ethdev.c | 192 ++++++++++++++++++++++++++++++++++++-----
 drivers/net/i40e/i40e_ethdev.h |   5 ++
 2 files changed, 177 insertions(+), 20 deletions(-)

diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 5a9f11d..f1c36a8 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -129,11 +129,13 @@
 	(1UL << RTE_ETH_FLOW_NONFRAG_IPV6_OTHER) | \
 	(1UL << RTE_ETH_FLOW_L2_PAYLOAD))
 
-#define I40E_PTP_40GB_INCVAL  0x0199999999ULL
-#define I40E_PTP_10GB_INCVAL  0x0333333333ULL
-#define I40E_PTP_1GB_INCVAL   0x2000000000ULL
-#define I40E_PRTTSYN_TSYNENA  0x80000000
-#define I40E_PRTTSYN_TSYNTYPE 0x0e000000
+/* Additional timesync values. */
+#define I40E_PTP_40GB_INCVAL     0x0199999999ULL
+#define I40E_PTP_10GB_INCVAL     0x0333333333ULL
+#define I40E_PTP_1GB_INCVAL      0x2000000000ULL
+#define I40E_PRTTSYN_TSYNENA     0x80000000
+#define I40E_PRTTSYN_TSYNTYPE    0x0e000000
+#define I40E_CYCLECOUNTER_MASK   0xffffffffffffffff
 
 #define I40E_MAX_PERCENT            100
 #define I40E_DEFAULT_DCB_APP_NUM    1
@@ -268,6 +270,11 @@ static int i40e_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 static int i40e_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 					   struct timespec *timestamp);
 static void i40e_read_stats_registers(struct i40e_pf *pf, struct i40e_hw *hw);
+static int i40e_timesync_time_adjust(struct rte_eth_dev *dev, int64_t delta);
+static int i40e_timesync_time_get(struct rte_eth_dev *dev,
+				  struct timespec *timestamp);
+static int i40e_timesync_time_set(struct rte_eth_dev *dev,
+				  struct timespec *timestamp);
 
 
 static const struct rte_pci_id pci_id_i40e_map[] = {
@@ -332,6 +339,9 @@ static const struct eth_dev_ops i40e_eth_dev_ops = {
 	.timesync_read_rx_timestamp   = i40e_timesync_read_rx_timestamp,
 	.timesync_read_tx_timestamp   = i40e_timesync_read_tx_timestamp,
 	.get_dcb_info                 = i40e_dev_get_dcb_info,
+	.timesync_time_adjust         = i40e_timesync_time_adjust,
+	.timesync_time_get            = i40e_timesync_time_get,
+	.timesync_time_set            = i40e_timesync_time_set,
 };
 
 /* store statistics names and its offset in stats structure */
@@ -6742,17 +6752,95 @@ i40e_mirror_rule_reset(struct rte_eth_dev *dev, uint8_t sw_id)
 	return 0;
 }
 
-static int
-i40e_timesync_enable(struct rte_eth_dev *dev)
+/*
+ * Adds the new cycles (in nanoseconds) to the previous time stored.
+ */
+static uint64_t
+timecounter_cycles_to_ns_time(struct timecounter *tc, uint64_t cycle_tstamp)
+{
+	uint64_t delta = (cycle_tstamp - tc->cycle_last);
+	uint64_t nsec = tc->nsec;
+
+	nsec += delta;
+
+	return nsec;
+}
+
+static uint64_t
+i40e_read_timesync_cyclecounter(struct rte_eth_dev *dev)
 {
 	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
-	struct rte_eth_link *link = &dev->data->dev_link;
-	uint32_t tsync_ctl_l;
-	uint32_t tsync_ctl_h;
+	uint64_t systim_cycles = 0;
+
+	systim_cycles |= (uint64_t)I40E_READ_REG(hw, I40E_PRTTSYN_TIME_L);
+	systim_cycles |= (uint64_t)I40E_READ_REG(hw, I40E_PRTTSYN_TIME_H)
+			<< 32;
+
+	return systim_cycles;
+}
+
+static uint64_t
+timecounter_read_ns_delta(struct rte_eth_dev *dev)
+{
+	uint64_t cycle_now, cycle_delta;
+	struct i40e_adapter *adapter =
+			(struct i40e_adapter *)dev->data->dev_private;
+
+	/* Read cycle counter. */
+	cycle_now = adapter->tc.cc->read(dev);
+
+	/* Calculate the delta since the last timecounter_read_delta(). */
+	cycle_delta = (cycle_now - adapter->tc.cycle_last);
+
+	/* Update time stamp of timecounter_read_delta() call. */
+	adapter->tc.cycle_last = cycle_now;
+
+	/* Delta already in nanoseconds. */
+	return cycle_delta;
+}
+
+static uint64_t
+timecounter_read(struct rte_eth_dev *dev)
+{
+	uint64_t nsec;
+	struct i40e_adapter *adapter =
+			(struct i40e_adapter *)dev->data->dev_private;
+
+	/* Increment time by nanoseconds since last call. */
+	nsec = timecounter_read_ns_delta(dev);
+	nsec += adapter->tc.nsec;
+	adapter->tc.nsec = nsec;
+
+	return nsec;
+}
+
+static void
+timecounter_init(struct rte_eth_dev *dev,
+		      uint64_t start_time)
+{
+	struct i40e_adapter *adapter =
+			(struct i40e_adapter *)dev->data->dev_private;
+	adapter->tc.cc = &adapter->cc;
+	adapter->tc.cycle_last = adapter->tc.cc->read(dev);
+	adapter->tc.nsec = start_time;
+}
+
+static void
+i40e_start_cyclecounter(struct rte_eth_dev *dev)
+{
+	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct i40e_adapter *adapter =
+			(struct i40e_adapter *)dev->data->dev_private;
+	struct rte_eth_link link;
 	uint32_t tsync_inc_l;
 	uint32_t tsync_inc_h;
 
-	switch (link->link_speed) {
+	/* Get current link speed. */
+	memset(&link, 0, sizeof(link));
+	i40e_dev_link_update(dev, 1);
+	rte_i40e_dev_atomic_read_link_status(dev, &link);
+
+	switch (link.link_speed) {
 	case ETH_LINK_SPEED_40G:
 		tsync_inc_l = I40E_PTP_40GB_INCVAL & 0xFFFFFFFF;
 		tsync_inc_h = I40E_PTP_40GB_INCVAL >> 32;
@@ -6770,6 +6858,63 @@ i40e_timesync_enable(struct rte_eth_dev *dev)
 		tsync_inc_h = 0x0;
 	}
 
+	/* Set the timesync increment value. */
+	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_L, tsync_inc_l);
+	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_H, tsync_inc_h);
+
+	memset(&adapter->cc, 0, sizeof(struct cyclecounter));
+	adapter->cc.read = i40e_read_timesync_cyclecounter;
+}
+
+static int
+i40e_timesync_time_adjust(struct rte_eth_dev *dev, int64_t delta)
+{
+	struct i40e_adapter *adapter =
+			(struct i40e_adapter *)dev->data->dev_private;
+
+	adapter->tc.nsec += delta;
+
+	return 0;
+}
+
+static int
+i40e_timesync_time_set(struct rte_eth_dev *dev, struct timespec *ts)
+{
+	uint64_t ns;
+
+	ns = timespec_to_ns(ts);
+
+	/* Reset the timecounter. */
+	timecounter_init(dev, ns);
+
+	return 0;
+}
+
+static int
+i40e_timesync_time_get(struct rte_eth_dev *dev, struct timespec *ts)
+{
+	uint64_t ns;
+
+	ns = timecounter_read(dev);
+	*ts = ns_to_timespec(ns);
+
+	return 0;
+}
+
+static int
+i40e_timesync_enable(struct rte_eth_dev *dev)
+{
+	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	uint32_t tsync_ctl_l;
+	uint32_t tsync_ctl_h;
+	uint64_t ns;
+	struct timespec zerotime = {0, 0};
+
+	/* Set 0.0 epoch time to initialize timecounter. */
+	ns = timespec_to_ns(&zerotime);
+	i40e_start_cyclecounter(dev);
+	timecounter_init(dev, ns);
+
 	/* Clear timesync registers. */
 	I40E_READ_REG(hw, I40E_PRTTSYN_STAT_0);
 	I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_H);
@@ -6779,10 +6924,6 @@ i40e_timesync_enable(struct rte_eth_dev *dev)
 	I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_L(3));
 	I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_H);
 
-	/* Set the timesync increment value. */
-	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_L, tsync_inc_l);
-	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_H, tsync_inc_h);
-
 	/* Enable timestamping of PTP packets. */
 	tsync_ctl_l = I40E_READ_REG(hw, I40E_PRTTSYN_CTL0);
 	tsync_ctl_l |= I40E_PRTTSYN_TSYNENA;
@@ -6814,7 +6955,7 @@ i40e_timesync_disable(struct rte_eth_dev *dev)
 	I40E_WRITE_REG(hw, I40E_PRTTSYN_CTL0, tsync_ctl_l);
 	I40E_WRITE_REG(hw, I40E_PRTTSYN_CTL1, tsync_ctl_h);
 
-	/* Set the timesync increment value. */
+	/* Reset the timesync increment value. */
 	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_L, 0x0);
 	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_H, 0x0);
 
@@ -6826,10 +6967,14 @@ i40e_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 				struct timespec *timestamp, uint32_t flags)
 {
 	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct i40e_adapter *adapter =
+		(struct i40e_adapter *)dev->data->dev_private;
+
 	uint32_t sync_status;
 	uint32_t rx_stmpl;
 	uint32_t rx_stmph;
 	uint32_t index = flags & 0x03;
+	uint64_t regival = 0;
 
 	sync_status = I40E_READ_REG(hw, I40E_PRTTSYN_STAT_1);
 	if ((sync_status & (1 << index)) == 0)
@@ -6837,9 +6982,11 @@ i40e_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 
 	rx_stmpl = I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_L(index));
 	rx_stmph = I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_H(index));
+	timecounter_read(dev);
 
-	timestamp->tv_sec = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
-	timestamp->tv_nsec = 0;
+	regival = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
+	regival = timecounter_cycles_to_ns_time(&adapter->tc, regival);
+	*timestamp = ns_to_timespec(regival);
 
 	return  0;
 }
@@ -6849,9 +6996,13 @@ i40e_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 				struct timespec *timestamp)
 {
 	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct i40e_adapter *adapter =
+		(struct i40e_adapter *)dev->data->dev_private;
+
 	uint32_t sync_status;
 	uint32_t tx_stmpl;
 	uint32_t tx_stmph;
+	uint64_t regival = 0;
 
 	sync_status = I40E_READ_REG(hw, I40E_PRTTSYN_STAT_0);
 	if ((sync_status & I40E_PRTTSYN_STAT_0_TXTIME_MASK) == 0)
@@ -6860,8 +7011,9 @@ i40e_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 	tx_stmpl = I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_L);
 	tx_stmph = I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_H);
 
-	timestamp->tv_sec = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
-	timestamp->tv_nsec = 0;
+	regival = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
+	regival = timecounter_cycles_to_ns_time(&adapter->tc, regival);
+	*timestamp = ns_to_timespec(regival);
 
 	return  0;
 }
diff --git a/drivers/net/i40e/i40e_ethdev.h b/drivers/net/i40e/i40e_ethdev.h
index de3b9d9..500f1f5 100644
--- a/drivers/net/i40e/i40e_ethdev.h
+++ b/drivers/net/i40e/i40e_ethdev.h
@@ -35,6 +35,7 @@
 #define _I40E_ETHDEV_H_
 
 #include <rte_eth_ctrl.h>
+#include <rte_ptp.h>
 
 #define I40E_VLAN_TAG_SIZE        4
 
@@ -502,6 +503,10 @@ struct i40e_adapter {
 	bool rx_vec_allowed;
 	bool tx_simple_allowed;
 	bool tx_vec_allowed;
+
+	/* for PTP */
+	struct cyclecounter cc;
+	struct timecounter tc;
 };
 
 int i40e_dev_switch_queues(struct i40e_pf *pf, bool on);
-- 
2.1.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v3 6/7] example: PTP client slave minimal implementation
  2015-11-03 16:38 ` [dpdk-dev] [PATCH v3 0/7] " Daniel Mrzyglod
                     ` (4 preceding siblings ...)
  2015-11-03 16:38   ` [dpdk-dev] [PATCH v3 5/7] i40e: " Daniel Mrzyglod
@ 2015-11-03 16:38   ` Daniel Mrzyglod
  2015-11-03 20:06     ` De Lara Guarch, Pablo
  2015-11-03 16:38   ` [dpdk-dev] [PATCH v3 7/7] doc: add a PTPCLIENT sample guide Daniel Mrzyglod
  6 siblings, 1 reply; 84+ messages in thread
From: Daniel Mrzyglod @ 2015-11-03 16:38 UTC (permalink / raw)
  To: dev

Add a sample application that acts as a PTP slave using the
DPDK ieee1588 functions.

Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
---
 MAINTAINERS                          |   3 +
 doc/guides/rel_notes/release_2_2.rst |   5 +
 examples/Makefile                    |   1 +
 examples/ptpclient/Makefile          |  56 +++
 examples/ptpclient/ptpclient.c       | 779 +++++++++++++++++++++++++++++++++++
 5 files changed, 844 insertions(+)
 create mode 100644 examples/ptpclient/Makefile
 create mode 100644 examples/ptpclient/ptpclient.c

diff --git a/MAINTAINERS b/MAINTAINERS
index c8be5d2..0638665 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -520,3 +520,6 @@ F: examples/tep_termination/
 F: examples/vmdq/
 F: examples/vmdq_dcb/
 F: doc/guides/sample_app_ug/vmdq_dcb_forwarding.rst
+
+M: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
+F: examples/ptpclient
diff --git a/doc/guides/rel_notes/release_2_2.rst b/doc/guides/rel_notes/release_2_2.rst
index 09fd642..c32338c 100644
--- a/doc/guides/rel_notes/release_2_2.rst
+++ b/doc/guides/rel_notes/release_2_2.rst
@@ -141,6 +141,11 @@ Libraries
 Examples
 ~~~~~~~~
 
+* **ptpclient: simple PTP slave client.**
+
+  Add a sample application that acts as a PTP slave using the
+  DPDK ieee1588 functions.
+
 
 Other
 ~~~~~
diff --git a/examples/Makefile b/examples/Makefile
index b4eddbd..4672534 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -74,5 +74,6 @@ DIRS-$(CONFIG_RTE_LIBRTE_XEN_DOM0) += vhost_xen
 DIRS-y += vmdq
 DIRS-y += vmdq_dcb
 DIRS-$(CONFIG_RTE_LIBRTE_POWER) += vm_power_manager
+DIRS-$(CONFIG_RTE_LIBRTE_IEEE1588) += ptpclient
 
 include $(RTE_SDK)/mk/rte.extsubdir.mk
diff --git a/examples/ptpclient/Makefile b/examples/ptpclient/Makefile
new file mode 100644
index 0000000..00dc68e
--- /dev/null
+++ b/examples/ptpclient/Makefile
@@ -0,0 +1,56 @@
+#   BSD LICENSE
+#
+#   Copyright(c) 2015 Intel Corporation. All rights reserved.
+#   All rights reserved.
+#
+#   Redistribution and use in source and binary forms, with or without
+#   modification, are permitted provided that the following conditions
+#   are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in
+#       the documentation and/or other materials provided with the
+#       distribution.
+#     * Neither the name of Intel Corporation nor the names of its
+#       contributors may be used to endorse or promote products derived
+#       from this software without specific prior written permission.
+#
+#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+
+# Default target, can be overriddegitn by command line or environment
+RTE_TARGET ?= x86_64-native-linuxapp-gcc
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# binary name
+APP = ptpclient
+
+# all source are stored in SRCS-y
+SRCS-y := ptpclient.c
+
+CFLAGS += -O3
+CFLAGS += $(WERROR_FLAGS)
+
+# workaround for a gcc bug with noreturn attribute
+# http://gcc.gnu.org/bugzilla/show_bug.cgi?id=12603
+ifeq ($(CONFIG_RTE_TOOLCHAIN_GCC),y)
+CFLAGS_main.o += -Wno-return-type
+endif
+
+include $(RTE_SDK)/mk/rte.extapp.mk
diff --git a/examples/ptpclient/ptpclient.c b/examples/ptpclient/ptpclient.c
new file mode 100644
index 0000000..8cfa8d0
--- /dev/null
+++ b/examples/ptpclient/ptpclient.c
@@ -0,0 +1,779 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This Application is a simple Layer 2 PTP v2 client
+ * IT shows T1-T4 values which are used to synchronize PHC clock.
+ * if -T 1 parameter is used Linux kernel Clock is synchronized with ptp clock
+ */
+
+#include <stdint.h>
+#include <inttypes.h>
+#include <rte_eal.h>
+#include <rte_ethdev.h>
+#include <rte_cycles.h>
+#include <rte_lcore.h>
+#include <rte_mbuf.h>
+#include <rte_ip.h>
+#include <limits.h>
+#include <sys/time.h>
+#include <getopt.h>
+
+#define RX_RING_SIZE 128
+#define TX_RING_SIZE 512
+
+#define NUM_MBUFS            8191
+#define MBUF_CACHE_SIZE       250
+
+/* Values for the PTP messageType field */
+#define SYNC                  0x0
+#define DELAY_REQ             0x1
+#define PDELAY_REQ            0x2
+#define PDELAY_RESP           0x3
+#define FOLLOW_UP             0x8
+#define DELAY_RESP            0x9
+#define PDELAY_RESP_FOLLOW_UP 0xA
+#define ANNOUNCE              0xB
+#define SIGNALING             0xC
+#define MANAGEMENT            0xD
+
+#define NSEC_PER_SEC        1000000000L
+#define KERNEL_TIME_ADJUST_LIMIT  20000
+#define PTP_PROTOCOL             0x88F7
+
+struct rte_mempool *mbuf_pool;
+uint32_t ptp_enabled_port_mask;
+uint8_t ptp_enabled_port_nb;
+static uint8_t ptp_enabled_ports[RTE_MAX_ETHPORTS];
+
+static const struct rte_eth_conf port_conf_default = {
+	.rxmode = { .max_rx_pkt_len = ETHER_MAX_LEN }
+};
+
+static const struct ether_addr ether_multicast = {
+	.addr_bytes = {0x01, 0x1b, 0x19, 0x0, 0x0, 0x0}
+};
+
+/* Structs used for PTP handling */
+struct tstamp {
+	uint16_t   sec_msb;
+	uint32_t   sec_lsb;
+	uint32_t   ns;
+}  __attribute__((packed));
+
+struct clock_identity {
+	uint8_t id[8];
+};
+
+struct port_identity {
+	struct clock_identity  clock_identity;
+	uint16_t               port_number;
+}  __attribute__((packed));
+
+struct ptp_header {
+	uint8_t              msgtype;
+	uint8_t              ver;
+	uint16_t             message_length;
+	uint8_t              domain_number;
+	uint8_t              reserved1;
+	uint8_t              flag_field[2];
+	int64_t              correction;
+	uint32_t             reserved2;
+	struct port_identity source_port_identity;
+	uint16_t             sequenceId;
+	uint8_t              control;
+	int8_t               log_message_interval;
+} __attribute__((packed));
+
+struct sync_msg {
+	struct ptp_header   hdr;
+	struct tstamp       origin_tstamp;
+} __attribute__((packed));
+
+struct follow_up_msg {
+	struct ptp_header   hdr;
+	struct tstamp       precise_origin_tstamp;
+	uint8_t             suffix[0];
+} __attribute__((packed));
+
+struct delay_req_msg {
+	struct ptp_header   hdr;
+	struct tstamp       origin_tstamp;
+} __attribute__((packed));
+
+struct delay_resp_msg {
+	struct ptp_header    hdr;
+	struct tstamp        rx_tstamp;
+	struct port_identity requesting_port_identity;
+	uint8_t              suffix[0];
+} __attribute__((packed));
+
+struct ptp_message {
+	union {
+		struct ptp_header          header;
+		struct sync_msg            sync;
+		struct delay_req_msg       delay_req;
+		struct follow_up_msg       follow_up;
+		struct delay_resp_msg      delay_resp;
+	} __attribute__((packed));
+};
+
+struct ptpv2_data_slave_ordinary {
+	struct rte_mbuf *m;
+	struct timespec tstamp1;
+	struct timespec tstamp2;
+	struct timespec tstamp3;
+	struct timespec tstamp4;
+	struct clock_identity client_clock_id;
+	struct clock_identity master_clock_id;
+	struct timeval new_adj;
+	int64_t delta;
+	uint8_t portid;
+	uint8_t clock_portid;
+	uint16_t seqID_SYNC;
+	uint16_t seqID_FOLLOWUP;
+	uint8_t ptpset;
+	uint8_t kernel_time_set;
+	uint8_t current_ptp_port;
+};
+
+static struct ptpv2_data_slave_ordinary ptp_data;
+
+static inline uint64_t timespec64_to_ns(const struct timespec *ts)
+{
+	return ((uint64_t) ts->tv_sec * NSEC_PER_SEC) + ts->tv_nsec;
+}
+
+static struct timeval
+ns_to_timeval(int64_t nsec)
+{
+	struct timespec t_spec = {0, 0};
+	struct timeval t_eval = {0, 0};
+	int32_t rem;
+
+	if (nsec == 0)
+		return t_eval;
+	rem = nsec % NSEC_PER_SEC;
+	t_spec.tv_sec = nsec / NSEC_PER_SEC;
+
+	if (rem < 0) {
+		t_spec.tv_sec--;
+		rem += NSEC_PER_SEC;
+	}
+
+	t_spec.tv_nsec = rem;
+	t_eval.tv_sec = t_spec.tv_sec;
+	t_eval.tv_usec = t_spec.tv_nsec / 1000;
+
+	return t_eval;
+}
+
+/*
+ * Initializes a given port using global settings and with the RX buffers
+ * coming from the mbuf_pool passed as a parameter.
+ */
+static inline int
+port_init(uint8_t port, struct rte_mempool *mbuf_pool)
+{
+	struct rte_eth_dev_info dev_info;
+	struct rte_eth_conf port_conf = port_conf_default;
+	const uint16_t rx_rings = 1;
+	const uint16_t tx_rings = 1;
+	int retval;
+	uint16_t q;
+
+	if (port >= rte_eth_dev_count())
+		return -1;
+
+	/* Configure the Ethernet device. */
+	retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf);
+	if (retval != 0)
+		return retval;
+
+	/* Allocate and set up 1 RX queue per Ethernet port. */
+	for (q = 0; q < rx_rings; q++) {
+		retval = rte_eth_rx_queue_setup(port, q, RX_RING_SIZE,
+				rte_eth_dev_socket_id(port), NULL, mbuf_pool);
+
+		if (retval < 0)
+			return retval;
+	}
+
+	/* Allocate and set up 1 TX queue per Ethernet port. */
+	for (q = 0; q < tx_rings; q++) {
+		/* Setup txq_flags */
+		struct rte_eth_txconf *txconf;
+
+		rte_eth_dev_info_get(q, &dev_info);
+		txconf = &dev_info.default_txconf;
+		txconf->txq_flags = 0;
+
+		retval = rte_eth_tx_queue_setup(port, q, TX_RING_SIZE,
+				rte_eth_dev_socket_id(port), txconf);
+		if (retval < 0)
+			return retval;
+	}
+
+	/* Start the Ethernet port. */
+	retval = rte_eth_dev_start(port);
+	if (retval < 0)
+		return retval;
+
+	/* Enable timesync timestamping for the Ethernet device */
+	rte_eth_timesync_enable(port);
+
+	/* Enable RX in promiscuous mode for the Ethernet device. */
+	rte_eth_promiscuous_enable(port);
+
+	return 0;
+}
+
+static void
+print_clock_info(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	int64_t nsec;
+	struct timespec net_time, sys_time;
+
+	printf("Master Clock id: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
+		ptp_data->master_clock_id.id[0],
+		ptp_data->master_clock_id.id[1],
+		ptp_data->master_clock_id.id[2],
+		ptp_data->master_clock_id.id[3],
+		ptp_data->master_clock_id.id[4],
+		ptp_data->master_clock_id.id[5],
+		ptp_data->master_clock_id.id[6],
+		ptp_data->master_clock_id.id[7]);
+
+	printf("\nT2 - Slave  Clock.  %lds %ldns",
+			(ptp_data->tstamp2.tv_sec),
+			(ptp_data->tstamp2.tv_nsec));
+
+	printf("\nT1 - Master Clock.  %lds %ldns ",
+			ptp_data->tstamp1.tv_sec,
+			(ptp_data->tstamp1.tv_nsec));
+
+	printf("\nT3 - Slave  Clock.  %lds %ldns",
+			ptp_data->tstamp3.tv_sec,
+			(ptp_data->tstamp3.tv_nsec));
+
+	printf("\nT4 - Master Clock.  %lds %ldns ",
+			ptp_data->tstamp4.tv_sec,
+			(ptp_data->tstamp4.tv_nsec));
+
+	printf("\nDelta between master and slave clocks:%"PRId64"ns\n",
+			ptp_data->delta);
+
+	clock_gettime(CLOCK_REALTIME, &sys_time);
+	rte_eth_timesync_time_get(ptp_data->clock_portid,
+			&net_time);
+	time_t ts = net_time.tv_sec;
+
+	printf("\n\nComparison between Linux kernel Time and PTP:");
+
+	printf("\nCurrent PTP Time: %.24s %.9ld ns",
+			ctime(&ts), net_time.tv_nsec);
+
+	nsec = (int64_t)timespec64_to_ns(&net_time) -
+			(int64_t)timespec64_to_ns(&sys_time);
+	ptp_data->new_adj = ns_to_timeval(nsec);
+
+	gettimeofday(&ptp_data->new_adj, NULL);
+	time_t tp = ptp_data->new_adj.tv_sec;
+
+	printf("\nCurrent SYS Time: %.24s %.6ld ns",
+				ctime(&tp), ptp_data->new_adj.tv_usec);
+
+	printf("\nDelta between PTP and Linux Kernel time:%"PRId64"ns\n",
+				nsec);
+	printf("[Ctrl+C to quit]\n");
+
+	/* Clear screen and put cursor in column 1, row 1 */
+	printf("\033[2J\033[1;1H");
+}
+
+static int64_t
+delta_eval(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	int64_t delta;
+	uint64_t t1 = 0;
+	uint64_t t2 = 0;
+	uint64_t t3 = 0;
+	uint64_t t4 = 0;
+
+	t1 = timespec64_to_ns(&ptp_data->tstamp1);
+	t2 = timespec64_to_ns(&ptp_data->tstamp2);
+	t3 = timespec64_to_ns(&ptp_data->tstamp3);
+	t4 = timespec64_to_ns(&ptp_data->tstamp4);
+
+	/* delta = -[(T2-T1) - (T4-T3)]/2 */
+	delta = -((int64_t)((t2 - t1) - (t4 - t3))) / 2;
+
+	return delta;
+}
+
+/*
+ * Parse PTP SYNC message
+ */
+static void
+parse_sync(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	struct ptp_header *ptp_hdr;
+
+	ptp_hdr = (struct ptp_header *)(rte_pktmbuf_mtod(ptp_data->m, char *)
+			+ sizeof(struct ether_hdr));
+	ptp_data->seqID_SYNC = rte_be_to_cpu_16(ptp_hdr->sequenceId);
+
+	if (ptp_data->ptpset == 0) {
+		rte_memcpy(&ptp_data->master_clock_id,
+				&ptp_hdr->source_port_identity.clock_identity,
+				sizeof(struct clock_identity));
+		ptp_data->ptpset = 1;
+	}
+
+	if (memcmp(&ptp_hdr->source_port_identity.clock_identity,
+			&ptp_hdr->source_port_identity.clock_identity,
+			sizeof(struct clock_identity)) == 0) {
+
+		if (ptp_data->ptpset == 1)
+			rte_eth_timesync_read_rx_timestamp(ptp_data->portid,
+					&ptp_data->tstamp2, 0);
+	}
+
+}
+
+/*
+ * Parse PTP FOLLOWUP message & Send DELAY_REQ to master clock.
+ */
+static void
+parse_fup(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	struct ether_hdr *eth_hdr;
+	struct ptp_header *ptp_hdr;
+	struct clock_identity *client_clkid;
+	struct ptp_message *ptp_msg;
+	struct rte_mbuf *created_pkt;
+	struct ether_addr eth_multicast = ether_multicast;
+	size_t pkt_size;
+	int wait_us;
+	struct rte_mbuf *m = ptp_data->m;
+
+	eth_hdr = rte_pktmbuf_mtod(m, struct ether_hdr *);
+	ptp_hdr = (struct ptp_header *)(rte_pktmbuf_mtod(m, char *)
+			+ sizeof(struct ether_hdr));
+	if (memcmp(&ptp_data->master_clock_id,
+			&ptp_hdr->source_port_identity.clock_identity,
+			sizeof(struct clock_identity)) != 0)
+		return;
+
+	ptp_data->seqID_FOLLOWUP = rte_be_to_cpu_16(ptp_hdr->sequenceId);
+	ptp_msg = (struct ptp_message *) (rte_pktmbuf_mtod(m, char *) +
+					  sizeof(struct ether_hdr));
+	ptp_data->tstamp1.tv_nsec =
+		ntohl(ptp_msg->follow_up.precise_origin_tstamp.ns);
+	ptp_data->tstamp1.tv_sec =
+		((uint64_t)ntohl(ptp_msg->follow_up.precise_origin_tstamp.sec_lsb)) |
+		(((uint64_t)ntohs
+				(ptp_msg->follow_up.precise_origin_tstamp.sec_msb))
+				<< 32);
+
+	if (ptp_data->seqID_FOLLOWUP == ptp_data->seqID_SYNC) {
+
+		created_pkt = rte_pktmbuf_alloc(mbuf_pool);
+		pkt_size = sizeof(struct ether_hdr) +
+			sizeof(struct ptp_message);
+		created_pkt->data_len = pkt_size;
+		created_pkt->pkt_len = pkt_size;
+		eth_hdr = rte_pktmbuf_mtod(created_pkt, struct ether_hdr *);
+		rte_eth_macaddr_get(ptp_data->portid, &eth_hdr->s_addr);
+
+		/* Set multicast address 01-1B-19-00-00-00 */
+		ether_addr_copy(&eth_multicast, &eth_hdr->d_addr);
+
+		eth_hdr->ether_type = htons(PTP_PROTOCOL);
+		ptp_msg = (struct ptp_message *)
+			(rte_pktmbuf_mtod(created_pkt, char *) +
+			sizeof(struct ether_hdr));
+
+		ptp_msg->delay_req.hdr.sequenceId = htons(ptp_data->seqID_SYNC);
+		ptp_msg->delay_req.hdr.msgtype = DELAY_REQ;
+		ptp_msg->delay_req.hdr.ver = 2;
+		ptp_msg->delay_req.hdr.control = 1;
+		ptp_msg->delay_req.hdr.log_message_interval = 127;
+
+		/* Set up clock id */
+		client_clkid =
+			&ptp_msg->delay_req.hdr.source_port_identity.clock_identity;
+
+		client_clkid->id[0] = eth_hdr->s_addr.addr_bytes[0];
+		client_clkid->id[1] = eth_hdr->s_addr.addr_bytes[1];
+		client_clkid->id[2] = eth_hdr->s_addr.addr_bytes[2];
+		client_clkid->id[3] = 0xFF;
+		client_clkid->id[4] = 0xFE;
+		client_clkid->id[5] = eth_hdr->s_addr.addr_bytes[3];
+		client_clkid->id[6] = eth_hdr->s_addr.addr_bytes[4];
+		client_clkid->id[7] = eth_hdr->s_addr.addr_bytes[5];
+
+		rte_memcpy(&ptp_data->client_clock_id,
+				client_clkid,
+				sizeof(struct clock_identity));
+
+		/* Enable Flag for Hardware Timestamping */
+		created_pkt->ol_flags |= PKT_TX_IEEE1588_TMST;
+
+		/* We read value from NIC to prevent latching with old value */
+		rte_eth_timesync_read_tx_timestamp(ptp_data->portid,
+				&ptp_data->tstamp3);
+
+		/* the packet is being transmitted */
+		rte_eth_tx_burst(ptp_data->portid, 0, &created_pkt, 1);
+
+		wait_us = 0;
+		ptp_data->tstamp3.tv_nsec = 0;
+		ptp_data->tstamp3.tv_sec = 0;
+
+		/* we must wait at least 1us to read TX Timestamp */
+		while ((rte_eth_timesync_read_tx_timestamp(ptp_data->portid,
+				&ptp_data->tstamp3) < 0) && (wait_us < 1000)) {
+			rte_delay_us(1);
+			wait_us++;
+		}
+	}
+}
+
+/*
+ * Update the kernel time with the difference
+ * between it and the current NIC time
+ */
+static inline void
+update_kernel_time(void)
+{
+	int64_t nsec;
+	struct timespec net_time, sys_time;
+
+	clock_gettime(CLOCK_REALTIME, &sys_time);
+	rte_eth_timesync_time_get(ptp_data.current_ptp_port, &net_time);
+
+	nsec = (int64_t)timespec64_to_ns(&net_time) -
+	       (int64_t)timespec64_to_ns(&sys_time);
+
+	ptp_data.new_adj = ns_to_timeval(nsec);
+
+	/*
+	 * If difference between kernel time and system time in NIC
+	 * is too big (more than +/- 20 microseconds), use clock_settime
+	 * to set directly the kernel time, as adjtime is better for
+	 * small adjustments (takes longer to adjust the time)
+	 */
+
+	if (nsec > KERNEL_TIME_ADJUST_LIMIT || nsec < -KERNEL_TIME_ADJUST_LIMIT)
+		clock_settime(CLOCK_REALTIME, &net_time);
+	else
+		adjtime(&ptp_data.new_adj, 0);
+
+
+}
+
+/*
+ * Parse DELAY_RESP message
+ */
+static void
+parse_drsp(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	struct rte_mbuf *m = ptp_data->m;
+	struct ptp_message *ptp_msg;
+	uint16_t seqid;
+
+	ptp_msg = (struct ptp_message *) (rte_pktmbuf_mtod(m, char *) +
+					sizeof(struct ether_hdr));
+	seqid =  rte_be_to_cpu_16(ptp_msg->delay_resp.hdr.sequenceId);
+	if (memcmp(&ptp_data->client_clock_id,
+		   &ptp_msg->delay_resp.requesting_port_identity.clock_identity,
+		   sizeof(struct clock_identity)) == 0) {
+		if (seqid == ptp_data->seqID_FOLLOWUP) {
+			ptp_data->tstamp4.tv_nsec =
+					ntohl(ptp_msg->delay_resp.rx_tstamp.ns);
+			ptp_data->tstamp4.tv_sec =
+				((uint64_t)ntohl
+					(ptp_msg->delay_resp.rx_tstamp.sec_lsb)) |
+				(((uint64_t)ntohs
+					(ptp_msg->delay_resp.rx_tstamp.sec_msb))
+					<< 32);
+
+			/* Evaluate delta for adjustment */
+			ptp_data->delta = delta_eval(ptp_data);
+
+			rte_eth_timesync_time_adjust(ptp_data->portid,
+						     ptp_data->delta);
+
+			/* If enabled in app parameters, update kernel time */
+			if (ptp_data->kernel_time_set == 1)
+				update_kernel_time();
+
+			ptp_data->clock_portid = ptp_data->portid;
+			ptp_data->current_ptp_port = ptp_data->clock_portid;
+
+		}
+	}
+}
+
+/* This function processes PTP packets, implementing
+ * slave PTP IEEE1588 L2 functionality
+ */
+static void
+parse_ptp_frames(uint8_t portid, struct rte_mbuf *m) {
+	struct ptp_header *ptp_hdr;
+	struct ether_hdr *eth_hdr;
+	uint16_t eth_type;
+
+	eth_hdr = rte_pktmbuf_mtod(m, struct ether_hdr *);
+	eth_type = rte_be_to_cpu_16(eth_hdr->ether_type);
+
+	if (eth_type == PTP_PROTOCOL) {
+		ptp_data.m = m;
+		ptp_data.portid = portid;
+		ptp_hdr = (struct ptp_header *)(rte_pktmbuf_mtod(m, char *)
+					+ sizeof(struct ether_hdr));
+
+		switch (ptp_hdr->msgtype) {
+		case SYNC:
+			parse_sync(&ptp_data);
+			break;
+		case FOLLOW_UP:
+			parse_fup(&ptp_data);
+			break;
+		case DELAY_RESP:
+			parse_drsp(&ptp_data);
+			print_clock_info(&ptp_data);
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+/*
+ * The lcore main. This is the main thread that does the work, reading from
+ * an input port and writing to an output port.
+ */
+static __attribute__((noreturn)) void
+lcore_main(void)
+{
+	uint8_t portid;
+	unsigned nb_rx;
+	struct rte_mbuf *m;
+
+	/*
+	 * Check that the port is on the same NUMA node as the polling thread
+	 * for best performance.
+	 */
+	printf("\nCore %u Waiting for SYNC packets. [Ctrl+C to quit]\n",
+			rte_lcore_id());
+
+	/* Run until the application is quit or killed. */
+
+	while (1) {
+		/*
+		 * Read packet from RX queues
+		 */
+		for (portid = 0; portid < ptp_enabled_port_nb; portid++) {
+
+			portid = ptp_enabled_ports[portid];
+			nb_rx = rte_eth_rx_burst(portid, 0, &m, 1);
+
+			if (likely(nb_rx == 0))
+				continue;
+
+			if (m->ol_flags & PKT_RX_IEEE1588_PTP)
+				parse_ptp_frames(portid, m);
+
+			rte_pktmbuf_free(m);
+		}
+	}
+}
+
+static void
+print_usage(const char *prgname)
+{
+	printf("%s [EAL options] -- -p PORTMASK -T VALUE\n"
+		"  -T VALUE: 0 - Disable, 1 - Enable Linux Clock "
+		" Synchronization (0 default)\n"
+		"  -p PORTMASK: hexadecimal bitmask of ports to configure\n",
+			prgname);
+}
+
+static int
+ptp_parse_portmask(const char *portmask)
+{
+	char *end = NULL;
+	unsigned long pm;
+
+	/* parse hexadecimal string */
+	pm = strtoul(portmask, &end, 16);
+	if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
+		return -1;
+
+	if (pm == 0)
+		return -1;
+
+	return pm;
+}
+
+static int
+parse_ptp_kernel(const char *param)
+{
+	char *end = NULL;
+	unsigned long pm;
+
+	pm = strtoul(param, &end, 16);
+	if ((param[0] == '\0') || (end == NULL) || (*end != '\0'))
+		return -1;
+	if (pm == 0)
+		return 0;
+
+	return 1;
+}
+
+/* Parse the argument given in the command line of the application */
+static int
+ptp_parse_args(int argc, char **argv)
+{
+	int opt, ret;
+	char **argvopt;
+	int option_index;
+	char *prgname = argv[0];
+	static struct option lgopts[] = { {NULL, 0, 0, 0} };
+
+	argvopt = argv;
+
+	while ((opt = getopt_long(argc, argvopt, "p:T:",
+				  lgopts, &option_index)) != EOF) {
+
+		switch (opt) {
+
+		/* portmask */
+		case 'p':
+			ptp_enabled_port_mask = ptp_parse_portmask(optarg);
+			if (ptp_enabled_port_mask == 0) {
+				printf("invalid portmask\n");
+				print_usage(prgname);
+				return -1;
+			}
+			break;
+		/* time synchronization */
+		case 'T':
+			ret = parse_ptp_kernel(optarg);
+			if (ret < 0) {
+				print_usage(prgname);
+				return -1;
+			}
+
+			ptp_data.kernel_time_set = ret;
+			break;
+
+		default:
+			print_usage(prgname);
+			return -1;
+		}
+	}
+
+	argv[optind-1] = prgname;
+
+	optind = 0; /* reset getopt lib */
+	return 0;
+}
+
+/*
+ * The main function, which does initialization and calls the per-lcore
+ * functions.
+ */
+int
+main(int argc, char *argv[])
+{
+	unsigned nb_ports;
+
+	uint8_t portid;
+
+	/* Initialize the Environment Abstraction Layer (EAL). */
+	int ret = rte_eal_init(argc, argv);
+
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
+
+	memset(&ptp_data, '\0', sizeof(struct ptpv2_data_slave_ordinary));
+
+	argc -= ret;
+	argv += ret;
+
+	ret = ptp_parse_args(argc, argv);
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "Error with PTP initialization\n");
+
+	/* Check that there is an even number of ports to send/receive on. */
+	nb_ports = rte_eth_dev_count();
+
+	/* Creates a new mempool in memory to hold the mbufs. */
+	mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", NUM_MBUFS * nb_ports,
+		MBUF_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
+
+	if (mbuf_pool == NULL)
+		rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
+
+	/* Initialize all ports. */
+	for (portid = 0; portid < nb_ports; portid++) {
+		if ((ptp_enabled_port_mask & (1 << portid)) != 0) {
+			if (port_init(portid, mbuf_pool) == 0) {
+				ptp_enabled_ports[ptp_enabled_port_nb] = portid;
+				ptp_enabled_port_nb++;
+			} else {
+				rte_exit(EXIT_FAILURE,
+					 "Cannot init port %"PRIu8 "\n",
+					 portid);
+			}
+		} else
+			printf("Skipping disabled port %u\n", portid);
+	}
+
+	if (ptp_enabled_port_nb == 0) {
+		rte_exit(EXIT_FAILURE,
+			"All available ports are disabled."
+			" Please set portmask.\n");
+	}
+
+	if (rte_lcore_count() > 1)
+		printf("\nWARNING: Too many lcores enabled. Only 1 used.\n");
+
+	/* Call lcore_main on the master core only. */
+	lcore_main();
+
+	return 0;
+}
-- 
2.1.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v3 7/7] doc: add a PTPCLIENT sample guide
  2015-11-03 16:38 ` [dpdk-dev] [PATCH v3 0/7] " Daniel Mrzyglod
                     ` (5 preceding siblings ...)
  2015-11-03 16:38   ` [dpdk-dev] [PATCH v3 6/7] example: PTP client slave minimal implementation Daniel Mrzyglod
@ 2015-11-03 16:38   ` Daniel Mrzyglod
  6 siblings, 0 replies; 84+ messages in thread
From: Daniel Mrzyglod @ 2015-11-03 16:38 UTC (permalink / raw)
  To: dev

It includes:
 - Add the ptpclient picture with svg format.
 - Add the ptpclient.rst file
 - Change the index.rst file for the above pictures index.

Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
---
 doc/guides/sample_app_ug/img/ptpclient.svg | 520 +++++++++++++++++++++++++++++
 doc/guides/sample_app_ug/index.rst         |   3 +
 doc/guides/sample_app_ug/ptpclient.rst     | 324 ++++++++++++++++++
 3 files changed, 847 insertions(+)
 create mode 100644 doc/guides/sample_app_ug/img/ptpclient.svg
 create mode 100644 doc/guides/sample_app_ug/ptpclient.rst

diff --git a/doc/guides/sample_app_ug/img/ptpclient.svg b/doc/guides/sample_app_ug/img/ptpclient.svg
new file mode 100644
index 0000000..55c134e
--- /dev/null
+++ b/doc/guides/sample_app_ug/img/ptpclient.svg
@@ -0,0 +1,520 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="105mm"
+   height="148mm"
+   viewBox="0 0 372.04724 524.40945"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="drawing3.svg">
+  <defs
+     id="defs4">
+    <marker
+       inkscape:stockid="Arrow2Mend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow2Mend"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4256"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="scale(-0.6,-0.6)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Lstart"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow2Lstart"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4247"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="matrix(1.1,0,0,1.1,1.1,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible"
+       id="marker6746"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="Arrow2Lend">
+      <path
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         id="path6748"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker6526"
+       style="overflow:visible"
+       inkscape:isstock="true"
+       inkscape:collect="always">
+      <path
+         id="path6528"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker6276"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path6278"
+         d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(-0.8,0,0,-0.8,-10,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible"
+       id="marker6128"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="DotM">
+      <path
+         transform="matrix(0.4,0,0,0.4,2.96,0.4)"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
+         id="path6130"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible"
+       id="marker6022"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="Arrow2Lend"
+       inkscape:collect="always">
+      <path
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         id="path6024"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible"
+       id="marker5922"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="DotM">
+      <path
+         transform="matrix(0.4,0,0,0.4,2.96,0.4)"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
+         id="path5924"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow2Lend"
+       style="overflow:visible"
+       inkscape:isstock="true"
+       inkscape:collect="always">
+      <path
+         id="path4250"
+         style="fill:#0000ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="DotM"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker5676"
+       style="overflow:visible"
+       inkscape:isstock="true"
+       inkscape:collect="always">
+      <path
+         id="path5678"
+         d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
+         style="fill:#0000ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(0.4,0,0,0.4,2.96,0.4)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lstart"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker5600"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path5602"
+         d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(0.8,0,0,0.8,10,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible"
+       id="marker5510"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="DotM"
+       inkscape:collect="always">
+      <path
+         transform="matrix(0.4,0,0,0.4,2.96,0.4)"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
+         id="path5512"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker4651"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4653"
+         d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(-0.8,0,0,-0.8,-10,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker4605"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4607"
+         d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(-0.8,0,0,-0.8,-10,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow1Lend"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4232"
+         d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(-0.8,0,0,-0.8,-10,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="DotM"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="DotM"
+       style="overflow:visible"
+       inkscape:isstock="true"
+       inkscape:collect="always">
+      <path
+         id="path4293"
+         d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(0.4,0,0,0.4,2.96,0.4)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lstart"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow1Lstart"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4229"
+         d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(0.8,0,0,0.8,10,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3402"
+       is_visible="true" />
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3398"
+       is_visible="true" />
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3394"
+       is_visible="true" />
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3378"
+       is_visible="true" />
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3374"
+       is_visible="true" />
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3370"
+       is_visible="true" />
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3366"
+       is_visible="true" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1.06"
+     inkscape:cx="280.7353"
+     inkscape:cy="266.181"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     showguides="false"
+     inkscape:window-width="1920"
+     inkscape:window-height="1053"
+     inkscape:window-x="1920"
+     inkscape:window-y="103"
+     inkscape:window-maximized="0" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-527.95276)">
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.44085264;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="m 64.094156,600.99321 c 0,60.57637 0,393.74649 0,393.74649"
+       id="path3388"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.63210523;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="m 291.64401,600.59617 0,394.60083"
+       id="path3390"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.28789771;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#DotM);marker-end:url(#marker6746)"
+       d="M 64.094156,623.68474 289.7829,665.38811"
+       id="path4223"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.28789771;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#marker5510);marker-end:url(#marker6526)"
+       d="M 63.199944,683.08006 290.44233,725.29448"
+       id="path5274"
+       inkscape:connector-curvature="0" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="295.22058"
+       y="665.10974"
+       id="text5424"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan5426"
+         x="295.22058"
+         y="665.10974">T2</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#800080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="242.44649"
+       y="657.08087"
+       id="text5428"
+       sodipodi:linespacing="100%"
+       transform="matrix(0.98289795,0.18415108,-0.18415108,0.98289795,0,0)"><tspan
+         sodipodi:role="line"
+         id="tspan5430"
+         x="242.44649"
+         y="657.08087">FOLLOW UP:T1</tspan></text>
+    <path
+       style="fill:#0000ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.28789771;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#marker5676);marker-end:url(#Arrow2Lend)"
+       d="M 291.5001,774.36878 64.400801,816.4401"
+       id="path5586"
+       inkscape:connector-curvature="0" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="-44.967201"
+       y="809.64362"
+       id="text5898"
+       sodipodi:linespacing="100%"
+       transform="matrix(0.98291896,-0.18403889,0.18403889,0.98291896,0,0)"><tspan
+         sodipodi:role="line"
+         id="tspan5900"
+         x="-44.967201"
+         y="809.64362">DELAY REQUEST</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="297.08099"
+       y="774.7981"
+       id="text5902"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan5904"
+         x="297.08099"
+         y="774.7981">T3</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#800080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="35.20845"
+       y="817.29871"
+       id="text5906"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan5908"
+         x="35.20845"
+         y="817.29871">T4</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#800080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="33.920555"
+       y="623.68475"
+       id="text5910"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan5912"
+         x="33.920555"
+         y="623.68475">T1</tspan></text>
+    <path
+       style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.28789771;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#marker6128);marker-end:url(#marker6022)"
+       d="M 63.971502,838.62056 290.6415,881.55049"
+       id="path5914"
+       inkscape:connector-curvature="0" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#800080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="223.59686"
+       y="811.73932"
+       id="text6858"
+       sodipodi:linespacing="100%"
+       transform="matrix(0.98367106,0.17997568,-0.17997568,0.98367106,0,0)"><tspan
+         sodipodi:role="line"
+         id="tspan6860"
+         x="223.59686"
+         y="811.73932">DELAY RESPONSE:T4</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="813.40204"
+       y="-298.41309"
+       id="text6862"
+       sodipodi:linespacing="100%"
+       transform="matrix(0.0147127,0.99989176,-0.99989176,0.0147127,0,0)"><tspan
+         sodipodi:role="line"
+         id="tspan6864"
+         x="813.40204"
+         y="-298.41309">time</tspan></text>
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2.1464963;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow2Mend)"
+       d="m 316.97185,863.23372 0,66.11208"
+       id="path6866"
+       inkscape:connector-curvature="0" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.42257881px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#800080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="38.764343"
+       y="590.47479"
+       id="text7094"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan7096"
+         x="38.764343"
+         y="590.47479">master</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:47.51625061px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="571.69812"
+       y="61.796165"
+       id="text7098"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan7100"
+         x="571.69812"
+         y="61.796165" /></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.42257881px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="271.23392"
+       y="593.71478"
+       id="text7102"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan7104"
+         x="271.23392"
+         y="593.71478">slave</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.3917141px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#800080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="244.40062"
+       y="602.10443"
+       id="text4317"
+       sodipodi:linespacing="125%"
+       transform="matrix(0.98605316,0.16643065,-0.16643065,0.98605316,0,0)"><tspan
+         sodipodi:role="line"
+         id="tspan4319"
+         x="244.40062"
+         y="602.10443">SYNC</tspan></text>
+  </g>
+</svg>
diff --git a/doc/guides/sample_app_ug/index.rst b/doc/guides/sample_app_ug/index.rst
index 9beedd9..8ae86c0 100644
--- a/doc/guides/sample_app_ug/index.rst
+++ b/doc/guides/sample_app_ug/index.rst
@@ -73,6 +73,7 @@ Sample Applications User Guide
     vm_power_management
     tep_termination
     proc_info
+    ptpclient
 
 **Figures**
 
@@ -136,6 +137,8 @@ Sample Applications User Guide
 :numref:`figure_overlay_networking` :ref:`figure_overlay_networking`
 :numref:`figure_tep_termination_arch` :ref:`figure_tep_termination_arch`
 
+:numref:`figure_ptpclient_highlevel` :ref:`figure_ptpclient_highlevel`
+
 **Tables**
 
 :numref:`table_qos_metering_1` :ref:`table_qos_metering_1`
diff --git a/doc/guides/sample_app_ug/ptpclient.rst b/doc/guides/sample_app_ug/ptpclient.rst
new file mode 100644
index 0000000..31aa505
--- /dev/null
+++ b/doc/guides/sample_app_ug/ptpclient.rst
@@ -0,0 +1,324 @@
+..  BSD LICENSE
+    Copyright(c) 2015 Intel Corporation. All rights reserved.
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions
+    are met:
+
+    * Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.
+    * Neither the name of Intel Corporation nor the names of its
+    contributors may be used to endorse or promote products derived
+    from this software without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+    OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+PTP Client Sample Application
+=============================
+
+The PTP (Precision Time Protocol) client sample application is a simple
+example of using the DPDK IEEE1588 API to communicate with a PTP master clock
+to synchronize the time on the NIC and, optionally, on the Linux system.
+
+Note, PTP is a time syncing protocol and cannot be used within DPDK as a
+time-stamping mechanism. See the following for an explanation of the protocol:
+`Precision Time Protocol
+<https://en.wikipedia.org/wiki/Precision_Time_Protocol>`_.
+
+
+Limitations
+-----------
+
+The PTP sample application is intended as a simple reference implementation of
+a PTP client using the DPDK IEEE1588 API.
+In order to keep the application simple the following assumptions are made:
+
+* The first discovered master is the master for the session.
+* Only L2 PTP packets are supported.
+* Only the PTP v2 protocol is supported.
+* Only the slave clock is implemented.
+
+
+How the Application Works
+-------------------------
+
+.. _figure_ptpclient_highlevel:
+
+.. figure:: img/ptpclient.*
+
+   PTP Synchronization Protocol
+
+The PTP synchronization in the sample application works as follows:
+
+* Master sends *Sync* message - the slave saves it as T2.
+* Master sends *Follow Up* message and sends time of T1.
+* Slave sends *Delay Request* frame to PTP Master and stores T3.
+* Master sends *Delay Response* T4 time which is time of received T3.
+
+The adjustment for slave can be represented as:
+
+   adj = -[(T2-T1)-(T4 - T3)]/2
+
+If the command line parameter ``-T 1`` is used the application also
+synchronizes the PTP PHC clock with the Linux kernel clock.
+
+
+Compiling the Application
+-------------------------
+
+To compile the application, export the path to the DPDK source tree and edit
+the ``config/common_linuxapp`` configuration file to enable IEEE1588:
+
+.. code-block:: console
+
+    export RTE_SDK=/path/to/rte_sdk
+
+    # Edit  common_linuxapp and set the following options:
+    CONFIG_RTE_LIBRTE_IEEE1588=y
+
+Set the target, for example:
+
+.. code-block:: console
+
+    export RTE_TARGET=x86_64-native-linuxapp-gcc
+
+See the *DPDK Getting Started* Guide for possible ``RTE_TARGET`` values.
+
+Build the application as follows:
+
+.. code-block:: console
+
+    # Recompile DPDK.
+    make install T=$RTE_TARGET
+
+    # Compile the application.
+    cd ${RTE_SDK}/examples/ptpclient
+    make
+
+
+Running the Application
+-----------------------
+
+To run the example in a ``linuxapp`` environment:
+
+.. code-block:: console
+
+    ./build/ptpclient -c 2 -n 4 -- -p 0x1 -T 0
+
+Refer to *DPDK Getting Started Guide* for general information on running
+applications and the Environment Abstraction Layer (EAL) options.
+
+* ``-p portmask``: Hexadecimal portmask.
+* ``-T 0``: Update only the PTP slave clock.
+* ``-T 1``: Update the PTP slave clock and synchronize the Linux Kernel to the PTP clock.
+
+
+Code Explanation
+----------------
+
+The following sections provide an explanation of the main components of the
+code.
+
+All DPDK library functions used in the sample code are prefixed with ``rte_``
+and are explained in detail in the *DPDK API Documentation*.
+
+
+The Main Function
+~~~~~~~~~~~~~~~~~
+
+The ``main()`` function performs the initialization and calls the execution
+threads for each lcore.
+
+The first task is to initialize the Environment Abstraction Layer (EAL).  The
+``argc`` and ``argv`` arguments are provided to the ``rte_eal_init()``
+function. The value returned is the number of parsed arguments:
+
+.. code-block:: c
+
+    int ret = rte_eal_init(argc, argv);
+    if (ret < 0)
+        rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
+
+And than we parse application specific arguments
+
+.. code-block:: c
+
+    argc -= ret;
+    argv += ret;
+
+    ret = ptp_parse_args(argc, argv);
+    if (ret < 0)
+        rte_exit(EXIT_FAILURE, "Error with PTP initialization\n");
+
+The ``main()`` also allocates a mempool to hold the mbufs (Message Buffers)
+used by the application:
+
+.. code-block:: c
+
+    mbuf_pool = rte_mempool_create("MBUF_POOL",
+                                   NUM_MBUFS * nb_ports,
+                                   MBUF_SIZE,
+                                   MBUF_CACHE_SIZE,
+                                   sizeof(struct rte_pktmbuf_pool_private),
+                                   rte_pktmbuf_pool_init, NULL,
+                                   rte_pktmbuf_init,      NULL,
+                                   rte_socket_id(),
+                                   0);
+
+Mbufs are the packet buffer structure used by DPDK. They are explained in
+detail in the "Mbuf Library" section of the *DPDK Programmer's Guide*.
+
+The ``main()`` function also initializes all the ports using the user defined
+``port_init()`` function with portmask provided by user:
+
+.. code-block:: c
+
+    for (portid = 0; portid < nb_ports; portid++)
+        if ((ptp_enabled_port_mask & (1 << portid)) != 0) {
+
+            if (port_init(portid, mbuf_pool) == 0) {
+                ptp_enabled_ports[ptp_enabled_port_nb] = portid;
+                ptp_enabled_port_nb++;
+            } else {
+                rte_exit(EXIT_FAILURE, "Cannot init port %"PRIu8 "\n",
+                        portid);
+            }
+        }
+
+
+Once the initialization is complete, the application is ready to launch a
+function on an lcore. In this example ``lcore_main()`` is called on a single
+lcore.
+
+.. code-block:: c
+
+	lcore_main();
+
+The ``lcore_main()`` function is explained below.
+
+
+The Lcores Main
+~~~~~~~~~~~~~~~
+
+As we saw above the ``main()`` function calls an application function on the
+available lcores.
+
+The main work of the application is done within the loop:
+
+.. code-block:: c
+
+        for (portid = 0; portid < ptp_enabled_port_nb; portid++) {
+
+            portid = ptp_enabled_ports[portid];
+            nb_rx = rte_eth_rx_burst(portid, 0, &m, 1);
+
+            if (ptp_data.kernel_time_set == 1) {
+                /* Update every KERNEL_UPDATE cycle */
+                if (i % KERNEL_UPDATE == 0) {
+                    clock_gettime(CLOCK_REALTIME, &ptp_data.systime);
+
+                    rte_eth_timesync_gettime(ptp_data.current_ptp_port,
+                            &ptp_data.timestamp);
+
+                    nsec = (int64_t)timespec64_to_ns(&ptp_data.timestamp) -
+                            (int64_t)timespec64_to_ns(&ptp_data.systime);
+
+                    ptp_data.new_adj = ns_to_timeval(nsec);
+                    adjtime(&ptp_data.new_adj, 0);
+                }
+                i++;
+            }
+
+            if (likely(nb_rx == 0))
+                continue;
+
+            if (m->ol_flags & PKT_RX_IEEE1588_PTP)
+                parse_ptp_frames(portid, m);
+
+            rte_pktmbuf_free(m);
+
+        }
+
+Packets are received one by one on the RX ports and, if required, PTP response
+packets are transmitted on the TX ports.
+
+If the offload flags in the mbuf indicate that the packet is a PTP packet then
+the packet is parsed to determine which type:
+
+.. code-block:: c
+
+            if (m->ol_flags & PKT_RX_IEEE1588_PTP)
+                 parse_ptp_frames(portid, m);
+
+
+All packets are freed explicitly using ``rte_pktmbuf_free()``.
+
+The forwarding loop can be interrupted and the application closed using
+``Ctrl-C``.
+
+
+PTP parsing
+~~~~~~~~~~~
+
+The ``parse_ptp_frames()`` function processes PTP packets, implementing slave
+PTP IEEE1588 L2 functionality.
+
+.. code-block:: c
+
+    void
+    parse_ptp_frames(uint8_t portid, struct rte_mbuf *m) {
+        struct ptp_header *ptp_hdr;
+        struct ether_hdr *eth_hdr;
+        uint16_t eth_type;
+
+        eth_hdr = rte_pktmbuf_mtod(m, struct ether_hdr *);
+        eth_type = rte_be_to_cpu_16(eth_hdr->ether_type);
+
+        if (eth_type == PTP_PROTOCOL) {
+            ptp_data.m = m;
+            ptp_data.portid = portid;
+            ptp_hdr = (struct ptp_header *)(rte_pktmbuf_mtod(m, char *)
+                        + sizeof(struct ether_hdr));
+
+            switch (ptp_hdr->msgtype) {
+            case SYNC:
+                parse_sync(&ptp_data);
+                break;
+            case FOLLOW_UP:
+                parse_fup(&ptp_data);
+                break;
+            case DELAY_RESP:
+                parse_drsp(&ptp_data);
+                print_clock_info(&ptp_data);
+                break;
+            default:
+                break;
+            }
+        }
+    }
+
+There are 3 types of packets on the RX path which we must parse to create a minimal
+implementation of the PTP slave client:
+
+* SYNC packet.
+* FOLLOW UP packet
+* DELAY RESPONSE packet.
+
+When we parse the *FOLLOW UP* packet we also create and send a *DELAY_REQUEST* packet.
+Also when we parse the *DELAY RESPONSE* packet, and all conditions are met we adjust the PTP slave clock.
-- 
2.1.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [dpdk-dev] [PATCH v3 6/7] example: PTP client slave minimal implementation
  2015-11-03 16:38   ` [dpdk-dev] [PATCH v3 6/7] example: PTP client slave minimal implementation Daniel Mrzyglod
@ 2015-11-03 20:06     ` De Lara Guarch, Pablo
  0 siblings, 0 replies; 84+ messages in thread
From: De Lara Guarch, Pablo @ 2015-11-03 20:06 UTC (permalink / raw)
  To: Mrzyglod, DanielX T, dev

Hi Daniel,

> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Daniel Mrzyglod
> Sent: Tuesday, November 03, 2015 4:39 PM
> To: dev@dpdk.org
> Subject: [dpdk-dev] [PATCH v3 6/7] example: PTP client slave minimal
> implementation
> 
> Add a sample application that acts as a PTP slave using the
> DPDK ieee1588 functions.
> 
> Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
> ---
>  MAINTAINERS                          |   3 +
>  doc/guides/rel_notes/release_2_2.rst |   5 +
>  examples/Makefile                    |   1 +
>  examples/ptpclient/Makefile          |  56 +++
>  examples/ptpclient/ptpclient.c       | 779
> +++++++++++++++++++++++++++++++++++
>  5 files changed, 844 insertions(+)
>  create mode 100644 examples/ptpclient/Makefile
>  create mode 100644 examples/ptpclient/ptpclient.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index c8be5d2..0638665 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -520,3 +520,6 @@ F: examples/tep_termination/
>  F: examples/vmdq/
>  F: examples/vmdq_dcb/
>  F: doc/guides/sample_app_ug/vmdq_dcb_forwarding.rst
> +
> +M: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
> +F: examples/ptpclient
> diff --git a/doc/guides/rel_notes/release_2_2.rst
> b/doc/guides/rel_notes/release_2_2.rst
> index 09fd642..c32338c 100644
> --- a/doc/guides/rel_notes/release_2_2.rst
> +++ b/doc/guides/rel_notes/release_2_2.rst
> @@ -141,6 +141,11 @@ Libraries
>  Examples
>  ~~~~~~~~
> 
> +* **ptpclient: simple PTP slave client.**
> +
> +  Add a sample application that acts as a PTP slave using the
> +  DPDK ieee1588 functions.
> +
> 

You have added this in a wrong place in release notes, under Resolved issues.
Could you move it to the correct place in the document?

Thanks,
Pablo

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v4 0/7] add sample ptp slave application
  2015-10-02 15:20 [dpdk-dev] [PATCH 0/3] add sample ptp slave application Daniel Mrzyglod
                   ` (4 preceding siblings ...)
  2015-11-03 16:38 ` [dpdk-dev] [PATCH v3 0/7] " Daniel Mrzyglod
@ 2015-11-04 10:06 ` Daniel Mrzyglod
  2015-11-04 10:06   ` [dpdk-dev] [PATCH v4 1/7] ethdev: add additional ieee1588 support functions Daniel Mrzyglod
                     ` (8 more replies)
  2015-11-05 14:05 ` [dpdk-dev] [PATCH v5 " Daniel Mrzyglod
  6 siblings, 9 replies; 84+ messages in thread
From: Daniel Mrzyglod @ 2015-11-04 10:06 UTC (permalink / raw)
  To: dev

Add a sample application that acts as a PTP slave using the DPDK IEEE1588
functions.

Also add some additional IEEE1588 support functions to enable getting,
setting and adjusting the device time.

V3->V4:
Doc:
 - Update documentation for ptpclient
 - fix: put information about ptpaplication in correct place

V2->V3:
PMD:
 - move common structures and functions for PTP protocol to librte_net/rte_ptp.h

V1->V2:
PMDs:
 - add support for e1000
 - add support for ixgbe
 - add support for i40
ethdev:
 - change function names to more proper
Doc:
 - add documentation for ptpclient
sample:
 - add kernel adjustment option
 - add portmask option to provide portmask to aplication

Daniel Mrzyglod (5):
  ethdev: add additional ieee1588 support functions
  net: Add common PTP structures and functions
  ixgbe: add additional ieee1588 support functions
  example: PTP client slave minimal implementation
  doc: add a PTPCLIENT sample guide

Pablo de Lara (2):
  igb: add additional ieee1588 support functions
  i40e: add additional ieee1588 support functions

 MAINTAINERS                                |   3 +
 doc/guides/rel_notes/release_2_2.rst       |   8 +
 doc/guides/sample_app_ug/img/ptpclient.svg | 520 +++++++++++++++++++
 doc/guides/sample_app_ug/index.rst         |   3 +
 doc/guides/sample_app_ug/ptpclient.rst     | 306 +++++++++++
 drivers/net/e1000/e1000_ethdev.h           |   3 +
 drivers/net/e1000/igb_ethdev.c             | 299 ++++++++++-
 drivers/net/i40e/i40e_ethdev.c             | 192 ++++++-
 drivers/net/i40e/i40e_ethdev.h             |   5 +
 drivers/net/ixgbe/ixgbe_ethdev.c           | 272 +++++++++-
 drivers/net/ixgbe/ixgbe_ethdev.h           |   3 +
 examples/Makefile                          |   1 +
 examples/ptpclient/Makefile                |  56 +++
 examples/ptpclient/ptpclient.c             | 779 +++++++++++++++++++++++++++++
 lib/librte_ether/rte_ethdev.c              |  36 ++
 lib/librte_ether/rte_ethdev.h              |  65 ++-
 lib/librte_ether/rte_ether_version.map     |   3 +
 lib/librte_net/Makefile                    |   2 +-
 lib/librte_net/rte_ptp.h                   | 105 ++++
 19 files changed, 2618 insertions(+), 43 deletions(-)
 create mode 100644 doc/guides/sample_app_ug/img/ptpclient.svg
 create mode 100644 doc/guides/sample_app_ug/ptpclient.rst
 create mode 100644 examples/ptpclient/Makefile
 create mode 100644 examples/ptpclient/ptpclient.c
 create mode 100644 lib/librte_net/rte_ptp.h

-- 
2.5.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v4 1/7] ethdev: add additional ieee1588 support functions
  2015-11-04 10:06 ` [dpdk-dev] [PATCH v4 0/7] add sample ptp slave application Daniel Mrzyglod
@ 2015-11-04 10:06   ` Daniel Mrzyglod
  2015-11-04 10:06   ` [dpdk-dev] [PATCH v4 2/7] net: Add common PTP structures and functions Daniel Mrzyglod
                     ` (7 subsequent siblings)
  8 siblings, 0 replies; 84+ messages in thread
From: Daniel Mrzyglod @ 2015-11-04 10:06 UTC (permalink / raw)
  To: dev

Add additional functions to support the existing IEEE1588
functionality.

* rte_eth_timesync_settime(), function to set the device clock time.
* rte_eth_timesync_gettime, function to get the device clock time.
* rte_eth_timesync_adjust, function to adjust the device clock time.

Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
---
 doc/guides/rel_notes/release_2_2.rst   |  3 ++
 lib/librte_ether/rte_ethdev.c          | 36 +++++++++++++++++++
 lib/librte_ether/rte_ethdev.h          | 65 +++++++++++++++++++++++++++++++++-
 lib/librte_ether/rte_ether_version.map |  3 ++
 4 files changed, 106 insertions(+), 1 deletion(-)

diff --git a/doc/guides/rel_notes/release_2_2.rst b/doc/guides/rel_notes/release_2_2.rst
index ca8471b..e39d422 100644
--- a/doc/guides/rel_notes/release_2_2.rst
+++ b/doc/guides/rel_notes/release_2_2.rst
@@ -205,6 +205,9 @@ API Changes
 
 * The devargs union field virtual is renamed to virt for C++ compatibility.
 
+* Add new functions in ethdev to support IEEE1588: rte_eth_timesync_time_adjust()
+  rte_eth_timesync_time_get(), rte_eth_timesync_time_set()
+
 
 ABI Changes
 -----------
diff --git a/lib/librte_ether/rte_ethdev.c b/lib/librte_ether/rte_ethdev.c
index fcc9e0f..759a731 100644
--- a/lib/librte_ether/rte_ethdev.c
+++ b/lib/librte_ether/rte_ethdev.c
@@ -3165,6 +3165,42 @@ rte_eth_timesync_read_tx_timestamp(uint8_t port_id, struct timespec *timestamp)
 }
 
 int
+rte_eth_timesync_time_adjust(uint8_t port_id, int64_t delta)
+{
+	struct rte_eth_dev *dev;
+
+	VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	FUNC_PTR_OR_ERR_RET(*dev->dev_ops->timesync_time_adjust, -ENOTSUP);
+	return (*dev->dev_ops->timesync_time_adjust)(dev, delta);
+}
+
+int
+rte_eth_timesync_time_get(uint8_t port_id, struct timespec *timestamp)
+{
+	struct rte_eth_dev *dev;
+
+	VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	FUNC_PTR_OR_ERR_RET(*dev->dev_ops->timesync_time_get, -ENOTSUP);
+	return (*dev->dev_ops->timesync_time_get)(dev, timestamp);
+}
+
+int
+rte_eth_timesync_time_set(uint8_t port_id, struct timespec *timestamp)
+{
+	struct rte_eth_dev *dev;
+
+	VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	FUNC_PTR_OR_ERR_RET(*dev->dev_ops->timesync_time_set, -ENOTSUP);
+	return (*dev->dev_ops->timesync_time_set)(dev, timestamp);
+}
+
+int
 rte_eth_dev_get_reg_length(uint8_t port_id)
 {
 	struct rte_eth_dev *dev;
diff --git a/lib/librte_ether/rte_ethdev.h b/lib/librte_ether/rte_ethdev.h
index 4f7b64b..f9d282b 100644
--- a/lib/librte_ether/rte_ethdev.h
+++ b/lib/librte_ether/rte_ethdev.h
@@ -1200,6 +1200,17 @@ typedef int (*eth_timesync_read_tx_timestamp_t)(struct rte_eth_dev *dev,
 						struct timespec *timestamp);
 /**< @internal Function used to read a TX IEEE1588/802.1AS timestamp. */
 
+typedef int (*eth_timesync_time_adjust)(struct rte_eth_dev *dev, int64_t);
+/**< @internal Function used to adjust device clock */
+
+typedef int (*eth_timesync_time_get)(struct rte_eth_dev *dev,
+						struct timespec *timestamp);
+/**< @internal Function used to get time from device clock. */
+
+typedef int (*eth_timesync_time_set)(struct rte_eth_dev *dev,
+						struct timespec *timestamp);
+/**< @internal Function used to get time from device clock */
+
 typedef int (*eth_get_reg_length_t)(struct rte_eth_dev *dev);
 /**< @internal Retrieve device register count  */
 
@@ -1394,6 +1405,12 @@ struct eth_dev_ops {
 
 	/** Get DCB information */
 	eth_get_dcb_info get_dcb_info;
+	/** Adjust the device clock */
+	eth_timesync_time_adjust timesync_time_adjust;
+	/** Get the device clock timespec */
+	eth_timesync_time_get timesync_time_get;
+	/** Set the device clock timespec */
+	eth_timesync_time_set timesync_time_set;
 };
 
 /**
@@ -3745,6 +3762,53 @@ extern int rte_eth_timesync_read_tx_timestamp(uint8_t port_id,
 					      struct timespec *timestamp);
 
 /**
+ * Adjust the timesync clock on an Ethernet device..
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param delta
+ *   The adjustment in nanoseconds
+ *
+ * @return
+ *   - 0: Success.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -ENOTSUP: The function is not supported by the Ethernet driver.
+ */
+extern int rte_eth_timesync_time_adjust(uint8_t port_id, int64_t delta);
+
+/**
+ * Read the time from the timesync clock on an Ethernet device.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param time
+ *   Pointer to the timespec struct.
+ *
+ * @return
+ *   - 0: Success.
+ */
+extern int rte_eth_timesync_time_get(uint8_t port_id,
+	      struct timespec *time);
+
+
+/**
+ * Set the time of the timesync clock on an Ethernet device.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param time
+ *   Pointer to the timespec struct.
+ *
+ * @return
+ *   - 0: Success.
+ *   - -EINVAL: No timestamp is available.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -ENOTSUP: The function is not supported by the Ethernet driver.
+ */
+extern int rte_eth_timesync_time_set(uint8_t port_id,
+	      struct timespec *time);
+
+/**
  * Copy pci device info to the Ethernet device data.
  *
  * @param eth_dev
@@ -3757,7 +3821,6 @@ extern int rte_eth_timesync_read_tx_timestamp(uint8_t port_id,
  */
 extern void rte_eth_copy_pci_info(struct rte_eth_dev *eth_dev, struct rte_pci_device *pci_dev);
 
-
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/librte_ether/rte_ether_version.map b/lib/librte_ether/rte_ether_version.map
index 9149aa7..bb9c808 100644
--- a/lib/librte_ether/rte_ether_version.map
+++ b/lib/librte_ether/rte_ether_version.map
@@ -135,5 +135,8 @@ DPDK_2.2 {
 	rte_eth_dev_get_dcb_info;
 	rte_eth_rx_queue_info_get;
 	rte_eth_tx_queue_info_get;
+	rte_eth_timesync_time_adjust;
+	rte_eth_timesync_time_get;
+	rte_eth_timesync_time_set;
 
 } DPDK_2.1;
-- 
2.5.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v4 2/7] net: Add common PTP structures and functions
  2015-11-04 10:06 ` [dpdk-dev] [PATCH v4 0/7] add sample ptp slave application Daniel Mrzyglod
  2015-11-04 10:06   ` [dpdk-dev] [PATCH v4 1/7] ethdev: add additional ieee1588 support functions Daniel Mrzyglod
@ 2015-11-04 10:06   ` Daniel Mrzyglod
  2015-11-04 10:06   ` [dpdk-dev] [PATCH v4 3/7] ixgbe: add additional ieee1588 support functions Daniel Mrzyglod
                     ` (6 subsequent siblings)
  8 siblings, 0 replies; 84+ messages in thread
From: Daniel Mrzyglod @ 2015-11-04 10:06 UTC (permalink / raw)
  To: dev

This patch add common functions and structures used for PTP processing.

Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
---
 lib/librte_net/Makefile  |   2 +-
 lib/librte_net/rte_ptp.h | 105 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 106 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_net/rte_ptp.h

diff --git a/lib/librte_net/Makefile b/lib/librte_net/Makefile
index ad2e482..1d33618 100644
--- a/lib/librte_net/Makefile
+++ b/lib/librte_net/Makefile
@@ -34,7 +34,7 @@ include $(RTE_SDK)/mk/rte.vars.mk
 CFLAGS += $(WERROR_FLAGS) -I$(SRCDIR) -O3
 
 # install includes
-SYMLINK-$(CONFIG_RTE_LIBRTE_NET)-include := rte_ip.h rte_tcp.h rte_udp.h rte_sctp.h rte_icmp.h rte_arp.h
+SYMLINK-$(CONFIG_RTE_LIBRTE_NET)-include := rte_ip.h rte_tcp.h rte_udp.h rte_sctp.h rte_icmp.h rte_arp.h rte_ptp.h
 
 
 include $(RTE_SDK)/mk/rte.install.mk
diff --git a/lib/librte_net/rte_ptp.h b/lib/librte_net/rte_ptp.h
new file mode 100644
index 0000000..8a4c83c
--- /dev/null
+++ b/lib/librte_net/rte_ptp.h
@@ -0,0 +1,105 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define NSEC_PER_SEC             1000000000L
+
+/*
+ * Structure for cyclecounter IEEE1588 functionality.
+ */
+struct cyclecounter {
+	uint64_t (*read)(struct rte_eth_dev *dev);
+	uint64_t mask;
+	uint32_t shift;
+};
+
+/*
+ * Structure to hold and calculate Unix epoch time.
+ */
+struct timecounter {
+	struct cyclecounter *cc;
+	uint64_t cycle_last;
+	uint64_t nsec;
+	uint64_t mask;
+	uint64_t frac;
+};
+
+
+/* Utility functions for PTP/IEEE1588 support. */
+
+static inline uint64_t
+timespec_to_ns(const struct timespec *ts)
+{
+	return ((uint64_t) ts->tv_sec * NSEC_PER_SEC) + ts->tv_nsec;
+}
+
+static inline struct timespec
+ns_to_timespec(uint64_t nsec)
+{
+	struct timespec ts = {0, 0};
+
+	if (nsec == 0)
+		return ts;
+
+	ts.tv_sec = nsec / NSEC_PER_SEC;
+	ts.tv_nsec = nsec % NSEC_PER_SEC;
+
+	return ts;
+}
+
+/*
+ * Converts cycle counter cycles to nanoseconds.
+ */
+static inline uint64_t
+cyclecounter_cycles_to_ns(const struct cyclecounter *cc,
+		      uint64_t cycles, uint64_t mask, uint64_t *frac)
+{
+	uint64_t ns;
+
+	/* Add fractional nanoseconds */
+	ns = cycles + *frac;
+	*frac = ns & mask;
+
+	/* Shift to get only nanoseconds. */
+	return ns >> cc->shift;
+}
+
+/*
+ * Like cyclecounter_cycles_to_ns(), but this is used when
+ * computing a time previous to the stored in the cycle counter.
+ */
+static inline uint64_t
+cyclecounter_cycles_to_ns_backwards(const struct cyclecounter *cc,
+			       uint64_t cycles, uint64_t frac)
+{
+	return ((cycles - frac) >> cc->shift);
+}
-- 
2.5.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v4 3/7] ixgbe: add additional ieee1588 support functions
  2015-11-04 10:06 ` [dpdk-dev] [PATCH v4 0/7] add sample ptp slave application Daniel Mrzyglod
  2015-11-04 10:06   ` [dpdk-dev] [PATCH v4 1/7] ethdev: add additional ieee1588 support functions Daniel Mrzyglod
  2015-11-04 10:06   ` [dpdk-dev] [PATCH v4 2/7] net: Add common PTP structures and functions Daniel Mrzyglod
@ 2015-11-04 10:06   ` Daniel Mrzyglod
  2015-11-04 10:06   ` [dpdk-dev] [PATCH v4 4/7] igb: " Daniel Mrzyglod
                     ` (5 subsequent siblings)
  8 siblings, 0 replies; 84+ messages in thread
From: Daniel Mrzyglod @ 2015-11-04 10:06 UTC (permalink / raw)
  To: dev

Add additional functions to support the existing IEEE1588
functionality and to enable getting, setting and adjusting
the device time.

Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
---
 drivers/net/ixgbe/ixgbe_ethdev.c | 272 +++++++++++++++++++++++++++++++++++++--
 drivers/net/ixgbe/ixgbe_ethdev.h |   3 +
 2 files changed, 264 insertions(+), 11 deletions(-)

diff --git a/drivers/net/ixgbe/ixgbe_ethdev.c b/drivers/net/ixgbe/ixgbe_ethdev.c
index 25966ef..e575f28 100644
--- a/drivers/net/ixgbe/ixgbe_ethdev.c
+++ b/drivers/net/ixgbe/ixgbe_ethdev.c
@@ -126,10 +126,17 @@
 #define IXGBE_HKEY_MAX_INDEX 10
 
 /* Additional timesync values. */
-#define IXGBE_TIMINCA_16NS_SHIFT 24
-#define IXGBE_TIMINCA_INCVALUE   16000000
-#define IXGBE_TIMINCA_INIT       ((0x02 << IXGBE_TIMINCA_16NS_SHIFT) \
-				  | IXGBE_TIMINCA_INCVALUE)
+#define NSEC_PER_SEC             1000000000L
+#define IXGBE_INCVAL_10GB        0x66666666
+#define IXGBE_INCVAL_1GB         0x40000000
+#define IXGBE_INCVAL_100         0x50000000
+#define IXGBE_INCVAL_SHIFT_10GB  28
+#define IXGBE_INCVAL_SHIFT_1GB   24
+#define IXGBE_INCVAL_SHIFT_100   21
+#define IXGBE_INCVAL_SHIFT_82599 7
+#define IXGBE_INCPER_SHIFT_82599 24
+
+#define IXGBE_CYCLECOUNTER_MASK   0xffffffffffffffff
 
 static int eth_ixgbe_dev_init(struct rte_eth_dev *eth_dev);
 static int eth_ixgbe_dev_uninit(struct rte_eth_dev *eth_dev);
@@ -329,6 +336,11 @@ static int ixgbe_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 					    uint32_t flags);
 static int ixgbe_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 					    struct timespec *timestamp);
+static int ixgbe_timesync_time_adjust(struct rte_eth_dev *dev, int64_t delta);
+static int ixgbe_timesync_time_get(struct rte_eth_dev *dev,
+				   struct timespec *timestamp);
+static int ixgbe_timesync_time_set(struct rte_eth_dev *dev,
+				   struct timespec *timestamp);
 
 /*
  * Define VF Stats MACRO for Non "cleared on read" register
@@ -484,6 +496,9 @@ static const struct eth_dev_ops ixgbe_eth_dev_ops = {
 	.get_eeprom           = ixgbe_get_eeprom,
 	.set_eeprom           = ixgbe_set_eeprom,
 	.get_dcb_info         = ixgbe_dev_get_dcb_info,
+	.timesync_time_adjust = ixgbe_timesync_time_adjust,
+	.timesync_time_get    = ixgbe_timesync_time_get,
+	.timesync_time_set    = ixgbe_timesync_time_set,
 };
 
 /*
@@ -5654,20 +5669,232 @@ ixgbe_dev_set_mc_addr_list(struct rte_eth_dev *dev,
 					 ixgbe_dev_addr_list_itr, TRUE);
 }
 
+/*
+ * Register units might not be nanoseconds. This function converts
+ * these units into nanoseconds and adds to the previous time stored.
+ */
+static uint64_t
+timecounter_cycles_to_ns_time(struct timecounter *tc, uint64_t cycle_tstamp)
+{
+	uint64_t delta;
+	uint64_t nsec = tc->nsec, frac = tc->frac;
+
+	delta = (cycle_tstamp - tc->cycle_last) & tc->cc->mask;
+	/*
+	 * Cycle counts that are correctly converted as they
+	 * are between -1/2 max cycle count and +1/2 max cycle count.
+	 */
+	if (delta > (tc->cc->mask / 2)) {
+		delta = (tc->cycle_last - cycle_tstamp) & tc->cc->mask;
+		nsec -= cyclecounter_cycles_to_ns_backwards(tc->cc,
+							    delta, frac);
+	} else {
+		nsec += cyclecounter_cycles_to_ns(tc->cc, delta, tc->mask,
+						  &frac);
+	}
+
+	return nsec;
+}
+
+static uint64_t
+ixgbe_read_timesync_cyclecounter(struct rte_eth_dev *dev)
+{
+	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	uint64_t systime_cycles = 0;
+
+	switch (hw->mac.type) {
+	case ixgbe_mac_X550:
+		/* SYSTIMEL stores ns and SYSTIMEH stores seconds. */
+		systime_cycles = (uint64_t)IXGBE_READ_REG(hw, IXGBE_SYSTIML);
+		systime_cycles += (uint64_t)IXGBE_READ_REG(hw, IXGBE_SYSTIMH)
+				* NSEC_PER_SEC;
+		break;
+	default:
+		systime_cycles |= (uint64_t)IXGBE_READ_REG(hw, IXGBE_SYSTIML);
+		systime_cycles |= (uint64_t)IXGBE_READ_REG(hw, IXGBE_SYSTIMH)
+				<< 32;
+	}
+
+	return systime_cycles;
+}
+
+/*
+ * Get nanoseconds since the last call of this function.
+ */
+static uint64_t
+timecounter_read_ns_delta(struct rte_eth_dev *dev)
+{
+	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	uint64_t cycle_now, cycle_delta;
+	uint64_t ns_offset;
+	struct ixgbe_adapter *adapter =
+				(struct ixgbe_adapter *)dev->data->dev_private;
+
+	/* Read cycle counter. */
+	cycle_now = adapter->tc.cc->read(dev);
+
+	/* Calculate the delta since the last timecounter_read_delta(). */
+	cycle_delta = (cycle_now - adapter->tc.cycle_last) &
+		       adapter->tc.cc->mask;
+
+	/* Convert to nanoseconds. */
+	if (hw->mac.type == ixgbe_mac_X550)
+		/* Registers store directly nanoseconds, no need to convert. */
+		ns_offset = cycle_delta;
+	else
+		ns_offset = cyclecounter_cycles_to_ns(adapter->tc.cc,
+						      cycle_delta,
+						      adapter->tc.mask,
+						      &adapter->tc.frac);
+
+	/*
+	 * Store current cycle counter for next
+	 * timecounter_read_ns_delta() call.
+	 */
+	adapter->tc.cycle_last = cycle_now;
+
+	return ns_offset;
+}
+
+static uint64_t
+timecounter_read(struct rte_eth_dev *dev)
+{
+	uint64_t nsec;
+	struct ixgbe_adapter *adapter =
+			(struct ixgbe_adapter *)dev->data->dev_private;
+
+	/* Increment time by nanoseconds since last call. */
+	nsec = timecounter_read_ns_delta(dev);
+	nsec += adapter->tc.nsec;
+	adapter->tc.nsec = nsec;
+
+	return nsec;
+}
+
+static void
+timecounter_init(struct rte_eth_dev *dev, uint64_t start_time)
+{
+	struct ixgbe_adapter *adapter =
+				(struct ixgbe_adapter *)dev->data->dev_private;
+
+	adapter->tc.cc = &adapter->cc;
+	adapter->tc.cycle_last = adapter->tc.cc->read(dev);
+	adapter->tc.nsec = start_time;
+	adapter->tc.mask = (1ULL << adapter->tc.cc->shift) - 1;
+	adapter->tc.frac = 0;
+}
+
+static void
+ixgbe_start_cyclecounter(struct rte_eth_dev *dev)
+{
+	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct ixgbe_adapter *adapter =
+		(struct ixgbe_adapter *)dev->data->dev_private;
+	struct rte_eth_link link;
+	uint32_t incval = 0;
+	uint32_t shift = 0;
+
+	/* Get current link speed. */
+	memset(&link, 0, sizeof(link));
+	ixgbe_dev_link_update(dev, 1);
+	rte_ixgbe_dev_atomic_read_link_status(dev, &link);
+
+	switch (link.link_speed) {
+	case ETH_LINK_SPEED_100:
+		incval = IXGBE_INCVAL_100;
+		shift = IXGBE_INCVAL_SHIFT_100;
+		break;
+	case ETH_LINK_SPEED_1000:
+		incval = IXGBE_INCVAL_1GB;
+		shift = IXGBE_INCVAL_SHIFT_1GB;
+		break;
+	case ETH_LINK_SPEED_10000:
+	default:
+		incval = IXGBE_INCVAL_10GB;
+		shift = IXGBE_INCVAL_SHIFT_10GB;
+		break;
+	}
+
+	switch (hw->mac.type) {
+	case ixgbe_mac_X550:
+		/* Independent of link speed. */
+		incval = 1;
+		/* Cycles read will be interpreted as ns. */
+		shift = 0;
+		/* Fall-through */
+	case ixgbe_mac_X540:
+		IXGBE_WRITE_REG(hw, IXGBE_TIMINCA, incval);
+		break;
+	case ixgbe_mac_82599EB:
+		incval >>= IXGBE_INCVAL_SHIFT_82599;
+		shift -= IXGBE_INCVAL_SHIFT_82599;
+		IXGBE_WRITE_REG(hw, IXGBE_TIMINCA,
+				(1 << IXGBE_INCPER_SHIFT_82599) | incval);
+		break;
+	default:
+		/* Not supported. */
+		return;
+	}
+
+	memset(&adapter->cc, 0, sizeof(struct cyclecounter));
+	adapter->cc.read = ixgbe_read_timesync_cyclecounter;
+	adapter->cc.mask = IXGBE_CYCLECOUNTER_MASK;
+	adapter->cc.shift = shift;
+}
+
+static int
+ixgbe_timesync_time_adjust(struct rte_eth_dev *dev, int64_t delta)
+{
+	struct ixgbe_adapter *adapter =
+			(struct ixgbe_adapter *)dev->data->dev_private;
+
+	adapter->tc.nsec += delta;
+
+	return 0;
+}
+
+static int
+ixgbe_timesync_time_set(struct rte_eth_dev *dev, struct timespec *ts)
+{
+	uint64_t ns;
+
+	ns = timespec_to_ns(ts);
+	/* Reset the timecounter. */
+	timecounter_init(dev, ns);
+
+	return 0;
+}
+
+static int
+ixgbe_timesync_time_get(struct rte_eth_dev *dev, struct timespec *ts)
+{
+	uint64_t ns;
+
+	ns = timecounter_read(dev);
+	*ts = ns_to_timespec(ns);
+
+	return 0;
+}
+
 static int
 ixgbe_timesync_enable(struct rte_eth_dev *dev)
 {
 	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
 	uint32_t tsync_ctl;
 	uint32_t tsauxc;
+	uint64_t ns;
+	struct timespec zerotime = {0, 0};
 
 	/* Enable system time for platforms where it isn't on by default. */
 	tsauxc = IXGBE_READ_REG(hw, IXGBE_TSAUXC);
 	tsauxc &= ~IXGBE_TSAUXC_DISABLE_SYSTIME;
-	IXGBE_WRITE_REG(hw, IXGBE_TSAUXC, tsauxc);
 
-	/* Start incrementing the register used to timestamp PTP packets. */
-	IXGBE_WRITE_REG(hw, IXGBE_TIMINCA, IXGBE_TIMINCA_INIT);
+	/* Set 0.0 epoch time to initialize timecounter. */
+	ns = timespec_to_ns(&zerotime);
+	ixgbe_start_cyclecounter(dev);
+	timecounter_init(dev, ns);
+
+	IXGBE_WRITE_REG(hw, IXGBE_TSAUXC, tsauxc);
 
 	/* Enable L2 filtering of IEEE1588/802.1AS Ethernet frame types. */
 	IXGBE_WRITE_REG(hw, IXGBE_ETQF(IXGBE_ETQF_FILTER_1588),
@@ -5685,6 +5912,9 @@ ixgbe_timesync_enable(struct rte_eth_dev *dev)
 	tsync_ctl |= IXGBE_TSYNCTXCTL_ENABLED;
 	IXGBE_WRITE_REG(hw, IXGBE_TSYNCTXCTL, tsync_ctl);
 
+	/* After writing to registers should be flush. */
+	IXGBE_WRITE_FLUSH(hw);
+
 	return 0;
 }
 
@@ -5719,9 +5949,13 @@ ixgbe_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 				 uint32_t flags __rte_unused)
 {
 	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct ixgbe_adapter *adapter =
+		(struct ixgbe_adapter *)dev->data->dev_private;
+
 	uint32_t tsync_rxctl;
 	uint32_t rx_stmpl;
 	uint32_t rx_stmph;
+	uint64_t regival = 0;
 
 	tsync_rxctl = IXGBE_READ_REG(hw, IXGBE_TSYNCRXCTL);
 	if ((tsync_rxctl & IXGBE_TSYNCRXCTL_VALID) == 0)
@@ -5729,9 +5963,15 @@ ixgbe_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 
 	rx_stmpl = IXGBE_READ_REG(hw, IXGBE_RXSTMPL);
 	rx_stmph = IXGBE_READ_REG(hw, IXGBE_RXSTMPH);
+	timecounter_read(dev);
 
-	timestamp->tv_sec = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
-	timestamp->tv_nsec = 0;
+	if (hw->mac.type == ixgbe_mac_X550)
+		regival = (uint64_t)((uint64_t)rx_stmph * NSEC_PER_SEC
+			+ rx_stmpl);
+	else
+		regival = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
+	regival = timecounter_cycles_to_ns_time(&adapter->tc, regival);
+	*timestamp = ns_to_timespec(regival);
 
 	return  0;
 }
@@ -5741,9 +5981,13 @@ ixgbe_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 				 struct timespec *timestamp)
 {
 	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct ixgbe_adapter *adapter =
+		(struct ixgbe_adapter *)dev->data->dev_private;
+
 	uint32_t tsync_txctl;
 	uint32_t tx_stmpl;
 	uint32_t tx_stmph;
+	uint64_t regival = 0;
 
 	tsync_txctl = IXGBE_READ_REG(hw, IXGBE_TSYNCTXCTL);
 	if ((tsync_txctl & IXGBE_TSYNCTXCTL_VALID) == 0)
@@ -5751,9 +5995,15 @@ ixgbe_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 
 	tx_stmpl = IXGBE_READ_REG(hw, IXGBE_TXSTMPL);
 	tx_stmph = IXGBE_READ_REG(hw, IXGBE_TXSTMPH);
+	timecounter_read(dev);
 
-	timestamp->tv_sec = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
-	timestamp->tv_nsec = 0;
+	if (hw->mac.type == ixgbe_mac_X550)
+		regival = (uint64_t)((uint64_t)tx_stmph * NSEC_PER_SEC
+			+ tx_stmpl);
+	else
+		regival = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
+	regival = timecounter_cycles_to_ns_time(&adapter->tc, regival);
+	*timestamp = ns_to_timespec(regival);
 
 	return  0;
 }
diff --git a/drivers/net/ixgbe/ixgbe_ethdev.h b/drivers/net/ixgbe/ixgbe_ethdev.h
index 569d678..701efde 100644
--- a/drivers/net/ixgbe/ixgbe_ethdev.h
+++ b/drivers/net/ixgbe/ixgbe_ethdev.h
@@ -37,6 +37,7 @@
 #include "base/ixgbe_dcb_82599.h"
 #include "base/ixgbe_dcb_82598.h"
 #include "ixgbe_bypass.h"
+#include <rte_ptp.h>
 
 /* need update link, bit flag */
 #define IXGBE_FLAG_NEED_LINK_UPDATE (uint32_t)(1 << 0)
@@ -279,6 +280,8 @@ struct ixgbe_adapter {
 
 	bool rx_bulk_alloc_allowed;
 	bool rx_vec_allowed;
+	struct cyclecounter cc;
+	struct timecounter tc;
 };
 
 #define IXGBE_DEV_PRIVATE_TO_HW(adapter)\
-- 
2.5.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v4 4/7] igb: add additional ieee1588 support functions
  2015-11-04 10:06 ` [dpdk-dev] [PATCH v4 0/7] add sample ptp slave application Daniel Mrzyglod
                     ` (2 preceding siblings ...)
  2015-11-04 10:06   ` [dpdk-dev] [PATCH v4 3/7] ixgbe: add additional ieee1588 support functions Daniel Mrzyglod
@ 2015-11-04 10:06   ` Daniel Mrzyglod
  2015-11-04 10:06   ` [dpdk-dev] [PATCH v4 5/7] i40e: " Daniel Mrzyglod
                     ` (4 subsequent siblings)
  8 siblings, 0 replies; 84+ messages in thread
From: Daniel Mrzyglod @ 2015-11-04 10:06 UTC (permalink / raw)
  To: dev

From: Pablo de Lara <pablo.de.lara.guarch@intel.com>

Add additional functions to support the existing IEEE1588
functionality and to enable getting, setting and adjusting
the device time.

Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
---
 drivers/net/e1000/e1000_ethdev.h |   3 +
 drivers/net/e1000/igb_ethdev.c   | 299 +++++++++++++++++++++++++++++++++++++--
 2 files changed, 292 insertions(+), 10 deletions(-)

diff --git a/drivers/net/e1000/e1000_ethdev.h b/drivers/net/e1000/e1000_ethdev.h
index 3c6f613..247002a 100644
--- a/drivers/net/e1000/e1000_ethdev.h
+++ b/drivers/net/e1000/e1000_ethdev.h
@@ -33,6 +33,7 @@
 
 #ifndef _E1000_ETHDEV_H_
 #define _E1000_ETHDEV_H_
+#include <rte_ptp.h>
 
 /* need update link, bit flag */
 #define E1000_FLAG_NEED_LINK_UPDATE (uint32_t)(1 << 0)
@@ -254,6 +255,8 @@ struct e1000_adapter {
 	struct e1000_vf_info    *vfdata;
 	struct e1000_filter_info filter;
 	bool stopped;
+	struct cyclecounter cc;
+	struct timecounter tc;
 };
 
 #define E1000_DEV_PRIVATE(adapter) \
diff --git a/drivers/net/e1000/igb_ethdev.c b/drivers/net/e1000/igb_ethdev.c
index cd7f7c1..edb7456 100644
--- a/drivers/net/e1000/igb_ethdev.c
+++ b/drivers/net/e1000/igb_ethdev.c
@@ -78,10 +78,11 @@
 #define IGB_8_BIT_MASK   UINT8_MAX
 
 /* Additional timesync values. */
-#define E1000_ETQF_FILTER_1588 3
-#define E1000_TIMINCA_INCVALUE 16000000
-#define E1000_TIMINCA_INIT     ((0x02 << E1000_TIMINCA_16NS_SHIFT) \
-				| E1000_TIMINCA_INCVALUE)
+#define E1000_CYCLECOUNTER_MASK      0xffffffffffffffff
+#define E1000_ETQF_FILTER_1588       3
+#define IGB_82576_TSYNC_SHIFT        16
+#define E1000_INCPERIOD_82576        (1 << E1000_TIMINCA_16NS_SHIFT)
+#define E1000_INCVALUE_82576         (16 << IGB_82576_TSYNC_SHIFT)
 #define E1000_TSAUXC_DISABLE_SYSTIME 0x80000000
 
 static int  eth_igb_configure(struct rte_eth_dev *dev);
@@ -236,6 +237,11 @@ static int igb_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 					  uint32_t flags);
 static int igb_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 					  struct timespec *timestamp);
+static int igb_timesync_time_adjust(struct rte_eth_dev *dev, int64_t delta);
+static int igb_timesync_time_get(struct rte_eth_dev *dev,
+				 struct timespec *timestamp);
+static int igb_timesync_time_set(struct rte_eth_dev *dev,
+				 struct timespec *timestamp);
 static int eth_igb_rx_queue_intr_enable(struct rte_eth_dev *dev,
 					uint16_t queue_id);
 static int eth_igb_rx_queue_intr_disable(struct rte_eth_dev *dev,
@@ -349,6 +355,9 @@ static const struct eth_dev_ops eth_igb_ops = {
 	.get_eeprom_length    = eth_igb_get_eeprom_length,
 	.get_eeprom           = eth_igb_get_eeprom,
 	.set_eeprom           = eth_igb_set_eeprom,
+	.timesync_time_adjust  = igb_timesync_time_adjust,
+	.timesync_time_get     = igb_timesync_time_get,
+	.timesync_time_set     = igb_timesync_time_set,
 };
 
 /*
@@ -4165,20 +4174,248 @@ eth_igb_set_mc_addr_list(struct rte_eth_dev *dev,
 	return 0;
 }
 
+/*
+ * Register units might not be nanoseconds. This function converts
+ * these units into nanoseconds and adds to the previous time stored.
+ */
+static uint64_t
+timecounter_cycles_to_ns_time(struct timecounter *tc, uint64_t cycle_tstamp)
+{
+	uint64_t delta;
+	uint64_t nsec = tc->nsec, frac = tc->frac;
+
+	delta = (cycle_tstamp - tc->cycle_last) & tc->cc->mask;
+	/*
+	 * Cycle counts that are correctly converted as they
+	 * are between -1/2 max cycle count and +1/2 max cycle count.
+	 */
+	if (delta > (tc->cc->mask / 2)) {
+		delta = (tc->cycle_last - cycle_tstamp) & tc->cc->mask;
+		nsec -= cyclecounter_cycles_to_ns_backwards(tc->cc, delta,
+							    frac);
+	} else {
+		nsec += cyclecounter_cycles_to_ns(tc->cc, delta, tc->mask,
+						  &frac);
+	}
+
+	return nsec;
+}
+
+static uint64_t
+igb_read_timesync_cyclecounter(struct rte_eth_dev *dev)
+{
+	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	uint64_t systime_cycles = 0;
+
+	switch (hw->mac.type) {
+	case e1000_i210:
+	case e1000_i211:
+		/*
+		 * Need to read System Time Residue Register to be able
+		 * to read the other two registers.
+		 */
+		E1000_READ_REG(hw, E1000_SYSTIMR);
+		/* SYSTIMEL stores ns and SYSTIMEH stores seconds. */
+		systime_cycles = (uint64_t)E1000_READ_REG(hw, E1000_SYSTIML);
+		systime_cycles += (uint64_t)E1000_READ_REG(hw, E1000_SYSTIMH)
+				* NSEC_PER_SEC;
+		break;
+	case e1000_82580:
+	case e1000_i350:
+	case e1000_i354:
+		/*
+		 * Need to read System Time Residue Register to be able
+		 * to read the other two registers.
+		 */
+		E1000_READ_REG(hw, E1000_SYSTIMR);
+		systime_cycles |= (uint64_t)E1000_READ_REG(hw, E1000_SYSTIML);
+		/* Only the 8 LSB are valid. */
+		systime_cycles |= (uint64_t)(E1000_READ_REG(hw, E1000_SYSTIMH)
+				& 0xff) << 32;
+		break;
+	default:
+		systime_cycles |= (uint64_t)E1000_READ_REG(hw, E1000_SYSTIML);
+		systime_cycles |= (uint64_t)E1000_READ_REG(hw, E1000_SYSTIMH)
+				<< 32;
+		break;
+	}
+
+	return systime_cycles;
+}
+
+/*
+ * Get nanoseconds since the last call of this function.
+ */
+static uint64_t
+timecounter_read_ns_delta(struct rte_eth_dev *dev)
+{
+	uint64_t cycle_now, cycle_delta;
+	uint64_t ns_offset;
+	struct e1000_adapter *adapter =
+				(struct e1000_adapter *)dev->data->dev_private;
+	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+
+	/* Read cycle counter. */
+	cycle_now = adapter->tc.cc->read(dev);
+
+	/* Calculate the delta since the last timecounter_read_delta(). */
+	cycle_delta = (cycle_now - adapter->tc.cycle_last) &
+		       adapter->tc.cc->mask;
+
+	/* Convert to nanoseconds. */
+	switch (hw->mac.type) {
+	case e1000_i210:
+	case e1000_i211:
+	case e1000_82580:
+	case e1000_i350:
+	case e1000_i354:
+		/* Registers store directly nanoseconds, no need to convert. */
+		ns_offset = cycle_delta;
+		break;
+	default:
+		ns_offset = cyclecounter_cycles_to_ns(adapter->tc.cc,
+						      cycle_delta,
+						      adapter->tc.mask,
+						      &adapter->tc.frac);
+	}
+
+	/*
+	 * Store current cycle counter for next
+	 * timecounter_read_ns_delta() call.
+	 */
+	adapter->tc.cycle_last = cycle_now;
+
+	return ns_offset;
+}
+
+static uint64_t
+timecounter_read(struct rte_eth_dev *dev)
+{
+	uint64_t nsec;
+	struct e1000_adapter *adapter =
+			(struct e1000_adapter *)dev->data->dev_private;
+
+	/* increment time by nanoseconds since last call */
+	nsec = timecounter_read_ns_delta(dev);
+	nsec += adapter->tc.nsec;
+	adapter->tc.nsec = nsec;
+
+	return nsec;
+}
+
+static void
+timecounter_init(struct rte_eth_dev *dev, uint64_t start_time)
+{
+	struct e1000_adapter *adapter =
+				(struct e1000_adapter *)dev->data->dev_private;
+	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+
+	adapter->tc.cc = &adapter->cc;
+	adapter->tc.cycle_last = adapter->tc.cc->read(dev);
+	adapter->tc.nsec = start_time;
+	switch (hw->mac.type) {
+	case e1000_82580:
+	case e1000_i350:
+	case e1000_i354:
+		/* 32 LSB bits + 8 MSB bits = 40 bits */
+		adapter->tc.mask = (1ULL << 40) - 1;
+	default:
+		adapter->tc.mask = (1ULL << adapter->tc.cc->shift) - 1;
+	}
+	adapter->tc.frac = 0;
+}
+
+static void
+igb_start_cyclecounter(struct rte_eth_dev *dev)
+{
+	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct e1000_adapter *adapter =
+		(struct e1000_adapter *)dev->data->dev_private;
+	uint32_t incval = 1;
+	uint32_t shift = 0;
+
+	switch (hw->mac.type) {
+	case e1000_i210:
+	case e1000_i211:
+	case e1000_82580:
+	case e1000_i350:
+	case e1000_i354:
+		/*
+		 * Start incrementing the register
+		 * used to timestamp PTP packets.
+		 */
+		E1000_WRITE_REG(hw, E1000_TIMINCA, incval);
+		break;
+	case e1000_82576:
+		incval = E1000_INCVALUE_82576;
+		shift = IGB_82576_TSYNC_SHIFT;
+		E1000_WRITE_REG(hw, E1000_TIMINCA,
+				E1000_INCPERIOD_82576 | incval);
+		break;
+	default:
+		/* Not supported */
+		return;
+	}
+
+	memset(&adapter->cc, 0, sizeof(struct cyclecounter));
+	adapter->cc.read = igb_read_timesync_cyclecounter;
+	adapter->cc.mask = E1000_CYCLECOUNTER_MASK;
+	adapter->cc.shift = shift;
+}
+
+static int
+igb_timesync_time_adjust(struct rte_eth_dev *dev, int64_t delta)
+{
+	struct e1000_adapter *adapter =
+			(struct e1000_adapter *)dev->data->dev_private;
+
+	adapter->tc.nsec += delta;
+
+	return 0;
+}
+
+static int
+igb_timesync_time_set(struct rte_eth_dev *dev, struct timespec *ts)
+{
+	uint64_t ns;
+
+	ns = timespec_to_ns(ts);
+
+	/* Reset the timecounter. */
+	timecounter_init(dev, ns);
+
+	return 0;
+}
+
+static int
+igb_timesync_time_get(struct rte_eth_dev *dev, struct timespec *ts)
+{
+	uint64_t ns;
+
+	ns = timecounter_read(dev);
+	*ts = ns_to_timespec(ns);
+
+	return 0;
+}
+
 static int
 igb_timesync_enable(struct rte_eth_dev *dev)
 {
 	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
 	uint32_t tsync_ctl;
 	uint32_t tsauxc;
+	uint64_t ns;
+	struct timespec zerotime = {0, 0};
 
 	/* Enable system time for it isn't on by default. */
 	tsauxc = E1000_READ_REG(hw, E1000_TSAUXC);
 	tsauxc &= ~E1000_TSAUXC_DISABLE_SYSTIME;
 	E1000_WRITE_REG(hw, E1000_TSAUXC, tsauxc);
 
-	/* Start incrementing the register used to timestamp PTP packets. */
-	E1000_WRITE_REG(hw, E1000_TIMINCA, E1000_TIMINCA_INIT);
+	/* Set 0.0 epoch time to initialize timecounter. */
+	ns = timespec_to_ns(&zerotime);
+	igb_start_cyclecounter(dev);
+	timecounter_init(dev, ns);
 
 	/* Enable L2 filtering of IEEE1588/802.1AS Ethernet frame types. */
 	E1000_WRITE_REG(hw, E1000_ETQF(E1000_ETQF_FILTER_1588),
@@ -4230,9 +4467,13 @@ igb_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 			       uint32_t flags __rte_unused)
 {
 	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct e1000_adapter *adapter =
+			(struct e1000_adapter *)dev->data->dev_private;
+
 	uint32_t tsync_rxctl;
 	uint32_t rx_stmpl;
 	uint32_t rx_stmph;
+	uint64_t regival = 0;
 
 	tsync_rxctl = E1000_READ_REG(hw, E1000_TSYNCRXCTL);
 	if ((tsync_rxctl & E1000_TSYNCRXCTL_VALID) == 0)
@@ -4240,9 +4481,26 @@ igb_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 
 	rx_stmpl = E1000_READ_REG(hw, E1000_RXSTMPL);
 	rx_stmph = E1000_READ_REG(hw, E1000_RXSTMPH);
+	timecounter_read(dev);
+
+	switch (hw->mac.type) {
+	case e1000_82580:
+	case e1000_i350:
+	case e1000_i354:
+		regival = (uint64_t)((((uint64_t)(rx_stmph & 0xff)) << 32)
+			| rx_stmpl);
+		break;
+	case e1000_i210:
+	case e1000_i211:
+		regival = (uint64_t)((uint64_t)rx_stmph * NSEC_PER_SEC
+			+ rx_stmpl);
+		break;
+	default:
+		regival = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
+	}
 
-	timestamp->tv_sec = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
-	timestamp->tv_nsec = 0;
+	regival = timecounter_cycles_to_ns_time(&adapter->tc, regival);
+	*timestamp = ns_to_timespec(regival);
 
 	return  0;
 }
@@ -4252,6 +4510,10 @@ igb_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 			       struct timespec *timestamp)
 {
 	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct e1000_adapter *adapter =
+			(struct e1000_adapter *)dev->data->dev_private;
+	uint64_t regival = 0;
+
 	uint32_t tsync_txctl;
 	uint32_t tx_stmpl;
 	uint32_t tx_stmph;
@@ -4262,9 +4524,26 @@ igb_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 
 	tx_stmpl = E1000_READ_REG(hw, E1000_TXSTMPL);
 	tx_stmph = E1000_READ_REG(hw, E1000_TXSTMPH);
+	timecounter_read(dev);
+
+	switch (hw->mac.type) {
+	case e1000_82580:
+	case e1000_i350:
+	case e1000_i354:
+		regival = (uint64_t)((((uint64_t)(tx_stmph & 0xff)) << 32)
+			| tx_stmpl);
+		break;
+	case e1000_i210:
+	case e1000_i211:
+		regival = (uint64_t)((uint64_t)tx_stmph * NSEC_PER_SEC
+			+ tx_stmpl);
+		break;
+	default:
+		regival = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
+	}
 
-	timestamp->tv_sec = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
-	timestamp->tv_nsec = 0;
+	regival = timecounter_cycles_to_ns_time(&adapter->tc, regival);
+	*timestamp = ns_to_timespec(regival);
 
 	return  0;
 }
-- 
2.5.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v4 5/7] i40e: add additional ieee1588 support functions
  2015-11-04 10:06 ` [dpdk-dev] [PATCH v4 0/7] add sample ptp slave application Daniel Mrzyglod
                     ` (3 preceding siblings ...)
  2015-11-04 10:06   ` [dpdk-dev] [PATCH v4 4/7] igb: " Daniel Mrzyglod
@ 2015-11-04 10:06   ` Daniel Mrzyglod
  2015-11-04 10:06   ` [dpdk-dev] [PATCH v4 6/7] example: PTP client slave minimal implementation Daniel Mrzyglod
                     ` (3 subsequent siblings)
  8 siblings, 0 replies; 84+ messages in thread
From: Daniel Mrzyglod @ 2015-11-04 10:06 UTC (permalink / raw)
  To: dev

From: Pablo de Lara <pablo.de.lara.guarch@intel.com>

Add additional functions to support the existing IEEE1588
functionality and to enable getting, setting and adjusting
the device time.

Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
---
 drivers/net/i40e/i40e_ethdev.c | 192 ++++++++++++++++++++++++++++++++++++-----
 drivers/net/i40e/i40e_ethdev.h |   5 ++
 2 files changed, 177 insertions(+), 20 deletions(-)

diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 34acc8c..0c9f630 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -129,11 +129,13 @@
 	(1UL << RTE_ETH_FLOW_NONFRAG_IPV6_OTHER) | \
 	(1UL << RTE_ETH_FLOW_L2_PAYLOAD))
 
-#define I40E_PTP_40GB_INCVAL  0x0199999999ULL
-#define I40E_PTP_10GB_INCVAL  0x0333333333ULL
-#define I40E_PTP_1GB_INCVAL   0x2000000000ULL
-#define I40E_PRTTSYN_TSYNENA  0x80000000
-#define I40E_PRTTSYN_TSYNTYPE 0x0e000000
+/* Additional timesync values. */
+#define I40E_PTP_40GB_INCVAL     0x0199999999ULL
+#define I40E_PTP_10GB_INCVAL     0x0333333333ULL
+#define I40E_PTP_1GB_INCVAL      0x2000000000ULL
+#define I40E_PRTTSYN_TSYNENA     0x80000000
+#define I40E_PRTTSYN_TSYNTYPE    0x0e000000
+#define I40E_CYCLECOUNTER_MASK   0xffffffffffffffff
 
 #define I40E_MAX_PERCENT            100
 #define I40E_DEFAULT_DCB_APP_NUM    1
@@ -404,6 +406,11 @@ static int i40e_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 static int i40e_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 					   struct timespec *timestamp);
 static void i40e_read_stats_registers(struct i40e_pf *pf, struct i40e_hw *hw);
+static int i40e_timesync_time_adjust(struct rte_eth_dev *dev, int64_t delta);
+static int i40e_timesync_time_get(struct rte_eth_dev *dev,
+				  struct timespec *timestamp);
+static int i40e_timesync_time_set(struct rte_eth_dev *dev,
+				  struct timespec *timestamp);
 
 
 static const struct rte_pci_id pci_id_i40e_map[] = {
@@ -468,6 +475,9 @@ static const struct eth_dev_ops i40e_eth_dev_ops = {
 	.timesync_read_rx_timestamp   = i40e_timesync_read_rx_timestamp,
 	.timesync_read_tx_timestamp   = i40e_timesync_read_tx_timestamp,
 	.get_dcb_info                 = i40e_dev_get_dcb_info,
+	.timesync_time_adjust         = i40e_timesync_time_adjust,
+	.timesync_time_get            = i40e_timesync_time_get,
+	.timesync_time_set            = i40e_timesync_time_set,
 };
 
 /* store statistics names and its offset in stats structure */
@@ -7544,17 +7554,95 @@ i40e_mirror_rule_reset(struct rte_eth_dev *dev, uint8_t sw_id)
 	return 0;
 }
 
-static int
-i40e_timesync_enable(struct rte_eth_dev *dev)
+/*
+ * Adds the new cycles (in nanoseconds) to the previous time stored.
+ */
+static uint64_t
+timecounter_cycles_to_ns_time(struct timecounter *tc, uint64_t cycle_tstamp)
+{
+	uint64_t delta = (cycle_tstamp - tc->cycle_last);
+	uint64_t nsec = tc->nsec;
+
+	nsec += delta;
+
+	return nsec;
+}
+
+static uint64_t
+i40e_read_timesync_cyclecounter(struct rte_eth_dev *dev)
 {
 	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
-	struct rte_eth_link *link = &dev->data->dev_link;
-	uint32_t tsync_ctl_l;
-	uint32_t tsync_ctl_h;
+	uint64_t systim_cycles = 0;
+
+	systim_cycles |= (uint64_t)I40E_READ_REG(hw, I40E_PRTTSYN_TIME_L);
+	systim_cycles |= (uint64_t)I40E_READ_REG(hw, I40E_PRTTSYN_TIME_H)
+			<< 32;
+
+	return systim_cycles;
+}
+
+static uint64_t
+timecounter_read_ns_delta(struct rte_eth_dev *dev)
+{
+	uint64_t cycle_now, cycle_delta;
+	struct i40e_adapter *adapter =
+			(struct i40e_adapter *)dev->data->dev_private;
+
+	/* Read cycle counter. */
+	cycle_now = adapter->tc.cc->read(dev);
+
+	/* Calculate the delta since the last timecounter_read_delta(). */
+	cycle_delta = (cycle_now - adapter->tc.cycle_last);
+
+	/* Update time stamp of timecounter_read_delta() call. */
+	adapter->tc.cycle_last = cycle_now;
+
+	/* Delta already in nanoseconds. */
+	return cycle_delta;
+}
+
+static uint64_t
+timecounter_read(struct rte_eth_dev *dev)
+{
+	uint64_t nsec;
+	struct i40e_adapter *adapter =
+			(struct i40e_adapter *)dev->data->dev_private;
+
+	/* Increment time by nanoseconds since last call. */
+	nsec = timecounter_read_ns_delta(dev);
+	nsec += adapter->tc.nsec;
+	adapter->tc.nsec = nsec;
+
+	return nsec;
+}
+
+static void
+timecounter_init(struct rte_eth_dev *dev,
+		      uint64_t start_time)
+{
+	struct i40e_adapter *adapter =
+			(struct i40e_adapter *)dev->data->dev_private;
+	adapter->tc.cc = &adapter->cc;
+	adapter->tc.cycle_last = adapter->tc.cc->read(dev);
+	adapter->tc.nsec = start_time;
+}
+
+static void
+i40e_start_cyclecounter(struct rte_eth_dev *dev)
+{
+	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct i40e_adapter *adapter =
+			(struct i40e_adapter *)dev->data->dev_private;
+	struct rte_eth_link link;
 	uint32_t tsync_inc_l;
 	uint32_t tsync_inc_h;
 
-	switch (link->link_speed) {
+	/* Get current link speed. */
+	memset(&link, 0, sizeof(link));
+	i40e_dev_link_update(dev, 1);
+	rte_i40e_dev_atomic_read_link_status(dev, &link);
+
+	switch (link.link_speed) {
 	case ETH_LINK_SPEED_40G:
 		tsync_inc_l = I40E_PTP_40GB_INCVAL & 0xFFFFFFFF;
 		tsync_inc_h = I40E_PTP_40GB_INCVAL >> 32;
@@ -7572,6 +7660,63 @@ i40e_timesync_enable(struct rte_eth_dev *dev)
 		tsync_inc_h = 0x0;
 	}
 
+	/* Set the timesync increment value. */
+	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_L, tsync_inc_l);
+	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_H, tsync_inc_h);
+
+	memset(&adapter->cc, 0, sizeof(struct cyclecounter));
+	adapter->cc.read = i40e_read_timesync_cyclecounter;
+}
+
+static int
+i40e_timesync_time_adjust(struct rte_eth_dev *dev, int64_t delta)
+{
+	struct i40e_adapter *adapter =
+			(struct i40e_adapter *)dev->data->dev_private;
+
+	adapter->tc.nsec += delta;
+
+	return 0;
+}
+
+static int
+i40e_timesync_time_set(struct rte_eth_dev *dev, struct timespec *ts)
+{
+	uint64_t ns;
+
+	ns = timespec_to_ns(ts);
+
+	/* Reset the timecounter. */
+	timecounter_init(dev, ns);
+
+	return 0;
+}
+
+static int
+i40e_timesync_time_get(struct rte_eth_dev *dev, struct timespec *ts)
+{
+	uint64_t ns;
+
+	ns = timecounter_read(dev);
+	*ts = ns_to_timespec(ns);
+
+	return 0;
+}
+
+static int
+i40e_timesync_enable(struct rte_eth_dev *dev)
+{
+	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	uint32_t tsync_ctl_l;
+	uint32_t tsync_ctl_h;
+	uint64_t ns;
+	struct timespec zerotime = {0, 0};
+
+	/* Set 0.0 epoch time to initialize timecounter. */
+	ns = timespec_to_ns(&zerotime);
+	i40e_start_cyclecounter(dev);
+	timecounter_init(dev, ns);
+
 	/* Clear timesync registers. */
 	I40E_READ_REG(hw, I40E_PRTTSYN_STAT_0);
 	I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_H);
@@ -7581,10 +7726,6 @@ i40e_timesync_enable(struct rte_eth_dev *dev)
 	I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_L(3));
 	I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_H);
 
-	/* Set the timesync increment value. */
-	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_L, tsync_inc_l);
-	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_H, tsync_inc_h);
-
 	/* Enable timestamping of PTP packets. */
 	tsync_ctl_l = I40E_READ_REG(hw, I40E_PRTTSYN_CTL0);
 	tsync_ctl_l |= I40E_PRTTSYN_TSYNENA;
@@ -7616,7 +7757,7 @@ i40e_timesync_disable(struct rte_eth_dev *dev)
 	I40E_WRITE_REG(hw, I40E_PRTTSYN_CTL0, tsync_ctl_l);
 	I40E_WRITE_REG(hw, I40E_PRTTSYN_CTL1, tsync_ctl_h);
 
-	/* Set the timesync increment value. */
+	/* Reset the timesync increment value. */
 	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_L, 0x0);
 	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_H, 0x0);
 
@@ -7628,10 +7769,14 @@ i40e_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 				struct timespec *timestamp, uint32_t flags)
 {
 	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct i40e_adapter *adapter =
+		(struct i40e_adapter *)dev->data->dev_private;
+
 	uint32_t sync_status;
 	uint32_t rx_stmpl;
 	uint32_t rx_stmph;
 	uint32_t index = flags & 0x03;
+	uint64_t regival = 0;
 
 	sync_status = I40E_READ_REG(hw, I40E_PRTTSYN_STAT_1);
 	if ((sync_status & (1 << index)) == 0)
@@ -7639,9 +7784,11 @@ i40e_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 
 	rx_stmpl = I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_L(index));
 	rx_stmph = I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_H(index));
+	timecounter_read(dev);
 
-	timestamp->tv_sec = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
-	timestamp->tv_nsec = 0;
+	regival = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
+	regival = timecounter_cycles_to_ns_time(&adapter->tc, regival);
+	*timestamp = ns_to_timespec(regival);
 
 	return  0;
 }
@@ -7651,9 +7798,13 @@ i40e_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 				struct timespec *timestamp)
 {
 	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct i40e_adapter *adapter =
+		(struct i40e_adapter *)dev->data->dev_private;
+
 	uint32_t sync_status;
 	uint32_t tx_stmpl;
 	uint32_t tx_stmph;
+	uint64_t regival = 0;
 
 	sync_status = I40E_READ_REG(hw, I40E_PRTTSYN_STAT_0);
 	if ((sync_status & I40E_PRTTSYN_STAT_0_TXTIME_MASK) == 0)
@@ -7662,8 +7813,9 @@ i40e_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 	tx_stmpl = I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_L);
 	tx_stmph = I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_H);
 
-	timestamp->tv_sec = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
-	timestamp->tv_nsec = 0;
+	regival = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
+	regival = timecounter_cycles_to_ns_time(&adapter->tc, regival);
+	*timestamp = ns_to_timespec(regival);
 
 	return  0;
 }
diff --git a/drivers/net/i40e/i40e_ethdev.h b/drivers/net/i40e/i40e_ethdev.h
index c64f83b..e3ebc8a 100644
--- a/drivers/net/i40e/i40e_ethdev.h
+++ b/drivers/net/i40e/i40e_ethdev.h
@@ -35,6 +35,7 @@
 #define _I40E_ETHDEV_H_
 
 #include <rte_eth_ctrl.h>
+#include <rte_ptp.h>
 
 #define I40E_VLAN_TAG_SIZE        4
 
@@ -511,6 +512,10 @@ struct i40e_adapter {
 	bool rx_vec_allowed;
 	bool tx_simple_allowed;
 	bool tx_vec_allowed;
+
+	/* for PTP */
+	struct cyclecounter cc;
+	struct timecounter tc;
 };
 
 int i40e_dev_switch_queues(struct i40e_pf *pf, bool on);
-- 
2.5.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v4 6/7] example: PTP client slave minimal implementation
  2015-11-04 10:06 ` [dpdk-dev] [PATCH v4 0/7] add sample ptp slave application Daniel Mrzyglod
                     ` (4 preceding siblings ...)
  2015-11-04 10:06   ` [dpdk-dev] [PATCH v4 5/7] i40e: " Daniel Mrzyglod
@ 2015-11-04 10:06   ` Daniel Mrzyglod
  2015-11-04 10:06   ` [dpdk-dev] [PATCH v4 7/7] doc: add a PTPCLIENT sample guide Daniel Mrzyglod
                     ` (2 subsequent siblings)
  8 siblings, 0 replies; 84+ messages in thread
From: Daniel Mrzyglod @ 2015-11-04 10:06 UTC (permalink / raw)
  To: dev

Add a sample application that acts as a PTP slave using the
DPDK ieee1588 functions.

Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
---
 MAINTAINERS                          |   3 +
 doc/guides/rel_notes/release_2_2.rst |   5 +
 examples/Makefile                    |   1 +
 examples/ptpclient/Makefile          |  56 +++
 examples/ptpclient/ptpclient.c       | 779 +++++++++++++++++++++++++++++++++++
 5 files changed, 844 insertions(+)
 create mode 100644 examples/ptpclient/Makefile
 create mode 100644 examples/ptpclient/ptpclient.c

diff --git a/MAINTAINERS b/MAINTAINERS
index c8be5d2..0638665 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -520,3 +520,6 @@ F: examples/tep_termination/
 F: examples/vmdq/
 F: examples/vmdq_dcb/
 F: doc/guides/sample_app_ug/vmdq_dcb_forwarding.rst
+
+M: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
+F: examples/ptpclient
diff --git a/doc/guides/rel_notes/release_2_2.rst b/doc/guides/rel_notes/release_2_2.rst
index e39d422..159450f 100644
--- a/doc/guides/rel_notes/release_2_2.rst
+++ b/doc/guides/rel_notes/release_2_2.rst
@@ -90,6 +90,11 @@ New Features
 
 * **Added port hotplug support to xenvirt.**
 
+* **ptpclient: simple PTP slave client.**
+
+  Add a sample application that acts as a PTP slave using the
+  DPDK ieee1588 functions.
+
 
 Resolved Issues
 ---------------
diff --git a/examples/Makefile b/examples/Makefile
index b4eddbd..4672534 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -74,5 +74,6 @@ DIRS-$(CONFIG_RTE_LIBRTE_XEN_DOM0) += vhost_xen
 DIRS-y += vmdq
 DIRS-y += vmdq_dcb
 DIRS-$(CONFIG_RTE_LIBRTE_POWER) += vm_power_manager
+DIRS-$(CONFIG_RTE_LIBRTE_IEEE1588) += ptpclient
 
 include $(RTE_SDK)/mk/rte.extsubdir.mk
diff --git a/examples/ptpclient/Makefile b/examples/ptpclient/Makefile
new file mode 100644
index 0000000..b77cf71
--- /dev/null
+++ b/examples/ptpclient/Makefile
@@ -0,0 +1,56 @@
+#   BSD LICENSE
+#
+#   Copyright(c) 2015 Intel Corporation. All rights reserved.
+#   All rights reserved.
+#
+#   Redistribution and use in source and binary forms, with or without
+#   modification, are permitted provided that the following conditions
+#   are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in
+#       the documentation and/or other materials provided with the
+#       distribution.
+#     * Neither the name of Intel Corporation nor the names of its
+#       contributors may be used to endorse or promote products derived
+#       from this software without specific prior written permission.
+#
+#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+
+# Default target, can be overriddegitn by command line or environment
+RTE_TARGET ?= x86_64-native-linuxapp-gcc
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# binary name
+APP = ptpclient
+
+# all source are stored in SRCS-y
+SRCS-y := ptpclient.c
+
+CFLAGS += -O3
+CFLAGS += $(WERROR_FLAGS)
+
+# workaround for a gcc bug with noreturn attribute
+# http://gcc.gnu.org/bugzilla/show_bug.cgi?id=12603
+ifeq ($(CONFIG_RTE_TOOLCHAIN_GCC),y)
+CFLAGS_main.o += -Wno-return-type
+endif
+
+include $(RTE_SDK)/mk/rte.extapp.mk
diff --git a/examples/ptpclient/ptpclient.c b/examples/ptpclient/ptpclient.c
new file mode 100644
index 0000000..2a9698b
--- /dev/null
+++ b/examples/ptpclient/ptpclient.c
@@ -0,0 +1,779 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This Application is a simple Layer 2 PTP v2 client
+ * IT shows T1-T4 values which are used to synchronize PHC clock.
+ * if -T 1 parameter is used Linux kernel Clock is synchronized with ptp clock
+ */
+
+#include <stdint.h>
+#include <inttypes.h>
+#include <rte_eal.h>
+#include <rte_ethdev.h>
+#include <rte_cycles.h>
+#include <rte_lcore.h>
+#include <rte_mbuf.h>
+#include <rte_ip.h>
+#include <limits.h>
+#include <sys/time.h>
+#include <getopt.h>
+
+#define RX_RING_SIZE 128
+#define TX_RING_SIZE 512
+
+#define NUM_MBUFS            8191
+#define MBUF_CACHE_SIZE       250
+
+/* Values for the PTP messageType field */
+#define SYNC                  0x0
+#define DELAY_REQ             0x1
+#define PDELAY_REQ            0x2
+#define PDELAY_RESP           0x3
+#define FOLLOW_UP             0x8
+#define DELAY_RESP            0x9
+#define PDELAY_RESP_FOLLOW_UP 0xA
+#define ANNOUNCE              0xB
+#define SIGNALING             0xC
+#define MANAGEMENT            0xD
+
+#define NSEC_PER_SEC        1000000000L
+#define KERNEL_TIME_ADJUST_LIMIT  20000
+#define PTP_PROTOCOL             0x88F7
+
+struct rte_mempool *mbuf_pool;
+uint32_t ptp_enabled_port_mask;
+uint8_t ptp_enabled_port_nb;
+static uint8_t ptp_enabled_ports[RTE_MAX_ETHPORTS];
+
+static const struct rte_eth_conf port_conf_default = {
+	.rxmode = { .max_rx_pkt_len = ETHER_MAX_LEN }
+};
+
+static const struct ether_addr ether_multicast = {
+	.addr_bytes = {0x01, 0x1b, 0x19, 0x0, 0x0, 0x0}
+};
+
+/* Structs used for PTP handling */
+struct tstamp {
+	uint16_t   sec_msb;
+	uint32_t   sec_lsb;
+	uint32_t   ns;
+}  __attribute__((packed));
+
+struct clock_identity {
+	uint8_t id[8];
+};
+
+struct port_identity {
+	struct clock_identity  clock_identity;
+	uint16_t               port_number;
+}  __attribute__((packed));
+
+struct ptp_header {
+	uint8_t              msgtype;
+	uint8_t              ver;
+	uint16_t             message_length;
+	uint8_t              domain_number;
+	uint8_t              reserved1;
+	uint8_t              flag_field[2];
+	int64_t              correction;
+	uint32_t             reserved2;
+	struct port_identity source_port_identity;
+	uint16_t             sequenceId;
+	uint8_t              control;
+	int8_t               log_message_interval;
+} __attribute__((packed));
+
+struct sync_msg {
+	struct ptp_header   hdr;
+	struct tstamp       origin_tstamp;
+} __attribute__((packed));
+
+struct follow_up_msg {
+	struct ptp_header   hdr;
+	struct tstamp       precise_origin_tstamp;
+	uint8_t             suffix[0];
+} __attribute__((packed));
+
+struct delay_req_msg {
+	struct ptp_header   hdr;
+	struct tstamp       origin_tstamp;
+} __attribute__((packed));
+
+struct delay_resp_msg {
+	struct ptp_header    hdr;
+	struct tstamp        rx_tstamp;
+	struct port_identity requesting_port_identity;
+	uint8_t              suffix[0];
+} __attribute__((packed));
+
+struct ptp_message {
+	union {
+		struct ptp_header          header;
+		struct sync_msg            sync;
+		struct delay_req_msg       delay_req;
+		struct follow_up_msg       follow_up;
+		struct delay_resp_msg      delay_resp;
+	} __attribute__((packed));
+};
+
+struct ptpv2_data_slave_ordinary {
+	struct rte_mbuf *m;
+	struct timespec tstamp1;
+	struct timespec tstamp2;
+	struct timespec tstamp3;
+	struct timespec tstamp4;
+	struct clock_identity client_clock_id;
+	struct clock_identity master_clock_id;
+	struct timeval new_adj;
+	int64_t delta;
+	uint8_t portid;
+	uint8_t clock_portid;
+	uint16_t seqID_SYNC;
+	uint16_t seqID_FOLLOWUP;
+	uint8_t ptpset;
+	uint8_t kernel_time_set;
+	uint8_t current_ptp_port;
+};
+
+static struct ptpv2_data_slave_ordinary ptp_data;
+
+static inline uint64_t timespec64_to_ns(const struct timespec *ts)
+{
+	return ((uint64_t) ts->tv_sec * NSEC_PER_SEC) + ts->tv_nsec;
+}
+
+static struct timeval
+ns_to_timeval(int64_t nsec)
+{
+	struct timespec t_spec = {0, 0};
+	struct timeval t_eval = {0, 0};
+	int32_t rem;
+
+	if (nsec == 0)
+		return t_eval;
+	rem = nsec % NSEC_PER_SEC;
+	t_spec.tv_sec = nsec / NSEC_PER_SEC;
+
+	if (rem < 0) {
+		t_spec.tv_sec--;
+		rem += NSEC_PER_SEC;
+	}
+
+	t_spec.tv_nsec = rem;
+	t_eval.tv_sec = t_spec.tv_sec;
+	t_eval.tv_usec = t_spec.tv_nsec / 1000;
+
+	return t_eval;
+}
+
+/*
+ * Initializes a given port using global settings and with the RX buffers
+ * coming from the mbuf_pool passed as a parameter.
+ */
+static inline int
+port_init(uint8_t port, struct rte_mempool *mbuf_pool)
+{
+	struct rte_eth_dev_info dev_info;
+	struct rte_eth_conf port_conf = port_conf_default;
+	const uint16_t rx_rings = 1;
+	const uint16_t tx_rings = 1;
+	int retval;
+	uint16_t q;
+
+	if (port >= rte_eth_dev_count())
+		return -1;
+
+	/* Configure the Ethernet device. */
+	retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf);
+	if (retval != 0)
+		return retval;
+
+	/* Allocate and set up 1 RX queue per Ethernet port. */
+	for (q = 0; q < rx_rings; q++) {
+		retval = rte_eth_rx_queue_setup(port, q, RX_RING_SIZE,
+				rte_eth_dev_socket_id(port), NULL, mbuf_pool);
+
+		if (retval < 0)
+			return retval;
+	}
+
+	/* Allocate and set up 1 TX queue per Ethernet port. */
+	for (q = 0; q < tx_rings; q++) {
+		/* Setup txq_flags */
+		struct rte_eth_txconf *txconf;
+
+		rte_eth_dev_info_get(q, &dev_info);
+		txconf = &dev_info.default_txconf;
+		txconf->txq_flags = 0;
+
+		retval = rte_eth_tx_queue_setup(port, q, TX_RING_SIZE,
+				rte_eth_dev_socket_id(port), txconf);
+		if (retval < 0)
+			return retval;
+	}
+
+	/* Start the Ethernet port. */
+	retval = rte_eth_dev_start(port);
+	if (retval < 0)
+		return retval;
+
+	/* Enable timesync timestamping for the Ethernet device */
+	rte_eth_timesync_enable(port);
+
+	/* Enable RX in promiscuous mode for the Ethernet device. */
+	rte_eth_promiscuous_enable(port);
+
+	return 0;
+}
+
+static void
+print_clock_info(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	int64_t nsec;
+	struct timespec net_time, sys_time;
+
+	printf("Master Clock id: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
+		ptp_data->master_clock_id.id[0],
+		ptp_data->master_clock_id.id[1],
+		ptp_data->master_clock_id.id[2],
+		ptp_data->master_clock_id.id[3],
+		ptp_data->master_clock_id.id[4],
+		ptp_data->master_clock_id.id[5],
+		ptp_data->master_clock_id.id[6],
+		ptp_data->master_clock_id.id[7]);
+
+	printf("\nT2 - Slave  Clock.  %lds %ldns",
+			(ptp_data->tstamp2.tv_sec),
+			(ptp_data->tstamp2.tv_nsec));
+
+	printf("\nT1 - Master Clock.  %lds %ldns ",
+			ptp_data->tstamp1.tv_sec,
+			(ptp_data->tstamp1.tv_nsec));
+
+	printf("\nT3 - Slave  Clock.  %lds %ldns",
+			ptp_data->tstamp3.tv_sec,
+			(ptp_data->tstamp3.tv_nsec));
+
+	printf("\nT4 - Master Clock.  %lds %ldns ",
+			ptp_data->tstamp4.tv_sec,
+			(ptp_data->tstamp4.tv_nsec));
+
+	printf("\nDelta between master and slave clocks:%"PRId64"ns\n",
+			ptp_data->delta);
+
+	clock_gettime(CLOCK_REALTIME, &sys_time);
+	rte_eth_timesync_time_get(ptp_data->clock_portid,
+			&net_time);
+	time_t ts = net_time.tv_sec;
+
+	printf("\n\nComparison between Linux kernel Time and PTP:");
+
+	printf("\nCurrent PTP Time: %.24s %.9ld ns",
+			ctime(&ts), net_time.tv_nsec);
+
+	nsec = (int64_t)timespec64_to_ns(&net_time) -
+			(int64_t)timespec64_to_ns(&sys_time);
+	ptp_data->new_adj = ns_to_timeval(nsec);
+
+	gettimeofday(&ptp_data->new_adj, NULL);
+	time_t tp = ptp_data->new_adj.tv_sec;
+
+	printf("\nCurrent SYS Time: %.24s %.6ld ns",
+				ctime(&tp), ptp_data->new_adj.tv_usec);
+
+	printf("\nDelta between PTP and Linux Kernel time:%"PRId64"ns\n",
+				nsec);
+	printf("[Ctrl+C to quit]\n");
+
+	/* Clear screen and put cursor in column 1, row 1 */
+	printf("\033[2J\033[1;1H");
+}
+
+static int64_t
+delta_eval(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	int64_t delta;
+	uint64_t t1 = 0;
+	uint64_t t2 = 0;
+	uint64_t t3 = 0;
+	uint64_t t4 = 0;
+
+	t1 = timespec64_to_ns(&ptp_data->tstamp1);
+	t2 = timespec64_to_ns(&ptp_data->tstamp2);
+	t3 = timespec64_to_ns(&ptp_data->tstamp3);
+	t4 = timespec64_to_ns(&ptp_data->tstamp4);
+
+	/* delta = -[(T2-T1) - (T4-T3)]/2 */
+	delta = -((int64_t)((t2 - t1) - (t4 - t3))) / 2;
+
+	return delta;
+}
+
+/*
+ * Parse PTP SYNC message
+ */
+static void
+parse_sync(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	struct ptp_header *ptp_hdr;
+
+	ptp_hdr = (struct ptp_header *)(rte_pktmbuf_mtod(ptp_data->m, char *)
+			+ sizeof(struct ether_hdr));
+	ptp_data->seqID_SYNC = rte_be_to_cpu_16(ptp_hdr->sequenceId);
+
+	if (ptp_data->ptpset == 0) {
+		rte_memcpy(&ptp_data->master_clock_id,
+				&ptp_hdr->source_port_identity.clock_identity,
+				sizeof(struct clock_identity));
+		ptp_data->ptpset = 1;
+	}
+
+	if (memcmp(&ptp_hdr->source_port_identity.clock_identity,
+			&ptp_hdr->source_port_identity.clock_identity,
+			sizeof(struct clock_identity)) == 0) {
+
+		if (ptp_data->ptpset == 1)
+			rte_eth_timesync_read_rx_timestamp(ptp_data->portid,
+					&ptp_data->tstamp2, 0);
+	}
+
+}
+
+/*
+ * Parse PTP FOLLOWUP message & Send DELAY_REQ to master clock.
+ */
+static void
+parse_fup(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	struct ether_hdr *eth_hdr;
+	struct ptp_header *ptp_hdr;
+	struct clock_identity *client_clkid;
+	struct ptp_message *ptp_msg;
+	struct rte_mbuf *created_pkt;
+	struct ether_addr eth_multicast = ether_multicast;
+	size_t pkt_size;
+	int wait_us;
+	struct rte_mbuf *m = ptp_data->m;
+
+	eth_hdr = rte_pktmbuf_mtod(m, struct ether_hdr *);
+	ptp_hdr = (struct ptp_header *)(rte_pktmbuf_mtod(m, char *)
+			+ sizeof(struct ether_hdr));
+	if (memcmp(&ptp_data->master_clock_id,
+			&ptp_hdr->source_port_identity.clock_identity,
+			sizeof(struct clock_identity)) != 0)
+		return;
+
+	ptp_data->seqID_FOLLOWUP = rte_be_to_cpu_16(ptp_hdr->sequenceId);
+	ptp_msg = (struct ptp_message *) (rte_pktmbuf_mtod(m, char *) +
+					  sizeof(struct ether_hdr));
+	ptp_data->tstamp1.tv_nsec =
+		ntohl(ptp_msg->follow_up.precise_origin_tstamp.ns);
+	ptp_data->tstamp1.tv_sec =
+		((uint64_t)ntohl(ptp_msg->follow_up.precise_origin_tstamp.sec_lsb)) |
+		(((uint64_t)ntohs
+				(ptp_msg->follow_up.precise_origin_tstamp.sec_msb))
+				<< 32);
+
+	if (ptp_data->seqID_FOLLOWUP == ptp_data->seqID_SYNC) {
+
+		created_pkt = rte_pktmbuf_alloc(mbuf_pool);
+		pkt_size = sizeof(struct ether_hdr) +
+			sizeof(struct ptp_message);
+		created_pkt->data_len = pkt_size;
+		created_pkt->pkt_len = pkt_size;
+		eth_hdr = rte_pktmbuf_mtod(created_pkt, struct ether_hdr *);
+		rte_eth_macaddr_get(ptp_data->portid, &eth_hdr->s_addr);
+
+		/* Set multicast address 01-1B-19-00-00-00 */
+		ether_addr_copy(&eth_multicast, &eth_hdr->d_addr);
+
+		eth_hdr->ether_type = htons(PTP_PROTOCOL);
+		ptp_msg = (struct ptp_message *)
+			(rte_pktmbuf_mtod(created_pkt, char *) +
+			sizeof(struct ether_hdr));
+
+		ptp_msg->delay_req.hdr.sequenceId = htons(ptp_data->seqID_SYNC);
+		ptp_msg->delay_req.hdr.msgtype = DELAY_REQ;
+		ptp_msg->delay_req.hdr.ver = 2;
+		ptp_msg->delay_req.hdr.control = 1;
+		ptp_msg->delay_req.hdr.log_message_interval = 127;
+
+		/* Set up clock id */
+		client_clkid =
+			&ptp_msg->delay_req.hdr.source_port_identity.clock_identity;
+
+		client_clkid->id[0] = eth_hdr->s_addr.addr_bytes[0];
+		client_clkid->id[1] = eth_hdr->s_addr.addr_bytes[1];
+		client_clkid->id[2] = eth_hdr->s_addr.addr_bytes[2];
+		client_clkid->id[3] = 0xFF;
+		client_clkid->id[4] = 0xFE;
+		client_clkid->id[5] = eth_hdr->s_addr.addr_bytes[3];
+		client_clkid->id[6] = eth_hdr->s_addr.addr_bytes[4];
+		client_clkid->id[7] = eth_hdr->s_addr.addr_bytes[5];
+
+		rte_memcpy(&ptp_data->client_clock_id,
+				client_clkid,
+				sizeof(struct clock_identity));
+
+		/* Enable Flag for Hardware Timestamping */
+		created_pkt->ol_flags |= PKT_TX_IEEE1588_TMST;
+
+		/* We read value from NIC to prevent latching with old value */
+		rte_eth_timesync_read_tx_timestamp(ptp_data->portid,
+				&ptp_data->tstamp3);
+
+		/* the packet is being transmitted */
+		rte_eth_tx_burst(ptp_data->portid, 0, &created_pkt, 1);
+
+		wait_us = 0;
+		ptp_data->tstamp3.tv_nsec = 0;
+		ptp_data->tstamp3.tv_sec = 0;
+
+		/* we must wait at least 1us to read TX Timestamp */
+		while ((rte_eth_timesync_read_tx_timestamp(ptp_data->portid,
+				&ptp_data->tstamp3) < 0) && (wait_us < 1000)) {
+			rte_delay_us(1);
+			wait_us++;
+		}
+	}
+}
+
+/*
+ * Update the kernel time with the difference
+ * between it and the current NIC time
+ */
+static inline void
+update_kernel_time(void)
+{
+	int64_t nsec;
+	struct timespec net_time, sys_time;
+
+	clock_gettime(CLOCK_REALTIME, &sys_time);
+	rte_eth_timesync_time_get(ptp_data.current_ptp_port, &net_time);
+
+	nsec = (int64_t)timespec64_to_ns(&net_time) -
+	       (int64_t)timespec64_to_ns(&sys_time);
+
+	ptp_data.new_adj = ns_to_timeval(nsec);
+
+	/*
+	 * If difference between kernel time and system time in NIC
+	 * is too big (more than +/- 20 microseconds), use clock_settime
+	 * to set directly the kernel time, as adjtime is better for
+	 * small adjustments (takes longer to adjust the time)
+	 */
+
+	if (nsec > KERNEL_TIME_ADJUST_LIMIT || nsec < -KERNEL_TIME_ADJUST_LIMIT)
+		clock_settime(CLOCK_REALTIME, &net_time);
+	else
+		adjtime(&ptp_data.new_adj, 0);
+
+
+}
+
+/*
+ * Parse DELAY_RESP message
+ */
+static void
+parse_drsp(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	struct rte_mbuf *m = ptp_data->m;
+	struct ptp_message *ptp_msg;
+	uint16_t seqid;
+
+	ptp_msg = (struct ptp_message *) (rte_pktmbuf_mtod(m, char *) +
+					sizeof(struct ether_hdr));
+	seqid =  rte_be_to_cpu_16(ptp_msg->delay_resp.hdr.sequenceId);
+	if (memcmp(&ptp_data->client_clock_id,
+		   &ptp_msg->delay_resp.requesting_port_identity.clock_identity,
+		   sizeof(struct clock_identity)) == 0) {
+		if (seqid == ptp_data->seqID_FOLLOWUP) {
+			ptp_data->tstamp4.tv_nsec =
+					ntohl(ptp_msg->delay_resp.rx_tstamp.ns);
+			ptp_data->tstamp4.tv_sec =
+				((uint64_t)ntohl
+					(ptp_msg->delay_resp.rx_tstamp.sec_lsb)) |
+				(((uint64_t)ntohs
+					(ptp_msg->delay_resp.rx_tstamp.sec_msb))
+					<< 32);
+
+			/* Evaluate delta for adjustment */
+			ptp_data->delta = delta_eval(ptp_data);
+
+			rte_eth_timesync_time_adjust(ptp_data->portid,
+						     ptp_data->delta);
+
+			/* If enabled in app parameters, update kernel time */
+			if (ptp_data->kernel_time_set == 1)
+				update_kernel_time();
+
+			ptp_data->clock_portid = ptp_data->portid;
+			ptp_data->current_ptp_port = ptp_data->clock_portid;
+
+		}
+	}
+}
+
+/* This function processes PTP packets, implementing
+ * slave PTP IEEE1588 L2 functionality
+ */
+static void
+parse_ptp_frames(uint8_t portid, struct rte_mbuf *m) {
+	struct ptp_header *ptp_hdr;
+	struct ether_hdr *eth_hdr;
+	uint16_t eth_type;
+
+	eth_hdr = rte_pktmbuf_mtod(m, struct ether_hdr *);
+	eth_type = rte_be_to_cpu_16(eth_hdr->ether_type);
+
+	if (eth_type == PTP_PROTOCOL) {
+		ptp_data.m = m;
+		ptp_data.portid = portid;
+		ptp_hdr = (struct ptp_header *)(rte_pktmbuf_mtod(m, char *)
+					+ sizeof(struct ether_hdr));
+
+		switch (ptp_hdr->msgtype) {
+		case SYNC:
+			parse_sync(&ptp_data);
+			break;
+		case FOLLOW_UP:
+			parse_fup(&ptp_data);
+			break;
+		case DELAY_RESP:
+			parse_drsp(&ptp_data);
+			print_clock_info(&ptp_data);
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+/*
+ * The lcore main. This is the main thread that does the work, reading from
+ * an input port and writing to an output port.
+ */
+static __attribute__((noreturn)) void
+lcore_main(void)
+{
+	uint8_t portid;
+	unsigned nb_rx;
+	struct rte_mbuf *m;
+
+	/*
+	 * Check that the port is on the same NUMA node as the polling thread
+	 * for best performance.
+	 */
+	printf("\nCore %u Waiting for SYNC packets. [Ctrl+C to quit]\n",
+			rte_lcore_id());
+
+	/* Run until the application is quit or killed. */
+
+	while (1) {
+		/*
+		 * Read packet from RX queues
+		 */
+		for (portid = 0; portid < ptp_enabled_port_nb; portid++) {
+
+			portid = ptp_enabled_ports[portid];
+			nb_rx = rte_eth_rx_burst(portid, 0, &m, 1);
+
+			if (likely(nb_rx == 0))
+				continue;
+
+			if (m->ol_flags & PKT_RX_IEEE1588_PTP)
+				parse_ptp_frames(portid, m);
+
+			rte_pktmbuf_free(m);
+		}
+	}
+}
+
+static void
+print_usage(const char *prgname)
+{
+	printf("%s [EAL options] -- -p PORTMASK -T VALUE\n"
+		"  -T VALUE: 0 - Disable, 1 - Enable Linux Clock "
+		" Synchronization (0 default)\n"
+		"  -p PORTMASK: hexadecimal bitmask of ports to configure\n",
+			prgname);
+}
+
+static int
+ptp_parse_portmask(const char *portmask)
+{
+	char *end = NULL;
+	unsigned long pm;
+
+	/* parse hexadecimal string */
+	pm = strtoul(portmask, &end, 16);
+	if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
+		return -1;
+
+	if (pm == 0)
+		return -1;
+
+	return pm;
+}
+
+static int
+parse_ptp_kernel(const char *param)
+{
+	char *end = NULL;
+	unsigned long pm;
+
+	pm = strtoul(param, &end, 16);
+	if ((param[0] == '\0') || (end == NULL) || (*end != '\0'))
+		return -1;
+	if (pm == 0)
+		return 0;
+
+	return 1;
+}
+
+/* Parse the argument given in the command line of the application */
+static int
+ptp_parse_args(int argc, char **argv)
+{
+	int opt, ret;
+	char **argvopt;
+	int option_index;
+	char *prgname = argv[0];
+	static struct option lgopts[] = { {NULL, 0, 0, 0} };
+
+	argvopt = argv;
+
+	while ((opt = getopt_long(argc, argvopt, "p:T:",
+				  lgopts, &option_index)) != EOF) {
+
+		switch (opt) {
+
+		/* portmask */
+		case 'p':
+			ptp_enabled_port_mask = ptp_parse_portmask(optarg);
+			if (ptp_enabled_port_mask == 0) {
+				printf("invalid portmask\n");
+				print_usage(prgname);
+				return -1;
+			}
+			break;
+		/* time synchronization */
+		case 'T':
+			ret = parse_ptp_kernel(optarg);
+			if (ret < 0) {
+				print_usage(prgname);
+				return -1;
+			}
+
+			ptp_data.kernel_time_set = ret;
+			break;
+
+		default:
+			print_usage(prgname);
+			return -1;
+		}
+	}
+
+	argv[optind-1] = prgname;
+
+	optind = 0; /* reset getopt lib */
+	return 0;
+}
+
+/*
+ * The main function, which does initialization and calls the per-lcore
+ * functions.
+ */
+int
+main(int argc, char *argv[])
+{
+	unsigned nb_ports;
+
+	uint8_t portid;
+
+	/* Initialize the Environment Abstraction Layer (EAL). */
+	int ret = rte_eal_init(argc, argv);
+
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
+
+	memset(&ptp_data, '\0', sizeof(struct ptpv2_data_slave_ordinary));
+
+	argc -= ret;
+	argv += ret;
+
+	ret = ptp_parse_args(argc, argv);
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "Error with PTP initialization\n");
+
+	/* Check that there is an even number of ports to send/receive on. */
+	nb_ports = rte_eth_dev_count();
+
+	/* Creates a new mempool in memory to hold the mbufs. */
+	mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", NUM_MBUFS * nb_ports,
+		MBUF_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
+
+	if (mbuf_pool == NULL)
+		rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
+
+	/* Initialize all ports. */
+	for (portid = 0; portid < nb_ports; portid++) {
+		if ((ptp_enabled_port_mask & (1 << portid)) != 0) {
+			if (port_init(portid, mbuf_pool) == 0) {
+				ptp_enabled_ports[ptp_enabled_port_nb] = portid;
+				ptp_enabled_port_nb++;
+			} else {
+				rte_exit(EXIT_FAILURE,
+					 "Cannot init port %"PRIu8 "\n",
+					 portid);
+			}
+		} else
+			printf("Skipping disabled port %u\n", portid);
+	}
+
+	if (ptp_enabled_port_nb == 0) {
+		rte_exit(EXIT_FAILURE,
+			"All available ports are disabled."
+			" Please set portmask.\n");
+	}
+
+	if (rte_lcore_count() > 1)
+		printf("\nWARNING: Too many lcores enabled. Only 1 used.\n");
+
+	/* Call lcore_main on the master core only. */
+	lcore_main();
+
+	return 0;
+}
-- 
2.5.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v4 7/7] doc: add a PTPCLIENT sample guide
  2015-11-04 10:06 ` [dpdk-dev] [PATCH v4 0/7] add sample ptp slave application Daniel Mrzyglod
                     ` (5 preceding siblings ...)
  2015-11-04 10:06   ` [dpdk-dev] [PATCH v4 6/7] example: PTP client slave minimal implementation Daniel Mrzyglod
@ 2015-11-04 10:06   ` Daniel Mrzyglod
  2015-11-05 12:46   ` [dpdk-dev] [PATCH v4 0/7] add sample ptp slave application Mcnamara, John
  2015-11-05 13:37   ` Mcnamara, John
  8 siblings, 0 replies; 84+ messages in thread
From: Daniel Mrzyglod @ 2015-11-04 10:06 UTC (permalink / raw)
  To: dev

It includes:
 - Add the ptpclient picture with svg format.
 - Add the ptpclient.rst file
 - Change the index.rst file for the above pictures index.

Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
---
 doc/guides/sample_app_ug/img/ptpclient.svg | 520 +++++++++++++++++++++++++++++
 doc/guides/sample_app_ug/index.rst         |   3 +
 doc/guides/sample_app_ug/ptpclient.rst     | 306 +++++++++++++++++
 3 files changed, 829 insertions(+)
 create mode 100644 doc/guides/sample_app_ug/img/ptpclient.svg
 create mode 100644 doc/guides/sample_app_ug/ptpclient.rst

diff --git a/doc/guides/sample_app_ug/img/ptpclient.svg b/doc/guides/sample_app_ug/img/ptpclient.svg
new file mode 100644
index 0000000..55c134e
--- /dev/null
+++ b/doc/guides/sample_app_ug/img/ptpclient.svg
@@ -0,0 +1,520 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="105mm"
+   height="148mm"
+   viewBox="0 0 372.04724 524.40945"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="drawing3.svg">
+  <defs
+     id="defs4">
+    <marker
+       inkscape:stockid="Arrow2Mend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow2Mend"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4256"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="scale(-0.6,-0.6)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Lstart"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow2Lstart"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4247"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="matrix(1.1,0,0,1.1,1.1,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible"
+       id="marker6746"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="Arrow2Lend">
+      <path
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         id="path6748"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker6526"
+       style="overflow:visible"
+       inkscape:isstock="true"
+       inkscape:collect="always">
+      <path
+         id="path6528"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker6276"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path6278"
+         d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(-0.8,0,0,-0.8,-10,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible"
+       id="marker6128"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="DotM">
+      <path
+         transform="matrix(0.4,0,0,0.4,2.96,0.4)"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
+         id="path6130"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible"
+       id="marker6022"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="Arrow2Lend"
+       inkscape:collect="always">
+      <path
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         id="path6024"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible"
+       id="marker5922"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="DotM">
+      <path
+         transform="matrix(0.4,0,0,0.4,2.96,0.4)"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
+         id="path5924"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow2Lend"
+       style="overflow:visible"
+       inkscape:isstock="true"
+       inkscape:collect="always">
+      <path
+         id="path4250"
+         style="fill:#0000ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="DotM"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker5676"
+       style="overflow:visible"
+       inkscape:isstock="true"
+       inkscape:collect="always">
+      <path
+         id="path5678"
+         d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
+         style="fill:#0000ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(0.4,0,0,0.4,2.96,0.4)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lstart"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker5600"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path5602"
+         d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(0.8,0,0,0.8,10,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible"
+       id="marker5510"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="DotM"
+       inkscape:collect="always">
+      <path
+         transform="matrix(0.4,0,0,0.4,2.96,0.4)"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
+         id="path5512"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker4651"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4653"
+         d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(-0.8,0,0,-0.8,-10,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker4605"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4607"
+         d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(-0.8,0,0,-0.8,-10,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow1Lend"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4232"
+         d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(-0.8,0,0,-0.8,-10,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="DotM"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="DotM"
+       style="overflow:visible"
+       inkscape:isstock="true"
+       inkscape:collect="always">
+      <path
+         id="path4293"
+         d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(0.4,0,0,0.4,2.96,0.4)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lstart"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow1Lstart"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4229"
+         d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(0.8,0,0,0.8,10,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3402"
+       is_visible="true" />
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3398"
+       is_visible="true" />
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3394"
+       is_visible="true" />
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3378"
+       is_visible="true" />
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3374"
+       is_visible="true" />
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3370"
+       is_visible="true" />
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3366"
+       is_visible="true" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1.06"
+     inkscape:cx="280.7353"
+     inkscape:cy="266.181"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     showguides="false"
+     inkscape:window-width="1920"
+     inkscape:window-height="1053"
+     inkscape:window-x="1920"
+     inkscape:window-y="103"
+     inkscape:window-maximized="0" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-527.95276)">
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.44085264;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="m 64.094156,600.99321 c 0,60.57637 0,393.74649 0,393.74649"
+       id="path3388"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.63210523;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="m 291.64401,600.59617 0,394.60083"
+       id="path3390"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.28789771;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#DotM);marker-end:url(#marker6746)"
+       d="M 64.094156,623.68474 289.7829,665.38811"
+       id="path4223"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.28789771;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#marker5510);marker-end:url(#marker6526)"
+       d="M 63.199944,683.08006 290.44233,725.29448"
+       id="path5274"
+       inkscape:connector-curvature="0" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="295.22058"
+       y="665.10974"
+       id="text5424"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan5426"
+         x="295.22058"
+         y="665.10974">T2</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#800080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="242.44649"
+       y="657.08087"
+       id="text5428"
+       sodipodi:linespacing="100%"
+       transform="matrix(0.98289795,0.18415108,-0.18415108,0.98289795,0,0)"><tspan
+         sodipodi:role="line"
+         id="tspan5430"
+         x="242.44649"
+         y="657.08087">FOLLOW UP:T1</tspan></text>
+    <path
+       style="fill:#0000ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.28789771;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#marker5676);marker-end:url(#Arrow2Lend)"
+       d="M 291.5001,774.36878 64.400801,816.4401"
+       id="path5586"
+       inkscape:connector-curvature="0" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="-44.967201"
+       y="809.64362"
+       id="text5898"
+       sodipodi:linespacing="100%"
+       transform="matrix(0.98291896,-0.18403889,0.18403889,0.98291896,0,0)"><tspan
+         sodipodi:role="line"
+         id="tspan5900"
+         x="-44.967201"
+         y="809.64362">DELAY REQUEST</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="297.08099"
+       y="774.7981"
+       id="text5902"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan5904"
+         x="297.08099"
+         y="774.7981">T3</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#800080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="35.20845"
+       y="817.29871"
+       id="text5906"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan5908"
+         x="35.20845"
+         y="817.29871">T4</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#800080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="33.920555"
+       y="623.68475"
+       id="text5910"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan5912"
+         x="33.920555"
+         y="623.68475">T1</tspan></text>
+    <path
+       style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.28789771;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#marker6128);marker-end:url(#marker6022)"
+       d="M 63.971502,838.62056 290.6415,881.55049"
+       id="path5914"
+       inkscape:connector-curvature="0" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#800080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="223.59686"
+       y="811.73932"
+       id="text6858"
+       sodipodi:linespacing="100%"
+       transform="matrix(0.98367106,0.17997568,-0.17997568,0.98367106,0,0)"><tspan
+         sodipodi:role="line"
+         id="tspan6860"
+         x="223.59686"
+         y="811.73932">DELAY RESPONSE:T4</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="813.40204"
+       y="-298.41309"
+       id="text6862"
+       sodipodi:linespacing="100%"
+       transform="matrix(0.0147127,0.99989176,-0.99989176,0.0147127,0,0)"><tspan
+         sodipodi:role="line"
+         id="tspan6864"
+         x="813.40204"
+         y="-298.41309">time</tspan></text>
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2.1464963;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow2Mend)"
+       d="m 316.97185,863.23372 0,66.11208"
+       id="path6866"
+       inkscape:connector-curvature="0" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.42257881px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#800080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="38.764343"
+       y="590.47479"
+       id="text7094"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan7096"
+         x="38.764343"
+         y="590.47479">master</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:47.51625061px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="571.69812"
+       y="61.796165"
+       id="text7098"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan7100"
+         x="571.69812"
+         y="61.796165" /></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.42257881px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="271.23392"
+       y="593.71478"
+       id="text7102"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan7104"
+         x="271.23392"
+         y="593.71478">slave</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.3917141px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#800080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="244.40062"
+       y="602.10443"
+       id="text4317"
+       sodipodi:linespacing="125%"
+       transform="matrix(0.98605316,0.16643065,-0.16643065,0.98605316,0,0)"><tspan
+         sodipodi:role="line"
+         id="tspan4319"
+         x="244.40062"
+         y="602.10443">SYNC</tspan></text>
+  </g>
+</svg>
diff --git a/doc/guides/sample_app_ug/index.rst b/doc/guides/sample_app_ug/index.rst
index 9beedd9..8ae86c0 100644
--- a/doc/guides/sample_app_ug/index.rst
+++ b/doc/guides/sample_app_ug/index.rst
@@ -73,6 +73,7 @@ Sample Applications User Guide
     vm_power_management
     tep_termination
     proc_info
+    ptpclient
 
 **Figures**
 
@@ -136,6 +137,8 @@ Sample Applications User Guide
 :numref:`figure_overlay_networking` :ref:`figure_overlay_networking`
 :numref:`figure_tep_termination_arch` :ref:`figure_tep_termination_arch`
 
+:numref:`figure_ptpclient_highlevel` :ref:`figure_ptpclient_highlevel`
+
 **Tables**
 
 :numref:`table_qos_metering_1` :ref:`table_qos_metering_1`
diff --git a/doc/guides/sample_app_ug/ptpclient.rst b/doc/guides/sample_app_ug/ptpclient.rst
new file mode 100644
index 0000000..6e425b7
--- /dev/null
+++ b/doc/guides/sample_app_ug/ptpclient.rst
@@ -0,0 +1,306 @@
+..  BSD LICENSE
+    Copyright(c) 2015 Intel Corporation. All rights reserved.
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions
+    are met:
+
+    * Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.
+    * Neither the name of Intel Corporation nor the names of its
+    contributors may be used to endorse or promote products derived
+    from this software without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+    OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+PTP Client Sample Application
+=============================
+
+The PTP (Precision Time Protocol) client sample application is a simple
+example of using the DPDK IEEE1588 API to communicate with a PTP master clock
+to synchronize the time on the NIC and, optionally, on the Linux system.
+
+Note, PTP is a time syncing protocol and cannot be used within DPDK as a
+time-stamping mechanism. See the following for an explanation of the protocol:
+`Precision Time Protocol
+<https://en.wikipedia.org/wiki/Precision_Time_Protocol>`_.
+
+
+Limitations
+-----------
+
+The PTP sample application is intended as a simple reference implementation of
+a PTP client using the DPDK IEEE1588 API.
+In order to keep the application simple the following assumptions are made:
+
+* The first discovered master is the master for the session.
+* Only L2 PTP packets are supported.
+* Only the PTP v2 protocol is supported.
+* Only the slave clock is implemented.
+
+
+How the Application Works
+-------------------------
+
+.. _figure_ptpclient_highlevel:
+
+.. figure:: img/ptpclient.*
+
+   PTP Synchronization Protocol
+
+The PTP synchronization in the sample application works as follows:
+
+* Master sends *Sync* message - the slave saves it as T2.
+* Master sends *Follow Up* message and sends time of T1.
+* Slave sends *Delay Request* frame to PTP Master and stores T3.
+* Master sends *Delay Response* T4 time which is time of received T3.
+
+The adjustment for slave can be represented as:
+
+   adj = -[(T2-T1)-(T4 - T3)]/2
+
+If the command line parameter ``-T 1`` is used the application also
+synchronizes the PTP PHC clock with the Linux kernel clock.
+
+
+Compiling the Application
+-------------------------
+
+To compile the application, export the path to the DPDK source tree and edit
+the ``config/common_linuxapp`` configuration file to enable IEEE1588:
+
+.. code-block:: console
+
+    export RTE_SDK=/path/to/rte_sdk
+
+    # Edit  common_linuxapp and set the following options:
+    CONFIG_RTE_LIBRTE_IEEE1588=y
+
+Set the target, for example:
+
+.. code-block:: console
+
+    export RTE_TARGET=x86_64-native-linuxapp-gcc
+
+See the *DPDK Getting Started* Guide for possible ``RTE_TARGET`` values.
+
+Build the application as follows:
+
+.. code-block:: console
+
+    # Recompile DPDK.
+    make install T=$RTE_TARGET
+
+    # Compile the application.
+    cd ${RTE_SDK}/examples/ptpclient
+    make
+
+
+Running the Application
+-----------------------
+
+To run the example in a ``linuxapp`` environment:
+
+.. code-block:: console
+
+    ./build/ptpclient -c 2 -n 4 -- -p 0x1 -T 0
+
+Refer to *DPDK Getting Started Guide* for general information on running
+applications and the Environment Abstraction Layer (EAL) options.
+
+* ``-p portmask``: Hexadecimal portmask.
+* ``-T 0``: Update only the PTP slave clock.
+* ``-T 1``: Update the PTP slave clock and synchronize the Linux Kernel to the PTP clock.
+
+
+Code Explanation
+----------------
+
+The following sections provide an explanation of the main components of the
+code.
+
+All DPDK library functions used in the sample code are prefixed with ``rte_``
+and are explained in detail in the *DPDK API Documentation*.
+
+
+The Main Function
+~~~~~~~~~~~~~~~~~
+
+The ``main()`` function performs the initialization and calls the execution
+threads for each lcore.
+
+The first task is to initialize the Environment Abstraction Layer (EAL).  The
+``argc`` and ``argv`` arguments are provided to the ``rte_eal_init()``
+function. The value returned is the number of parsed arguments:
+
+.. code-block:: c
+
+    int ret = rte_eal_init(argc, argv);
+    if (ret < 0)
+        rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
+
+And than we parse application specific arguments
+
+.. code-block:: c
+
+    argc -= ret;
+    argv += ret;
+
+    ret = ptp_parse_args(argc, argv);
+    if (ret < 0)
+        rte_exit(EXIT_FAILURE, "Error with PTP initialization\n");
+
+The ``main()`` also allocates a mempool to hold the mbufs (Message Buffers)
+used by the application:
+
+.. code-block:: c
+
+    mbuf_pool = rte_mempool_create("MBUF_POOL",
+                                   NUM_MBUFS * nb_ports,
+                                   MBUF_SIZE,
+                                   MBUF_CACHE_SIZE,
+                                   sizeof(struct rte_pktmbuf_pool_private),
+                                   rte_pktmbuf_pool_init, NULL,
+                                   rte_pktmbuf_init,      NULL,
+                                   rte_socket_id(),
+                                   0);
+
+Mbufs are the packet buffer structure used by DPDK. They are explained in
+detail in the "Mbuf Library" section of the *DPDK Programmer's Guide*.
+
+The ``main()`` function also initializes all the ports using the user defined
+``port_init()`` function with portmask provided by user:
+
+.. code-block:: c
+
+    for (portid = 0; portid < nb_ports; portid++)
+        if ((ptp_enabled_port_mask & (1 << portid)) != 0) {
+
+            if (port_init(portid, mbuf_pool) == 0) {
+                ptp_enabled_ports[ptp_enabled_port_nb] = portid;
+                ptp_enabled_port_nb++;
+            } else {
+                rte_exit(EXIT_FAILURE, "Cannot init port %"PRIu8 "\n",
+                        portid);
+            }
+        }
+
+
+Once the initialization is complete, the application is ready to launch a
+function on an lcore. In this example ``lcore_main()`` is called on a single
+lcore.
+
+.. code-block:: c
+
+	lcore_main();
+
+The ``lcore_main()`` function is explained below.
+
+
+The Lcores Main
+~~~~~~~~~~~~~~~
+
+As we saw above the ``main()`` function calls an application function on the
+available lcores.
+
+The main work of the application is done within the loop:
+
+.. code-block:: c
+
+        for (portid = 0; portid < ptp_enabled_port_nb; portid++) {
+
+            portid = ptp_enabled_ports[portid];
+            nb_rx = rte_eth_rx_burst(portid, 0, &m, 1);
+
+            if (likely(nb_rx == 0))
+                continue;
+
+            if (m->ol_flags & PKT_RX_IEEE1588_PTP)
+                parse_ptp_frames(portid, m);
+
+            rte_pktmbuf_free(m);
+        }
+
+Packets are received one by one on the RX ports and, if required, PTP response
+packets are transmitted on the TX ports.
+
+If the offload flags in the mbuf indicate that the packet is a PTP packet then
+the packet is parsed to determine which type:
+
+.. code-block:: c
+
+            if (m->ol_flags & PKT_RX_IEEE1588_PTP)
+                 parse_ptp_frames(portid, m);
+
+
+All packets are freed explicitly using ``rte_pktmbuf_free()``.
+
+The forwarding loop can be interrupted and the application closed using
+``Ctrl-C``.
+
+
+PTP parsing
+~~~~~~~~~~~
+
+The ``parse_ptp_frames()`` function processes PTP packets, implementing slave
+PTP IEEE1588 L2 functionality.
+
+.. code-block:: c
+
+    void
+    parse_ptp_frames(uint8_t portid, struct rte_mbuf *m) {
+        struct ptp_header *ptp_hdr;
+        struct ether_hdr *eth_hdr;
+        uint16_t eth_type;
+
+        eth_hdr = rte_pktmbuf_mtod(m, struct ether_hdr *);
+        eth_type = rte_be_to_cpu_16(eth_hdr->ether_type);
+
+        if (eth_type == PTP_PROTOCOL) {
+            ptp_data.m = m;
+            ptp_data.portid = portid;
+            ptp_hdr = (struct ptp_header *)(rte_pktmbuf_mtod(m, char *)
+                        + sizeof(struct ether_hdr));
+
+            switch (ptp_hdr->msgtype) {
+            case SYNC:
+                parse_sync(&ptp_data);
+                break;
+            case FOLLOW_UP:
+                parse_fup(&ptp_data);
+                break;
+            case DELAY_RESP:
+                parse_drsp(&ptp_data);
+                print_clock_info(&ptp_data);
+                break;
+            default:
+                break;
+            }
+        }
+    }
+
+There are 3 types of packets on the RX path which we must parse to create a minimal
+implementation of the PTP slave client:
+
+* SYNC packet.
+* FOLLOW UP packet
+* DELAY RESPONSE packet.
+
+When we parse the *FOLLOW UP* packet we also create and send a *DELAY_REQUEST* packet.
+Also when we parse the *DELAY RESPONSE* packet, and all conditions are met we adjust the PTP slave clock.
-- 
2.5.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [dpdk-dev] [PATCH v4 0/7] add sample ptp slave application
  2015-11-04 10:06 ` [dpdk-dev] [PATCH v4 0/7] add sample ptp slave application Daniel Mrzyglod
                     ` (6 preceding siblings ...)
  2015-11-04 10:06   ` [dpdk-dev] [PATCH v4 7/7] doc: add a PTPCLIENT sample guide Daniel Mrzyglod
@ 2015-11-05 12:46   ` Mcnamara, John
  2015-11-05 15:17     ` Thomas Monjalon
  2015-11-05 13:37   ` Mcnamara, John
  8 siblings, 1 reply; 84+ messages in thread
From: Mcnamara, John @ 2015-11-05 12:46 UTC (permalink / raw)
  To: Thomas Monjalon; +Cc: dev

> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Daniel Mrzyglod
> Sent: Wednesday, November 4, 2015 10:06 AM
> To: dev@dpdk.org
> Subject: [dpdk-dev] [PATCH v4 0/7] add sample ptp slave application

Hi Thomas,

Why is this patchset marked as "Deferred"?

http://dpdk.org/dev/patchwork/project/dpdk/list/?state=10

John.
-- 

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [dpdk-dev] [PATCH v4 0/7] add sample ptp slave application
  2015-11-04 10:06 ` [dpdk-dev] [PATCH v4 0/7] add sample ptp slave application Daniel Mrzyglod
                     ` (7 preceding siblings ...)
  2015-11-05 12:46   ` [dpdk-dev] [PATCH v4 0/7] add sample ptp slave application Mcnamara, John
@ 2015-11-05 13:37   ` Mcnamara, John
  8 siblings, 0 replies; 84+ messages in thread
From: Mcnamara, John @ 2015-11-05 13:37 UTC (permalink / raw)
  To: Mrzyglod, DanielX T, dev

> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Daniel Mrzyglod
> Sent: Wednesday, November 4, 2015 10:06 AM
> To: dev@dpdk.org
> Subject: [dpdk-dev] [PATCH v4 0/7] add sample ptp slave application
> 
> Add a sample application that acts as a PTP slave using the DPDK IEEE1588
> functions.
> 
> Also add some additional IEEE1588 support functions to enable getting,
> setting and adjusting the device time.


Patch 5/7 has a merge conflict with another patch that went in very recently so it is probably worth rebasing that.

Otherwise,

Series Acked-by: John McNamara <john.mcnamara@intel.com>

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v5 0/7] add sample ptp slave application
  2015-10-02 15:20 [dpdk-dev] [PATCH 0/3] add sample ptp slave application Daniel Mrzyglod
                   ` (5 preceding siblings ...)
  2015-11-04 10:06 ` [dpdk-dev] [PATCH v4 0/7] add sample ptp slave application Daniel Mrzyglod
@ 2015-11-05 14:05 ` Daniel Mrzyglod
  2015-11-05 14:06   ` [dpdk-dev] [PATCH v5 1/7] ethdev: add additional ieee1588 support functions Daniel Mrzyglod
                     ` (8 more replies)
  6 siblings, 9 replies; 84+ messages in thread
From: Daniel Mrzyglod @ 2015-11-05 14:05 UTC (permalink / raw)
  To: dev

Add a sample application that acts as a PTP slave using the DPDK IEEE1588
functions.

Also add some additional IEEE1588 support functions to enable getting,
setting and adjusting the device time.

V4->v5:
 - rebase to the current master

V3->V4:
Doc:
 - Update documentation for ptpclient
 - fix: put information about ptpaplication in correct place

V2->V3:
PMD:
 - move common structures and functions for PTP protocol to librte_net/rte_ptp.h

V1->V2:
PMDs:
 - add support for e1000
 - add support for ixgbe
 - add support for i40
ethdev:
 - change function names to more proper
Doc:
 - add documentation for ptpclient
sample:
 - add kernel adjustment option
 - add portmask option to provide portmask to aplication
Daniel Mrzyglod (5):
  ethdev: add additional ieee1588 support functions
  net: Add common PTP structures and functions
  ixgbe: add additional ieee1588 support functions
  example: PTP client slave minimal implementation
  doc: add a PTPCLIENT sample guide

Pablo de Lara (2):
  igb: add additional ieee1588 support functions
  i40e: add additional ieee1588 support functions

 MAINTAINERS                                |   3 +
 doc/guides/rel_notes/release_2_2.rst       |   8 +
 doc/guides/sample_app_ug/img/ptpclient.svg | 520 +++++++++++++++++++
 doc/guides/sample_app_ug/index.rst         |   3 +
 doc/guides/sample_app_ug/ptpclient.rst     | 306 +++++++++++
 drivers/net/e1000/e1000_ethdev.h           |   3 +
 drivers/net/e1000/igb_ethdev.c             | 299 ++++++++++-
 drivers/net/i40e/i40e_ethdev.c             | 196 +++++++-
 drivers/net/i40e/i40e_ethdev.h             |   5 +
 drivers/net/ixgbe/ixgbe_ethdev.c           | 272 +++++++++-
 drivers/net/ixgbe/ixgbe_ethdev.h           |   3 +
 examples/Makefile                          |   1 +
 examples/ptpclient/Makefile                |  56 +++
 examples/ptpclient/ptpclient.c             | 779 +++++++++++++++++++++++++++++
 lib/librte_ether/rte_ethdev.c              |  36 ++
 lib/librte_ether/rte_ethdev.h              |  65 ++-
 lib/librte_ether/rte_ether_version.map     |   3 +
 lib/librte_net/Makefile                    |   2 +-
 lib/librte_net/rte_ptp.h                   | 105 ++++
 19 files changed, 2622 insertions(+), 43 deletions(-)
 create mode 100644 doc/guides/sample_app_ug/img/ptpclient.svg
 create mode 100644 doc/guides/sample_app_ug/ptpclient.rst
 create mode 100644 examples/ptpclient/Makefile
 create mode 100644 examples/ptpclient/ptpclient.c
 create mode 100644 lib/librte_net/rte_ptp.h

-- 
2.5.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v5 1/7] ethdev: add additional ieee1588 support functions
  2015-11-05 14:05 ` [dpdk-dev] [PATCH v5 " Daniel Mrzyglod
@ 2015-11-05 14:06   ` Daniel Mrzyglod
  2015-11-10 11:03     ` Thomas Monjalon
  2015-11-05 14:06   ` [dpdk-dev] [PATCH v5 2/7] net: Add common PTP structures and functions Daniel Mrzyglod
                     ` (7 subsequent siblings)
  8 siblings, 1 reply; 84+ messages in thread
From: Daniel Mrzyglod @ 2015-11-05 14:06 UTC (permalink / raw)
  To: dev

Add additional functions to support the existing IEEE1588
functionality.

* rte_eth_timesync_settime(), function to set the device clock time.
* rte_eth_timesync_gettime, function to get the device clock time.
* rte_eth_timesync_adjust, function to adjust the device clock time.

Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
---
 doc/guides/rel_notes/release_2_2.rst   |  3 ++
 lib/librte_ether/rte_ethdev.c          | 36 +++++++++++++++++++
 lib/librte_ether/rte_ethdev.h          | 65 +++++++++++++++++++++++++++++++++-
 lib/librte_ether/rte_ether_version.map |  3 ++
 4 files changed, 106 insertions(+), 1 deletion(-)

diff --git a/doc/guides/rel_notes/release_2_2.rst b/doc/guides/rel_notes/release_2_2.rst
index 59dda59..17b281c 100644
--- a/doc/guides/rel_notes/release_2_2.rst
+++ b/doc/guides/rel_notes/release_2_2.rst
@@ -222,6 +222,9 @@ API Changes
 
 * The devargs union field virtual is renamed to virt for C++ compatibility.
 
+* Add new functions in ethdev to support IEEE1588: rte_eth_timesync_time_adjust()
+  rte_eth_timesync_time_get(), rte_eth_timesync_time_set()
+
 
 ABI Changes
 -----------
diff --git a/lib/librte_ether/rte_ethdev.c b/lib/librte_ether/rte_ethdev.c
index e0e1dca..20cf013 100644
--- a/lib/librte_ether/rte_ethdev.c
+++ b/lib/librte_ether/rte_ethdev.c
@@ -3193,6 +3193,42 @@ rte_eth_timesync_read_tx_timestamp(uint8_t port_id, struct timespec *timestamp)
 }
 
 int
+rte_eth_timesync_time_adjust(uint8_t port_id, int64_t delta)
+{
+	struct rte_eth_dev *dev;
+
+	VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	FUNC_PTR_OR_ERR_RET(*dev->dev_ops->timesync_time_adjust, -ENOTSUP);
+	return (*dev->dev_ops->timesync_time_adjust)(dev, delta);
+}
+
+int
+rte_eth_timesync_time_get(uint8_t port_id, struct timespec *timestamp)
+{
+	struct rte_eth_dev *dev;
+
+	VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	FUNC_PTR_OR_ERR_RET(*dev->dev_ops->timesync_time_get, -ENOTSUP);
+	return (*dev->dev_ops->timesync_time_get)(dev, timestamp);
+}
+
+int
+rte_eth_timesync_time_set(uint8_t port_id, struct timespec *timestamp)
+{
+	struct rte_eth_dev *dev;
+
+	VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	FUNC_PTR_OR_ERR_RET(*dev->dev_ops->timesync_time_set, -ENOTSUP);
+	return (*dev->dev_ops->timesync_time_set)(dev, timestamp);
+}
+
+int
 rte_eth_dev_get_reg_length(uint8_t port_id)
 {
 	struct rte_eth_dev *dev;
diff --git a/lib/librte_ether/rte_ethdev.h b/lib/librte_ether/rte_ethdev.h
index 48a540d..585d980 100644
--- a/lib/librte_ether/rte_ethdev.h
+++ b/lib/librte_ether/rte_ethdev.h
@@ -1206,6 +1206,17 @@ typedef int (*eth_timesync_read_tx_timestamp_t)(struct rte_eth_dev *dev,
 						struct timespec *timestamp);
 /**< @internal Function used to read a TX IEEE1588/802.1AS timestamp. */
 
+typedef int (*eth_timesync_time_adjust)(struct rte_eth_dev *dev, int64_t);
+/**< @internal Function used to adjust device clock */
+
+typedef int (*eth_timesync_time_get)(struct rte_eth_dev *dev,
+						struct timespec *timestamp);
+/**< @internal Function used to get time from device clock. */
+
+typedef int (*eth_timesync_time_set)(struct rte_eth_dev *dev,
+						struct timespec *timestamp);
+/**< @internal Function used to get time from device clock */
+
 typedef int (*eth_get_reg_length_t)(struct rte_eth_dev *dev);
 /**< @internal Retrieve device register count  */
 
@@ -1400,6 +1411,12 @@ struct eth_dev_ops {
 
 	/** Get DCB information */
 	eth_get_dcb_info get_dcb_info;
+	/** Adjust the device clock */
+	eth_timesync_time_adjust timesync_time_adjust;
+	/** Get the device clock timespec */
+	eth_timesync_time_get timesync_time_get;
+	/** Set the device clock timespec */
+	eth_timesync_time_set timesync_time_set;
 };
 
 /**
@@ -3755,6 +3772,53 @@ extern int rte_eth_timesync_read_tx_timestamp(uint8_t port_id,
 					      struct timespec *timestamp);
 
 /**
+ * Adjust the timesync clock on an Ethernet device..
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param delta
+ *   The adjustment in nanoseconds
+ *
+ * @return
+ *   - 0: Success.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -ENOTSUP: The function is not supported by the Ethernet driver.
+ */
+extern int rte_eth_timesync_time_adjust(uint8_t port_id, int64_t delta);
+
+/**
+ * Read the time from the timesync clock on an Ethernet device.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param time
+ *   Pointer to the timespec struct.
+ *
+ * @return
+ *   - 0: Success.
+ */
+extern int rte_eth_timesync_time_get(uint8_t port_id,
+	      struct timespec *time);
+
+
+/**
+ * Set the time of the timesync clock on an Ethernet device.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param time
+ *   Pointer to the timespec struct.
+ *
+ * @return
+ *   - 0: Success.
+ *   - -EINVAL: No timestamp is available.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -ENOTSUP: The function is not supported by the Ethernet driver.
+ */
+extern int rte_eth_timesync_time_set(uint8_t port_id,
+	      struct timespec *time);
+
+/**
  * Copy pci device info to the Ethernet device data.
  *
  * @param eth_dev
@@ -3767,7 +3831,6 @@ extern int rte_eth_timesync_read_tx_timestamp(uint8_t port_id,
  */
 extern void rte_eth_copy_pci_info(struct rte_eth_dev *eth_dev, struct rte_pci_device *pci_dev);
 
-
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/librte_ether/rte_ether_version.map b/lib/librte_ether/rte_ether_version.map
index 9149aa7..bb9c808 100644
--- a/lib/librte_ether/rte_ether_version.map
+++ b/lib/librte_ether/rte_ether_version.map
@@ -135,5 +135,8 @@ DPDK_2.2 {
 	rte_eth_dev_get_dcb_info;
 	rte_eth_rx_queue_info_get;
 	rte_eth_tx_queue_info_get;
+	rte_eth_timesync_time_adjust;
+	rte_eth_timesync_time_get;
+	rte_eth_timesync_time_set;
 
 } DPDK_2.1;
-- 
2.5.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v5 2/7] net: Add common PTP structures and functions
  2015-11-05 14:05 ` [dpdk-dev] [PATCH v5 " Daniel Mrzyglod
  2015-11-05 14:06   ` [dpdk-dev] [PATCH v5 1/7] ethdev: add additional ieee1588 support functions Daniel Mrzyglod
@ 2015-11-05 14:06   ` Daniel Mrzyglod
  2015-11-10 11:25     ` Thomas Monjalon
  2015-11-05 14:06   ` [dpdk-dev] [PATCH v5 3/7] ixgbe: add additional ieee1588 support functions Daniel Mrzyglod
                     ` (6 subsequent siblings)
  8 siblings, 1 reply; 84+ messages in thread
From: Daniel Mrzyglod @ 2015-11-05 14:06 UTC (permalink / raw)
  To: dev

This patch add common functions and structures used for PTP processing.

Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
---
 lib/librte_net/Makefile  |   2 +-
 lib/librte_net/rte_ptp.h | 105 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 106 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_net/rte_ptp.h

diff --git a/lib/librte_net/Makefile b/lib/librte_net/Makefile
index ad2e482..1d33618 100644
--- a/lib/librte_net/Makefile
+++ b/lib/librte_net/Makefile
@@ -34,7 +34,7 @@ include $(RTE_SDK)/mk/rte.vars.mk
 CFLAGS += $(WERROR_FLAGS) -I$(SRCDIR) -O3
 
 # install includes
-SYMLINK-$(CONFIG_RTE_LIBRTE_NET)-include := rte_ip.h rte_tcp.h rte_udp.h rte_sctp.h rte_icmp.h rte_arp.h
+SYMLINK-$(CONFIG_RTE_LIBRTE_NET)-include := rte_ip.h rte_tcp.h rte_udp.h rte_sctp.h rte_icmp.h rte_arp.h rte_ptp.h
 
 
 include $(RTE_SDK)/mk/rte.install.mk
diff --git a/lib/librte_net/rte_ptp.h b/lib/librte_net/rte_ptp.h
new file mode 100644
index 0000000..8a4c83c
--- /dev/null
+++ b/lib/librte_net/rte_ptp.h
@@ -0,0 +1,105 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define NSEC_PER_SEC             1000000000L
+
+/*
+ * Structure for cyclecounter IEEE1588 functionality.
+ */
+struct cyclecounter {
+	uint64_t (*read)(struct rte_eth_dev *dev);
+	uint64_t mask;
+	uint32_t shift;
+};
+
+/*
+ * Structure to hold and calculate Unix epoch time.
+ */
+struct timecounter {
+	struct cyclecounter *cc;
+	uint64_t cycle_last;
+	uint64_t nsec;
+	uint64_t mask;
+	uint64_t frac;
+};
+
+
+/* Utility functions for PTP/IEEE1588 support. */
+
+static inline uint64_t
+timespec_to_ns(const struct timespec *ts)
+{
+	return ((uint64_t) ts->tv_sec * NSEC_PER_SEC) + ts->tv_nsec;
+}
+
+static inline struct timespec
+ns_to_timespec(uint64_t nsec)
+{
+	struct timespec ts = {0, 0};
+
+	if (nsec == 0)
+		return ts;
+
+	ts.tv_sec = nsec / NSEC_PER_SEC;
+	ts.tv_nsec = nsec % NSEC_PER_SEC;
+
+	return ts;
+}
+
+/*
+ * Converts cycle counter cycles to nanoseconds.
+ */
+static inline uint64_t
+cyclecounter_cycles_to_ns(const struct cyclecounter *cc,
+		      uint64_t cycles, uint64_t mask, uint64_t *frac)
+{
+	uint64_t ns;
+
+	/* Add fractional nanoseconds */
+	ns = cycles + *frac;
+	*frac = ns & mask;
+
+	/* Shift to get only nanoseconds. */
+	return ns >> cc->shift;
+}
+
+/*
+ * Like cyclecounter_cycles_to_ns(), but this is used when
+ * computing a time previous to the stored in the cycle counter.
+ */
+static inline uint64_t
+cyclecounter_cycles_to_ns_backwards(const struct cyclecounter *cc,
+			       uint64_t cycles, uint64_t frac)
+{
+	return ((cycles - frac) >> cc->shift);
+}
-- 
2.5.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v5 3/7] ixgbe: add additional ieee1588 support functions
  2015-11-05 14:05 ` [dpdk-dev] [PATCH v5 " Daniel Mrzyglod
  2015-11-05 14:06   ` [dpdk-dev] [PATCH v5 1/7] ethdev: add additional ieee1588 support functions Daniel Mrzyglod
  2015-11-05 14:06   ` [dpdk-dev] [PATCH v5 2/7] net: Add common PTP structures and functions Daniel Mrzyglod
@ 2015-11-05 14:06   ` Daniel Mrzyglod
  2015-11-05 14:06   ` [dpdk-dev] [PATCH v5 4/7] igb: " Daniel Mrzyglod
                     ` (5 subsequent siblings)
  8 siblings, 0 replies; 84+ messages in thread
From: Daniel Mrzyglod @ 2015-11-05 14:06 UTC (permalink / raw)
  To: dev

Add additional functions to support the existing IEEE1588
functionality and to enable getting, setting and adjusting
the device time.

Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
---
 drivers/net/ixgbe/ixgbe_ethdev.c | 272 +++++++++++++++++++++++++++++++++++++--
 drivers/net/ixgbe/ixgbe_ethdev.h |   3 +
 2 files changed, 264 insertions(+), 11 deletions(-)

diff --git a/drivers/net/ixgbe/ixgbe_ethdev.c b/drivers/net/ixgbe/ixgbe_ethdev.c
index 0b0bbcf..1c4300e 100644
--- a/drivers/net/ixgbe/ixgbe_ethdev.c
+++ b/drivers/net/ixgbe/ixgbe_ethdev.c
@@ -126,10 +126,17 @@
 #define IXGBE_HKEY_MAX_INDEX 10
 
 /* Additional timesync values. */
-#define IXGBE_TIMINCA_16NS_SHIFT 24
-#define IXGBE_TIMINCA_INCVALUE   16000000
-#define IXGBE_TIMINCA_INIT       ((0x02 << IXGBE_TIMINCA_16NS_SHIFT) \
-				  | IXGBE_TIMINCA_INCVALUE)
+#define NSEC_PER_SEC             1000000000L
+#define IXGBE_INCVAL_10GB        0x66666666
+#define IXGBE_INCVAL_1GB         0x40000000
+#define IXGBE_INCVAL_100         0x50000000
+#define IXGBE_INCVAL_SHIFT_10GB  28
+#define IXGBE_INCVAL_SHIFT_1GB   24
+#define IXGBE_INCVAL_SHIFT_100   21
+#define IXGBE_INCVAL_SHIFT_82599 7
+#define IXGBE_INCPER_SHIFT_82599 24
+
+#define IXGBE_CYCLECOUNTER_MASK   0xffffffffffffffff
 
 static int eth_ixgbe_dev_init(struct rte_eth_dev *eth_dev);
 static int eth_ixgbe_dev_uninit(struct rte_eth_dev *eth_dev);
@@ -325,6 +332,11 @@ static int ixgbe_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 					    uint32_t flags);
 static int ixgbe_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 					    struct timespec *timestamp);
+static int ixgbe_timesync_time_adjust(struct rte_eth_dev *dev, int64_t delta);
+static int ixgbe_timesync_time_get(struct rte_eth_dev *dev,
+				   struct timespec *timestamp);
+static int ixgbe_timesync_time_set(struct rte_eth_dev *dev,
+				   struct timespec *timestamp);
 
 /*
  * Define VF Stats MACRO for Non "cleared on read" register
@@ -480,6 +492,9 @@ static const struct eth_dev_ops ixgbe_eth_dev_ops = {
 	.get_eeprom           = ixgbe_get_eeprom,
 	.set_eeprom           = ixgbe_set_eeprom,
 	.get_dcb_info         = ixgbe_dev_get_dcb_info,
+	.timesync_time_adjust = ixgbe_timesync_time_adjust,
+	.timesync_time_get    = ixgbe_timesync_time_get,
+	.timesync_time_set    = ixgbe_timesync_time_set,
 };
 
 /*
@@ -5608,20 +5623,232 @@ ixgbe_dev_set_mc_addr_list(struct rte_eth_dev *dev,
 					 ixgbe_dev_addr_list_itr, TRUE);
 }
 
+/*
+ * Register units might not be nanoseconds. This function converts
+ * these units into nanoseconds and adds to the previous time stored.
+ */
+static uint64_t
+timecounter_cycles_to_ns_time(struct timecounter *tc, uint64_t cycle_tstamp)
+{
+	uint64_t delta;
+	uint64_t nsec = tc->nsec, frac = tc->frac;
+
+	delta = (cycle_tstamp - tc->cycle_last) & tc->cc->mask;
+	/*
+	 * Cycle counts that are correctly converted as they
+	 * are between -1/2 max cycle count and +1/2 max cycle count.
+	 */
+	if (delta > (tc->cc->mask / 2)) {
+		delta = (tc->cycle_last - cycle_tstamp) & tc->cc->mask;
+		nsec -= cyclecounter_cycles_to_ns_backwards(tc->cc,
+							    delta, frac);
+	} else {
+		nsec += cyclecounter_cycles_to_ns(tc->cc, delta, tc->mask,
+						  &frac);
+	}
+
+	return nsec;
+}
+
+static uint64_t
+ixgbe_read_timesync_cyclecounter(struct rte_eth_dev *dev)
+{
+	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	uint64_t systime_cycles = 0;
+
+	switch (hw->mac.type) {
+	case ixgbe_mac_X550:
+		/* SYSTIMEL stores ns and SYSTIMEH stores seconds. */
+		systime_cycles = (uint64_t)IXGBE_READ_REG(hw, IXGBE_SYSTIML);
+		systime_cycles += (uint64_t)IXGBE_READ_REG(hw, IXGBE_SYSTIMH)
+				* NSEC_PER_SEC;
+		break;
+	default:
+		systime_cycles |= (uint64_t)IXGBE_READ_REG(hw, IXGBE_SYSTIML);
+		systime_cycles |= (uint64_t)IXGBE_READ_REG(hw, IXGBE_SYSTIMH)
+				<< 32;
+	}
+
+	return systime_cycles;
+}
+
+/*
+ * Get nanoseconds since the last call of this function.
+ */
+static uint64_t
+timecounter_read_ns_delta(struct rte_eth_dev *dev)
+{
+	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	uint64_t cycle_now, cycle_delta;
+	uint64_t ns_offset;
+	struct ixgbe_adapter *adapter =
+				(struct ixgbe_adapter *)dev->data->dev_private;
+
+	/* Read cycle counter. */
+	cycle_now = adapter->tc.cc->read(dev);
+
+	/* Calculate the delta since the last timecounter_read_delta(). */
+	cycle_delta = (cycle_now - adapter->tc.cycle_last) &
+		       adapter->tc.cc->mask;
+
+	/* Convert to nanoseconds. */
+	if (hw->mac.type == ixgbe_mac_X550)
+		/* Registers store directly nanoseconds, no need to convert. */
+		ns_offset = cycle_delta;
+	else
+		ns_offset = cyclecounter_cycles_to_ns(adapter->tc.cc,
+						      cycle_delta,
+						      adapter->tc.mask,
+						      &adapter->tc.frac);
+
+	/*
+	 * Store current cycle counter for next
+	 * timecounter_read_ns_delta() call.
+	 */
+	adapter->tc.cycle_last = cycle_now;
+
+	return ns_offset;
+}
+
+static uint64_t
+timecounter_read(struct rte_eth_dev *dev)
+{
+	uint64_t nsec;
+	struct ixgbe_adapter *adapter =
+			(struct ixgbe_adapter *)dev->data->dev_private;
+
+	/* Increment time by nanoseconds since last call. */
+	nsec = timecounter_read_ns_delta(dev);
+	nsec += adapter->tc.nsec;
+	adapter->tc.nsec = nsec;
+
+	return nsec;
+}
+
+static void
+timecounter_init(struct rte_eth_dev *dev, uint64_t start_time)
+{
+	struct ixgbe_adapter *adapter =
+				(struct ixgbe_adapter *)dev->data->dev_private;
+
+	adapter->tc.cc = &adapter->cc;
+	adapter->tc.cycle_last = adapter->tc.cc->read(dev);
+	adapter->tc.nsec = start_time;
+	adapter->tc.mask = (1ULL << adapter->tc.cc->shift) - 1;
+	adapter->tc.frac = 0;
+}
+
+static void
+ixgbe_start_cyclecounter(struct rte_eth_dev *dev)
+{
+	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct ixgbe_adapter *adapter =
+		(struct ixgbe_adapter *)dev->data->dev_private;
+	struct rte_eth_link link;
+	uint32_t incval = 0;
+	uint32_t shift = 0;
+
+	/* Get current link speed. */
+	memset(&link, 0, sizeof(link));
+	ixgbe_dev_link_update(dev, 1);
+	rte_ixgbe_dev_atomic_read_link_status(dev, &link);
+
+	switch (link.link_speed) {
+	case ETH_LINK_SPEED_100:
+		incval = IXGBE_INCVAL_100;
+		shift = IXGBE_INCVAL_SHIFT_100;
+		break;
+	case ETH_LINK_SPEED_1000:
+		incval = IXGBE_INCVAL_1GB;
+		shift = IXGBE_INCVAL_SHIFT_1GB;
+		break;
+	case ETH_LINK_SPEED_10000:
+	default:
+		incval = IXGBE_INCVAL_10GB;
+		shift = IXGBE_INCVAL_SHIFT_10GB;
+		break;
+	}
+
+	switch (hw->mac.type) {
+	case ixgbe_mac_X550:
+		/* Independent of link speed. */
+		incval = 1;
+		/* Cycles read will be interpreted as ns. */
+		shift = 0;
+		/* Fall-through */
+	case ixgbe_mac_X540:
+		IXGBE_WRITE_REG(hw, IXGBE_TIMINCA, incval);
+		break;
+	case ixgbe_mac_82599EB:
+		incval >>= IXGBE_INCVAL_SHIFT_82599;
+		shift -= IXGBE_INCVAL_SHIFT_82599;
+		IXGBE_WRITE_REG(hw, IXGBE_TIMINCA,
+				(1 << IXGBE_INCPER_SHIFT_82599) | incval);
+		break;
+	default:
+		/* Not supported. */
+		return;
+	}
+
+	memset(&adapter->cc, 0, sizeof(struct cyclecounter));
+	adapter->cc.read = ixgbe_read_timesync_cyclecounter;
+	adapter->cc.mask = IXGBE_CYCLECOUNTER_MASK;
+	adapter->cc.shift = shift;
+}
+
+static int
+ixgbe_timesync_time_adjust(struct rte_eth_dev *dev, int64_t delta)
+{
+	struct ixgbe_adapter *adapter =
+			(struct ixgbe_adapter *)dev->data->dev_private;
+
+	adapter->tc.nsec += delta;
+
+	return 0;
+}
+
+static int
+ixgbe_timesync_time_set(struct rte_eth_dev *dev, struct timespec *ts)
+{
+	uint64_t ns;
+
+	ns = timespec_to_ns(ts);
+	/* Reset the timecounter. */
+	timecounter_init(dev, ns);
+
+	return 0;
+}
+
+static int
+ixgbe_timesync_time_get(struct rte_eth_dev *dev, struct timespec *ts)
+{
+	uint64_t ns;
+
+	ns = timecounter_read(dev);
+	*ts = ns_to_timespec(ns);
+
+	return 0;
+}
+
 static int
 ixgbe_timesync_enable(struct rte_eth_dev *dev)
 {
 	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
 	uint32_t tsync_ctl;
 	uint32_t tsauxc;
+	uint64_t ns;
+	struct timespec zerotime = {0, 0};
 
 	/* Enable system time for platforms where it isn't on by default. */
 	tsauxc = IXGBE_READ_REG(hw, IXGBE_TSAUXC);
 	tsauxc &= ~IXGBE_TSAUXC_DISABLE_SYSTIME;
-	IXGBE_WRITE_REG(hw, IXGBE_TSAUXC, tsauxc);
 
-	/* Start incrementing the register used to timestamp PTP packets. */
-	IXGBE_WRITE_REG(hw, IXGBE_TIMINCA, IXGBE_TIMINCA_INIT);
+	/* Set 0.0 epoch time to initialize timecounter. */
+	ns = timespec_to_ns(&zerotime);
+	ixgbe_start_cyclecounter(dev);
+	timecounter_init(dev, ns);
+
+	IXGBE_WRITE_REG(hw, IXGBE_TSAUXC, tsauxc);
 
 	/* Enable L2 filtering of IEEE1588/802.1AS Ethernet frame types. */
 	IXGBE_WRITE_REG(hw, IXGBE_ETQF(IXGBE_ETQF_FILTER_1588),
@@ -5639,6 +5866,9 @@ ixgbe_timesync_enable(struct rte_eth_dev *dev)
 	tsync_ctl |= IXGBE_TSYNCTXCTL_ENABLED;
 	IXGBE_WRITE_REG(hw, IXGBE_TSYNCTXCTL, tsync_ctl);
 
+	/* After writing to registers should be flush. */
+	IXGBE_WRITE_FLUSH(hw);
+
 	return 0;
 }
 
@@ -5673,9 +5903,13 @@ ixgbe_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 				 uint32_t flags __rte_unused)
 {
 	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct ixgbe_adapter *adapter =
+		(struct ixgbe_adapter *)dev->data->dev_private;
+
 	uint32_t tsync_rxctl;
 	uint32_t rx_stmpl;
 	uint32_t rx_stmph;
+	uint64_t regival = 0;
 
 	tsync_rxctl = IXGBE_READ_REG(hw, IXGBE_TSYNCRXCTL);
 	if ((tsync_rxctl & IXGBE_TSYNCRXCTL_VALID) == 0)
@@ -5683,9 +5917,15 @@ ixgbe_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 
 	rx_stmpl = IXGBE_READ_REG(hw, IXGBE_RXSTMPL);
 	rx_stmph = IXGBE_READ_REG(hw, IXGBE_RXSTMPH);
+	timecounter_read(dev);
 
-	timestamp->tv_sec = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
-	timestamp->tv_nsec = 0;
+	if (hw->mac.type == ixgbe_mac_X550)
+		regival = (uint64_t)((uint64_t)rx_stmph * NSEC_PER_SEC
+			+ rx_stmpl);
+	else
+		regival = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
+	regival = timecounter_cycles_to_ns_time(&adapter->tc, regival);
+	*timestamp = ns_to_timespec(regival);
 
 	return  0;
 }
@@ -5695,9 +5935,13 @@ ixgbe_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 				 struct timespec *timestamp)
 {
 	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct ixgbe_adapter *adapter =
+		(struct ixgbe_adapter *)dev->data->dev_private;
+
 	uint32_t tsync_txctl;
 	uint32_t tx_stmpl;
 	uint32_t tx_stmph;
+	uint64_t regival = 0;
 
 	tsync_txctl = IXGBE_READ_REG(hw, IXGBE_TSYNCTXCTL);
 	if ((tsync_txctl & IXGBE_TSYNCTXCTL_VALID) == 0)
@@ -5705,9 +5949,15 @@ ixgbe_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 
 	tx_stmpl = IXGBE_READ_REG(hw, IXGBE_TXSTMPL);
 	tx_stmph = IXGBE_READ_REG(hw, IXGBE_TXSTMPH);
+	timecounter_read(dev);
 
-	timestamp->tv_sec = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
-	timestamp->tv_nsec = 0;
+	if (hw->mac.type == ixgbe_mac_X550)
+		regival = (uint64_t)((uint64_t)tx_stmph * NSEC_PER_SEC
+			+ tx_stmpl);
+	else
+		regival = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
+	regival = timecounter_cycles_to_ns_time(&adapter->tc, regival);
+	*timestamp = ns_to_timespec(regival);
 
 	return  0;
 }
diff --git a/drivers/net/ixgbe/ixgbe_ethdev.h b/drivers/net/ixgbe/ixgbe_ethdev.h
index 1856c42..48b81e8 100644
--- a/drivers/net/ixgbe/ixgbe_ethdev.h
+++ b/drivers/net/ixgbe/ixgbe_ethdev.h
@@ -37,6 +37,7 @@
 #include "base/ixgbe_dcb_82599.h"
 #include "base/ixgbe_dcb_82598.h"
 #include "ixgbe_bypass.h"
+#include <rte_ptp.h>
 
 /* need update link, bit flag */
 #define IXGBE_FLAG_NEED_LINK_UPDATE (uint32_t)(1 << 0)
@@ -282,6 +283,8 @@ struct ixgbe_adapter {
 
 	bool rx_bulk_alloc_allowed;
 	bool rx_vec_allowed;
+	struct cyclecounter cc;
+	struct timecounter tc;
 };
 
 #define IXGBE_DEV_PRIVATE_TO_HW(adapter)\
-- 
2.5.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v5 4/7] igb: add additional ieee1588 support functions
  2015-11-05 14:05 ` [dpdk-dev] [PATCH v5 " Daniel Mrzyglod
                     ` (2 preceding siblings ...)
  2015-11-05 14:06   ` [dpdk-dev] [PATCH v5 3/7] ixgbe: add additional ieee1588 support functions Daniel Mrzyglod
@ 2015-11-05 14:06   ` Daniel Mrzyglod
  2015-11-05 14:06   ` [dpdk-dev] [PATCH v5 5/7] i40e: " Daniel Mrzyglod
                     ` (4 subsequent siblings)
  8 siblings, 0 replies; 84+ messages in thread
From: Daniel Mrzyglod @ 2015-11-05 14:06 UTC (permalink / raw)
  To: dev

From: Pablo de Lara <pablo.de.lara.guarch@intel.com>

Add additional functions to support the existing IEEE1588
functionality and to enable getting, setting and adjusting
the device time.

Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
---
 drivers/net/e1000/e1000_ethdev.h |   3 +
 drivers/net/e1000/igb_ethdev.c   | 299 +++++++++++++++++++++++++++++++++++++--
 2 files changed, 292 insertions(+), 10 deletions(-)

diff --git a/drivers/net/e1000/e1000_ethdev.h b/drivers/net/e1000/e1000_ethdev.h
index a667a1a..c2b64c7 100644
--- a/drivers/net/e1000/e1000_ethdev.h
+++ b/drivers/net/e1000/e1000_ethdev.h
@@ -33,6 +33,7 @@
 
 #ifndef _E1000_ETHDEV_H_
 #define _E1000_ETHDEV_H_
+#include <rte_ptp.h>
 
 /* need update link, bit flag */
 #define E1000_FLAG_NEED_LINK_UPDATE (uint32_t)(1 << 0)
@@ -257,6 +258,8 @@ struct e1000_adapter {
 	struct e1000_vf_info    *vfdata;
 	struct e1000_filter_info filter;
 	bool stopped;
+	struct cyclecounter cc;
+	struct timecounter tc;
 };
 
 #define E1000_DEV_PRIVATE(adapter) \
diff --git a/drivers/net/e1000/igb_ethdev.c b/drivers/net/e1000/igb_ethdev.c
index 2cb115c..dcc68b5 100644
--- a/drivers/net/e1000/igb_ethdev.c
+++ b/drivers/net/e1000/igb_ethdev.c
@@ -78,10 +78,11 @@
 #define IGB_8_BIT_MASK   UINT8_MAX
 
 /* Additional timesync values. */
-#define E1000_ETQF_FILTER_1588 3
-#define E1000_TIMINCA_INCVALUE 16000000
-#define E1000_TIMINCA_INIT     ((0x02 << E1000_TIMINCA_16NS_SHIFT) \
-				| E1000_TIMINCA_INCVALUE)
+#define E1000_CYCLECOUNTER_MASK      0xffffffffffffffff
+#define E1000_ETQF_FILTER_1588       3
+#define IGB_82576_TSYNC_SHIFT        16
+#define E1000_INCPERIOD_82576        (1 << E1000_TIMINCA_16NS_SHIFT)
+#define E1000_INCVALUE_82576         (16 << IGB_82576_TSYNC_SHIFT)
 #define E1000_TSAUXC_DISABLE_SYSTIME 0x80000000
 
 static int  eth_igb_configure(struct rte_eth_dev *dev);
@@ -236,6 +237,11 @@ static int igb_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 					  uint32_t flags);
 static int igb_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 					  struct timespec *timestamp);
+static int igb_timesync_time_adjust(struct rte_eth_dev *dev, int64_t delta);
+static int igb_timesync_time_get(struct rte_eth_dev *dev,
+				 struct timespec *timestamp);
+static int igb_timesync_time_set(struct rte_eth_dev *dev,
+				 struct timespec *timestamp);
 static int eth_igb_rx_queue_intr_enable(struct rte_eth_dev *dev,
 					uint16_t queue_id);
 static int eth_igb_rx_queue_intr_disable(struct rte_eth_dev *dev,
@@ -349,6 +355,9 @@ static const struct eth_dev_ops eth_igb_ops = {
 	.get_eeprom_length    = eth_igb_get_eeprom_length,
 	.get_eeprom           = eth_igb_get_eeprom,
 	.set_eeprom           = eth_igb_set_eeprom,
+	.timesync_time_adjust  = igb_timesync_time_adjust,
+	.timesync_time_get     = igb_timesync_time_get,
+	.timesync_time_set     = igb_timesync_time_set,
 };
 
 /*
@@ -4182,20 +4191,248 @@ eth_igb_set_mc_addr_list(struct rte_eth_dev *dev,
 	return 0;
 }
 
+/*
+ * Register units might not be nanoseconds. This function converts
+ * these units into nanoseconds and adds to the previous time stored.
+ */
+static uint64_t
+timecounter_cycles_to_ns_time(struct timecounter *tc, uint64_t cycle_tstamp)
+{
+	uint64_t delta;
+	uint64_t nsec = tc->nsec, frac = tc->frac;
+
+	delta = (cycle_tstamp - tc->cycle_last) & tc->cc->mask;
+	/*
+	 * Cycle counts that are correctly converted as they
+	 * are between -1/2 max cycle count and +1/2 max cycle count.
+	 */
+	if (delta > (tc->cc->mask / 2)) {
+		delta = (tc->cycle_last - cycle_tstamp) & tc->cc->mask;
+		nsec -= cyclecounter_cycles_to_ns_backwards(tc->cc, delta,
+							    frac);
+	} else {
+		nsec += cyclecounter_cycles_to_ns(tc->cc, delta, tc->mask,
+						  &frac);
+	}
+
+	return nsec;
+}
+
+static uint64_t
+igb_read_timesync_cyclecounter(struct rte_eth_dev *dev)
+{
+	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	uint64_t systime_cycles = 0;
+
+	switch (hw->mac.type) {
+	case e1000_i210:
+	case e1000_i211:
+		/*
+		 * Need to read System Time Residue Register to be able
+		 * to read the other two registers.
+		 */
+		E1000_READ_REG(hw, E1000_SYSTIMR);
+		/* SYSTIMEL stores ns and SYSTIMEH stores seconds. */
+		systime_cycles = (uint64_t)E1000_READ_REG(hw, E1000_SYSTIML);
+		systime_cycles += (uint64_t)E1000_READ_REG(hw, E1000_SYSTIMH)
+				* NSEC_PER_SEC;
+		break;
+	case e1000_82580:
+	case e1000_i350:
+	case e1000_i354:
+		/*
+		 * Need to read System Time Residue Register to be able
+		 * to read the other two registers.
+		 */
+		E1000_READ_REG(hw, E1000_SYSTIMR);
+		systime_cycles |= (uint64_t)E1000_READ_REG(hw, E1000_SYSTIML);
+		/* Only the 8 LSB are valid. */
+		systime_cycles |= (uint64_t)(E1000_READ_REG(hw, E1000_SYSTIMH)
+				& 0xff) << 32;
+		break;
+	default:
+		systime_cycles |= (uint64_t)E1000_READ_REG(hw, E1000_SYSTIML);
+		systime_cycles |= (uint64_t)E1000_READ_REG(hw, E1000_SYSTIMH)
+				<< 32;
+		break;
+	}
+
+	return systime_cycles;
+}
+
+/*
+ * Get nanoseconds since the last call of this function.
+ */
+static uint64_t
+timecounter_read_ns_delta(struct rte_eth_dev *dev)
+{
+	uint64_t cycle_now, cycle_delta;
+	uint64_t ns_offset;
+	struct e1000_adapter *adapter =
+				(struct e1000_adapter *)dev->data->dev_private;
+	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+
+	/* Read cycle counter. */
+	cycle_now = adapter->tc.cc->read(dev);
+
+	/* Calculate the delta since the last timecounter_read_delta(). */
+	cycle_delta = (cycle_now - adapter->tc.cycle_last) &
+		       adapter->tc.cc->mask;
+
+	/* Convert to nanoseconds. */
+	switch (hw->mac.type) {
+	case e1000_i210:
+	case e1000_i211:
+	case e1000_82580:
+	case e1000_i350:
+	case e1000_i354:
+		/* Registers store directly nanoseconds, no need to convert. */
+		ns_offset = cycle_delta;
+		break;
+	default:
+		ns_offset = cyclecounter_cycles_to_ns(adapter->tc.cc,
+						      cycle_delta,
+						      adapter->tc.mask,
+						      &adapter->tc.frac);
+	}
+
+	/*
+	 * Store current cycle counter for next
+	 * timecounter_read_ns_delta() call.
+	 */
+	adapter->tc.cycle_last = cycle_now;
+
+	return ns_offset;
+}
+
+static uint64_t
+timecounter_read(struct rte_eth_dev *dev)
+{
+	uint64_t nsec;
+	struct e1000_adapter *adapter =
+			(struct e1000_adapter *)dev->data->dev_private;
+
+	/* increment time by nanoseconds since last call */
+	nsec = timecounter_read_ns_delta(dev);
+	nsec += adapter->tc.nsec;
+	adapter->tc.nsec = nsec;
+
+	return nsec;
+}
+
+static void
+timecounter_init(struct rte_eth_dev *dev, uint64_t start_time)
+{
+	struct e1000_adapter *adapter =
+				(struct e1000_adapter *)dev->data->dev_private;
+	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+
+	adapter->tc.cc = &adapter->cc;
+	adapter->tc.cycle_last = adapter->tc.cc->read(dev);
+	adapter->tc.nsec = start_time;
+	switch (hw->mac.type) {
+	case e1000_82580:
+	case e1000_i350:
+	case e1000_i354:
+		/* 32 LSB bits + 8 MSB bits = 40 bits */
+		adapter->tc.mask = (1ULL << 40) - 1;
+	default:
+		adapter->tc.mask = (1ULL << adapter->tc.cc->shift) - 1;
+	}
+	adapter->tc.frac = 0;
+}
+
+static void
+igb_start_cyclecounter(struct rte_eth_dev *dev)
+{
+	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct e1000_adapter *adapter =
+		(struct e1000_adapter *)dev->data->dev_private;
+	uint32_t incval = 1;
+	uint32_t shift = 0;
+
+	switch (hw->mac.type) {
+	case e1000_i210:
+	case e1000_i211:
+	case e1000_82580:
+	case e1000_i350:
+	case e1000_i354:
+		/*
+		 * Start incrementing the register
+		 * used to timestamp PTP packets.
+		 */
+		E1000_WRITE_REG(hw, E1000_TIMINCA, incval);
+		break;
+	case e1000_82576:
+		incval = E1000_INCVALUE_82576;
+		shift = IGB_82576_TSYNC_SHIFT;
+		E1000_WRITE_REG(hw, E1000_TIMINCA,
+				E1000_INCPERIOD_82576 | incval);
+		break;
+	default:
+		/* Not supported */
+		return;
+	}
+
+	memset(&adapter->cc, 0, sizeof(struct cyclecounter));
+	adapter->cc.read = igb_read_timesync_cyclecounter;
+	adapter->cc.mask = E1000_CYCLECOUNTER_MASK;
+	adapter->cc.shift = shift;
+}
+
+static int
+igb_timesync_time_adjust(struct rte_eth_dev *dev, int64_t delta)
+{
+	struct e1000_adapter *adapter =
+			(struct e1000_adapter *)dev->data->dev_private;
+
+	adapter->tc.nsec += delta;
+
+	return 0;
+}
+
+static int
+igb_timesync_time_set(struct rte_eth_dev *dev, struct timespec *ts)
+{
+	uint64_t ns;
+
+	ns = timespec_to_ns(ts);
+
+	/* Reset the timecounter. */
+	timecounter_init(dev, ns);
+
+	return 0;
+}
+
+static int
+igb_timesync_time_get(struct rte_eth_dev *dev, struct timespec *ts)
+{
+	uint64_t ns;
+
+	ns = timecounter_read(dev);
+	*ts = ns_to_timespec(ns);
+
+	return 0;
+}
+
 static int
 igb_timesync_enable(struct rte_eth_dev *dev)
 {
 	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
 	uint32_t tsync_ctl;
 	uint32_t tsauxc;
+	uint64_t ns;
+	struct timespec zerotime = {0, 0};
 
 	/* Enable system time for it isn't on by default. */
 	tsauxc = E1000_READ_REG(hw, E1000_TSAUXC);
 	tsauxc &= ~E1000_TSAUXC_DISABLE_SYSTIME;
 	E1000_WRITE_REG(hw, E1000_TSAUXC, tsauxc);
 
-	/* Start incrementing the register used to timestamp PTP packets. */
-	E1000_WRITE_REG(hw, E1000_TIMINCA, E1000_TIMINCA_INIT);
+	/* Set 0.0 epoch time to initialize timecounter. */
+	ns = timespec_to_ns(&zerotime);
+	igb_start_cyclecounter(dev);
+	timecounter_init(dev, ns);
 
 	/* Enable L2 filtering of IEEE1588/802.1AS Ethernet frame types. */
 	E1000_WRITE_REG(hw, E1000_ETQF(E1000_ETQF_FILTER_1588),
@@ -4247,9 +4484,13 @@ igb_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 			       uint32_t flags __rte_unused)
 {
 	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct e1000_adapter *adapter =
+			(struct e1000_adapter *)dev->data->dev_private;
+
 	uint32_t tsync_rxctl;
 	uint32_t rx_stmpl;
 	uint32_t rx_stmph;
+	uint64_t regival = 0;
 
 	tsync_rxctl = E1000_READ_REG(hw, E1000_TSYNCRXCTL);
 	if ((tsync_rxctl & E1000_TSYNCRXCTL_VALID) == 0)
@@ -4257,9 +4498,26 @@ igb_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 
 	rx_stmpl = E1000_READ_REG(hw, E1000_RXSTMPL);
 	rx_stmph = E1000_READ_REG(hw, E1000_RXSTMPH);
+	timecounter_read(dev);
+
+	switch (hw->mac.type) {
+	case e1000_82580:
+	case e1000_i350:
+	case e1000_i354:
+		regival = (uint64_t)((((uint64_t)(rx_stmph & 0xff)) << 32)
+			| rx_stmpl);
+		break;
+	case e1000_i210:
+	case e1000_i211:
+		regival = (uint64_t)((uint64_t)rx_stmph * NSEC_PER_SEC
+			+ rx_stmpl);
+		break;
+	default:
+		regival = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
+	}
 
-	timestamp->tv_sec = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
-	timestamp->tv_nsec = 0;
+	regival = timecounter_cycles_to_ns_time(&adapter->tc, regival);
+	*timestamp = ns_to_timespec(regival);
 
 	return  0;
 }
@@ -4269,6 +4527,10 @@ igb_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 			       struct timespec *timestamp)
 {
 	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct e1000_adapter *adapter =
+			(struct e1000_adapter *)dev->data->dev_private;
+	uint64_t regival = 0;
+
 	uint32_t tsync_txctl;
 	uint32_t tx_stmpl;
 	uint32_t tx_stmph;
@@ -4279,9 +4541,26 @@ igb_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 
 	tx_stmpl = E1000_READ_REG(hw, E1000_TXSTMPL);
 	tx_stmph = E1000_READ_REG(hw, E1000_TXSTMPH);
+	timecounter_read(dev);
+
+	switch (hw->mac.type) {
+	case e1000_82580:
+	case e1000_i350:
+	case e1000_i354:
+		regival = (uint64_t)((((uint64_t)(tx_stmph & 0xff)) << 32)
+			| tx_stmpl);
+		break;
+	case e1000_i210:
+	case e1000_i211:
+		regival = (uint64_t)((uint64_t)tx_stmph * NSEC_PER_SEC
+			+ tx_stmpl);
+		break;
+	default:
+		regival = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
+	}
 
-	timestamp->tv_sec = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
-	timestamp->tv_nsec = 0;
+	regival = timecounter_cycles_to_ns_time(&adapter->tc, regival);
+	*timestamp = ns_to_timespec(regival);
 
 	return  0;
 }
-- 
2.5.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v5 5/7] i40e: add additional ieee1588 support functions
  2015-11-05 14:05 ` [dpdk-dev] [PATCH v5 " Daniel Mrzyglod
                     ` (3 preceding siblings ...)
  2015-11-05 14:06   ` [dpdk-dev] [PATCH v5 4/7] igb: " Daniel Mrzyglod
@ 2015-11-05 14:06   ` Daniel Mrzyglod
  2015-11-05 14:06   ` [dpdk-dev] [PATCH v5 6/7] example: PTP client slave minimal implementation Daniel Mrzyglod
                     ` (3 subsequent siblings)
  8 siblings, 0 replies; 84+ messages in thread
From: Daniel Mrzyglod @ 2015-11-05 14:06 UTC (permalink / raw)
  To: dev

From: Pablo de Lara <pablo.de.lara.guarch@intel.com>

Add additional functions to support the existing IEEE1588
functionality and to enable getting, setting and adjusting
the device time.

Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
---
 drivers/net/i40e/i40e_ethdev.c | 196 ++++++++++++++++++++++++++++++++++++-----
 drivers/net/i40e/i40e_ethdev.h |   5 ++
 2 files changed, 181 insertions(+), 20 deletions(-)

diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index ddf3d38..98d61f9 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -125,11 +125,13 @@
 	(1UL << RTE_ETH_FLOW_NONFRAG_IPV6_OTHER) | \
 	(1UL << RTE_ETH_FLOW_L2_PAYLOAD))
 
-#define I40E_PTP_40GB_INCVAL  0x0199999999ULL
-#define I40E_PTP_10GB_INCVAL  0x0333333333ULL
-#define I40E_PTP_1GB_INCVAL   0x2000000000ULL
-#define I40E_PRTTSYN_TSYNENA  0x80000000
-#define I40E_PRTTSYN_TSYNTYPE 0x0e000000
+/* Additional timesync values. */
+#define I40E_PTP_40GB_INCVAL     0x0199999999ULL
+#define I40E_PTP_10GB_INCVAL     0x0333333333ULL
+#define I40E_PTP_1GB_INCVAL      0x2000000000ULL
+#define I40E_PRTTSYN_TSYNENA     0x80000000
+#define I40E_PRTTSYN_TSYNTYPE    0x0e000000
+#define I40E_CYCLECOUNTER_MASK   0xffffffffffffffff
 
 #define I40E_MAX_PERCENT            100
 #define I40E_DEFAULT_DCB_APP_NUM    1
@@ -400,11 +402,20 @@ static int i40e_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 static int i40e_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 					   struct timespec *timestamp);
 static void i40e_read_stats_registers(struct i40e_pf *pf, struct i40e_hw *hw);
+
+static int i40e_timesync_time_adjust(struct rte_eth_dev *dev, int64_t delta);
+
+static int i40e_timesync_time_get(struct rte_eth_dev *dev,
+				  struct timespec *timestamp);
+static int i40e_timesync_time_set(struct rte_eth_dev *dev,
+				  struct timespec *timestamp);
+
 static int i40e_dev_rx_queue_intr_enable(struct rte_eth_dev *dev,
 					 uint16_t queue_id);
 static int i40e_dev_rx_queue_intr_disable(struct rte_eth_dev *dev,
 					  uint16_t queue_id);
 
+
 static const struct rte_pci_id pci_id_i40e_map[] = {
 #define RTE_PCI_DEV_ID_DECL_I40E(vend, dev) {RTE_PCI_DEVICE(vend, dev)},
 #include "rte_pci_dev_ids.h"
@@ -469,6 +480,9 @@ static const struct eth_dev_ops i40e_eth_dev_ops = {
 	.timesync_read_rx_timestamp   = i40e_timesync_read_rx_timestamp,
 	.timesync_read_tx_timestamp   = i40e_timesync_read_tx_timestamp,
 	.get_dcb_info                 = i40e_dev_get_dcb_info,
+	.timesync_time_adjust         = i40e_timesync_time_adjust,
+	.timesync_time_get            = i40e_timesync_time_get,
+	.timesync_time_set            = i40e_timesync_time_set,
 };
 
 /* store statistics names and its offset in stats structure */
@@ -7738,17 +7752,95 @@ i40e_mirror_rule_reset(struct rte_eth_dev *dev, uint8_t sw_id)
 	return 0;
 }
 
-static int
-i40e_timesync_enable(struct rte_eth_dev *dev)
+/*
+ * Adds the new cycles (in nanoseconds) to the previous time stored.
+ */
+static uint64_t
+timecounter_cycles_to_ns_time(struct timecounter *tc, uint64_t cycle_tstamp)
+{
+	uint64_t delta = (cycle_tstamp - tc->cycle_last);
+	uint64_t nsec = tc->nsec;
+
+	nsec += delta;
+
+	return nsec;
+}
+
+static uint64_t
+i40e_read_timesync_cyclecounter(struct rte_eth_dev *dev)
 {
 	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
-	struct rte_eth_link *link = &dev->data->dev_link;
-	uint32_t tsync_ctl_l;
-	uint32_t tsync_ctl_h;
+	uint64_t systim_cycles = 0;
+
+	systim_cycles |= (uint64_t)I40E_READ_REG(hw, I40E_PRTTSYN_TIME_L);
+	systim_cycles |= (uint64_t)I40E_READ_REG(hw, I40E_PRTTSYN_TIME_H)
+			<< 32;
+
+	return systim_cycles;
+}
+
+static uint64_t
+timecounter_read_ns_delta(struct rte_eth_dev *dev)
+{
+	uint64_t cycle_now, cycle_delta;
+	struct i40e_adapter *adapter =
+			(struct i40e_adapter *)dev->data->dev_private;
+
+	/* Read cycle counter. */
+	cycle_now = adapter->tc.cc->read(dev);
+
+	/* Calculate the delta since the last timecounter_read_delta(). */
+	cycle_delta = (cycle_now - adapter->tc.cycle_last);
+
+	/* Update time stamp of timecounter_read_delta() call. */
+	adapter->tc.cycle_last = cycle_now;
+
+	/* Delta already in nanoseconds. */
+	return cycle_delta;
+}
+
+static uint64_t
+timecounter_read(struct rte_eth_dev *dev)
+{
+	uint64_t nsec;
+	struct i40e_adapter *adapter =
+			(struct i40e_adapter *)dev->data->dev_private;
+
+	/* Increment time by nanoseconds since last call. */
+	nsec = timecounter_read_ns_delta(dev);
+	nsec += adapter->tc.nsec;
+	adapter->tc.nsec = nsec;
+
+	return nsec;
+}
+
+static void
+timecounter_init(struct rte_eth_dev *dev,
+		      uint64_t start_time)
+{
+	struct i40e_adapter *adapter =
+			(struct i40e_adapter *)dev->data->dev_private;
+	adapter->tc.cc = &adapter->cc;
+	adapter->tc.cycle_last = adapter->tc.cc->read(dev);
+	adapter->tc.nsec = start_time;
+}
+
+static void
+i40e_start_cyclecounter(struct rte_eth_dev *dev)
+{
+	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct i40e_adapter *adapter =
+			(struct i40e_adapter *)dev->data->dev_private;
+	struct rte_eth_link link;
 	uint32_t tsync_inc_l;
 	uint32_t tsync_inc_h;
 
-	switch (link->link_speed) {
+	/* Get current link speed. */
+	memset(&link, 0, sizeof(link));
+	i40e_dev_link_update(dev, 1);
+	rte_i40e_dev_atomic_read_link_status(dev, &link);
+
+	switch (link.link_speed) {
 	case ETH_LINK_SPEED_40G:
 		tsync_inc_l = I40E_PTP_40GB_INCVAL & 0xFFFFFFFF;
 		tsync_inc_h = I40E_PTP_40GB_INCVAL >> 32;
@@ -7766,6 +7858,63 @@ i40e_timesync_enable(struct rte_eth_dev *dev)
 		tsync_inc_h = 0x0;
 	}
 
+	/* Set the timesync increment value. */
+	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_L, tsync_inc_l);
+	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_H, tsync_inc_h);
+
+	memset(&adapter->cc, 0, sizeof(struct cyclecounter));
+	adapter->cc.read = i40e_read_timesync_cyclecounter;
+}
+
+static int
+i40e_timesync_time_adjust(struct rte_eth_dev *dev, int64_t delta)
+{
+	struct i40e_adapter *adapter =
+			(struct i40e_adapter *)dev->data->dev_private;
+
+	adapter->tc.nsec += delta;
+
+	return 0;
+}
+
+static int
+i40e_timesync_time_set(struct rte_eth_dev *dev, struct timespec *ts)
+{
+	uint64_t ns;
+
+	ns = timespec_to_ns(ts);
+
+	/* Reset the timecounter. */
+	timecounter_init(dev, ns);
+
+	return 0;
+}
+
+static int
+i40e_timesync_time_get(struct rte_eth_dev *dev, struct timespec *ts)
+{
+	uint64_t ns;
+
+	ns = timecounter_read(dev);
+	*ts = ns_to_timespec(ns);
+
+	return 0;
+}
+
+static int
+i40e_timesync_enable(struct rte_eth_dev *dev)
+{
+	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	uint32_t tsync_ctl_l;
+	uint32_t tsync_ctl_h;
+	uint64_t ns;
+	struct timespec zerotime = {0, 0};
+
+	/* Set 0.0 epoch time to initialize timecounter. */
+	ns = timespec_to_ns(&zerotime);
+	i40e_start_cyclecounter(dev);
+	timecounter_init(dev, ns);
+
 	/* Clear timesync registers. */
 	I40E_READ_REG(hw, I40E_PRTTSYN_STAT_0);
 	I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_H);
@@ -7775,10 +7924,6 @@ i40e_timesync_enable(struct rte_eth_dev *dev)
 	I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_L(3));
 	I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_H);
 
-	/* Set the timesync increment value. */
-	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_L, tsync_inc_l);
-	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_H, tsync_inc_h);
-
 	/* Enable timestamping of PTP packets. */
 	tsync_ctl_l = I40E_READ_REG(hw, I40E_PRTTSYN_CTL0);
 	tsync_ctl_l |= I40E_PRTTSYN_TSYNENA;
@@ -7810,7 +7955,7 @@ i40e_timesync_disable(struct rte_eth_dev *dev)
 	I40E_WRITE_REG(hw, I40E_PRTTSYN_CTL0, tsync_ctl_l);
 	I40E_WRITE_REG(hw, I40E_PRTTSYN_CTL1, tsync_ctl_h);
 
-	/* Set the timesync increment value. */
+	/* Reset the timesync increment value. */
 	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_L, 0x0);
 	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_H, 0x0);
 
@@ -7822,10 +7967,14 @@ i40e_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 				struct timespec *timestamp, uint32_t flags)
 {
 	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct i40e_adapter *adapter =
+		(struct i40e_adapter *)dev->data->dev_private;
+
 	uint32_t sync_status;
 	uint32_t rx_stmpl;
 	uint32_t rx_stmph;
 	uint32_t index = flags & 0x03;
+	uint64_t regival = 0;
 
 	sync_status = I40E_READ_REG(hw, I40E_PRTTSYN_STAT_1);
 	if ((sync_status & (1 << index)) == 0)
@@ -7833,9 +7982,11 @@ i40e_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 
 	rx_stmpl = I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_L(index));
 	rx_stmph = I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_H(index));
+	timecounter_read(dev);
 
-	timestamp->tv_sec = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
-	timestamp->tv_nsec = 0;
+	regival = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
+	regival = timecounter_cycles_to_ns_time(&adapter->tc, regival);
+	*timestamp = ns_to_timespec(regival);
 
 	return  0;
 }
@@ -7845,9 +7996,13 @@ i40e_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 				struct timespec *timestamp)
 {
 	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct i40e_adapter *adapter =
+		(struct i40e_adapter *)dev->data->dev_private;
+
 	uint32_t sync_status;
 	uint32_t tx_stmpl;
 	uint32_t tx_stmph;
+	uint64_t regival = 0;
 
 	sync_status = I40E_READ_REG(hw, I40E_PRTTSYN_STAT_0);
 	if ((sync_status & I40E_PRTTSYN_STAT_0_TXTIME_MASK) == 0)
@@ -7856,8 +8011,9 @@ i40e_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 	tx_stmpl = I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_L);
 	tx_stmph = I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_H);
 
-	timestamp->tv_sec = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
-	timestamp->tv_nsec = 0;
+	regival = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
+	regival = timecounter_cycles_to_ns_time(&adapter->tc, regival);
+	*timestamp = ns_to_timespec(regival);
 
 	return  0;
 }
diff --git a/drivers/net/i40e/i40e_ethdev.h b/drivers/net/i40e/i40e_ethdev.h
index d281935..2df5364 100644
--- a/drivers/net/i40e/i40e_ethdev.h
+++ b/drivers/net/i40e/i40e_ethdev.h
@@ -35,6 +35,7 @@
 #define _I40E_ETHDEV_H_
 
 #include <rte_eth_ctrl.h>
+#include <rte_ptp.h>
 
 #define I40E_VLAN_TAG_SIZE        4
 
@@ -521,6 +522,10 @@ struct i40e_adapter {
 	bool rx_vec_allowed;
 	bool tx_simple_allowed;
 	bool tx_vec_allowed;
+
+	/* for PTP */
+	struct cyclecounter cc;
+	struct timecounter tc;
 };
 
 int i40e_dev_switch_queues(struct i40e_pf *pf, bool on);
-- 
2.5.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v5 6/7] example: PTP client slave minimal implementation
  2015-11-05 14:05 ` [dpdk-dev] [PATCH v5 " Daniel Mrzyglod
                     ` (4 preceding siblings ...)
  2015-11-05 14:06   ` [dpdk-dev] [PATCH v5 5/7] i40e: " Daniel Mrzyglod
@ 2015-11-05 14:06   ` Daniel Mrzyglod
  2015-11-05 14:06   ` [dpdk-dev] [PATCH v5 7/7] doc: add a PTPCLIENT sample guide Daniel Mrzyglod
                     ` (2 subsequent siblings)
  8 siblings, 0 replies; 84+ messages in thread
From: Daniel Mrzyglod @ 2015-11-05 14:06 UTC (permalink / raw)
  To: dev

Add a sample application that acts as a PTP slave using the
DPDK ieee1588 functions.

Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
---
 MAINTAINERS                          |   3 +
 doc/guides/rel_notes/release_2_2.rst |   5 +
 examples/Makefile                    |   1 +
 examples/ptpclient/Makefile          |  56 +++
 examples/ptpclient/ptpclient.c       | 779 +++++++++++++++++++++++++++++++++++
 5 files changed, 844 insertions(+)
 create mode 100644 examples/ptpclient/Makefile
 create mode 100644 examples/ptpclient/ptpclient.c

diff --git a/MAINTAINERS b/MAINTAINERS
index c8be5d2..0638665 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -520,3 +520,6 @@ F: examples/tep_termination/
 F: examples/vmdq/
 F: examples/vmdq_dcb/
 F: doc/guides/sample_app_ug/vmdq_dcb_forwarding.rst
+
+M: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
+F: examples/ptpclient
diff --git a/doc/guides/rel_notes/release_2_2.rst b/doc/guides/rel_notes/release_2_2.rst
index 17b281c..1bfbdd2 100644
--- a/doc/guides/rel_notes/release_2_2.rst
+++ b/doc/guides/rel_notes/release_2_2.rst
@@ -94,6 +94,11 @@ New Features
 
 * **Added port hotplug support to xenvirt.**
 
+* **ptpclient: simple PTP slave client.**
+
+  Add a sample application that acts as a PTP slave using the
+  DPDK ieee1588 functions.
+
 
 Resolved Issues
 ---------------
diff --git a/examples/Makefile b/examples/Makefile
index b4eddbd..4672534 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -74,5 +74,6 @@ DIRS-$(CONFIG_RTE_LIBRTE_XEN_DOM0) += vhost_xen
 DIRS-y += vmdq
 DIRS-y += vmdq_dcb
 DIRS-$(CONFIG_RTE_LIBRTE_POWER) += vm_power_manager
+DIRS-$(CONFIG_RTE_LIBRTE_IEEE1588) += ptpclient
 
 include $(RTE_SDK)/mk/rte.extsubdir.mk
diff --git a/examples/ptpclient/Makefile b/examples/ptpclient/Makefile
new file mode 100644
index 0000000..b77cf71
--- /dev/null
+++ b/examples/ptpclient/Makefile
@@ -0,0 +1,56 @@
+#   BSD LICENSE
+#
+#   Copyright(c) 2015 Intel Corporation. All rights reserved.
+#   All rights reserved.
+#
+#   Redistribution and use in source and binary forms, with or without
+#   modification, are permitted provided that the following conditions
+#   are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in
+#       the documentation and/or other materials provided with the
+#       distribution.
+#     * Neither the name of Intel Corporation nor the names of its
+#       contributors may be used to endorse or promote products derived
+#       from this software without specific prior written permission.
+#
+#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+
+# Default target, can be overriddegitn by command line or environment
+RTE_TARGET ?= x86_64-native-linuxapp-gcc
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# binary name
+APP = ptpclient
+
+# all source are stored in SRCS-y
+SRCS-y := ptpclient.c
+
+CFLAGS += -O3
+CFLAGS += $(WERROR_FLAGS)
+
+# workaround for a gcc bug with noreturn attribute
+# http://gcc.gnu.org/bugzilla/show_bug.cgi?id=12603
+ifeq ($(CONFIG_RTE_TOOLCHAIN_GCC),y)
+CFLAGS_main.o += -Wno-return-type
+endif
+
+include $(RTE_SDK)/mk/rte.extapp.mk
diff --git a/examples/ptpclient/ptpclient.c b/examples/ptpclient/ptpclient.c
new file mode 100644
index 0000000..2a9698b
--- /dev/null
+++ b/examples/ptpclient/ptpclient.c
@@ -0,0 +1,779 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This Application is a simple Layer 2 PTP v2 client
+ * IT shows T1-T4 values which are used to synchronize PHC clock.
+ * if -T 1 parameter is used Linux kernel Clock is synchronized with ptp clock
+ */
+
+#include <stdint.h>
+#include <inttypes.h>
+#include <rte_eal.h>
+#include <rte_ethdev.h>
+#include <rte_cycles.h>
+#include <rte_lcore.h>
+#include <rte_mbuf.h>
+#include <rte_ip.h>
+#include <limits.h>
+#include <sys/time.h>
+#include <getopt.h>
+
+#define RX_RING_SIZE 128
+#define TX_RING_SIZE 512
+
+#define NUM_MBUFS            8191
+#define MBUF_CACHE_SIZE       250
+
+/* Values for the PTP messageType field */
+#define SYNC                  0x0
+#define DELAY_REQ             0x1
+#define PDELAY_REQ            0x2
+#define PDELAY_RESP           0x3
+#define FOLLOW_UP             0x8
+#define DELAY_RESP            0x9
+#define PDELAY_RESP_FOLLOW_UP 0xA
+#define ANNOUNCE              0xB
+#define SIGNALING             0xC
+#define MANAGEMENT            0xD
+
+#define NSEC_PER_SEC        1000000000L
+#define KERNEL_TIME_ADJUST_LIMIT  20000
+#define PTP_PROTOCOL             0x88F7
+
+struct rte_mempool *mbuf_pool;
+uint32_t ptp_enabled_port_mask;
+uint8_t ptp_enabled_port_nb;
+static uint8_t ptp_enabled_ports[RTE_MAX_ETHPORTS];
+
+static const struct rte_eth_conf port_conf_default = {
+	.rxmode = { .max_rx_pkt_len = ETHER_MAX_LEN }
+};
+
+static const struct ether_addr ether_multicast = {
+	.addr_bytes = {0x01, 0x1b, 0x19, 0x0, 0x0, 0x0}
+};
+
+/* Structs used for PTP handling */
+struct tstamp {
+	uint16_t   sec_msb;
+	uint32_t   sec_lsb;
+	uint32_t   ns;
+}  __attribute__((packed));
+
+struct clock_identity {
+	uint8_t id[8];
+};
+
+struct port_identity {
+	struct clock_identity  clock_identity;
+	uint16_t               port_number;
+}  __attribute__((packed));
+
+struct ptp_header {
+	uint8_t              msgtype;
+	uint8_t              ver;
+	uint16_t             message_length;
+	uint8_t              domain_number;
+	uint8_t              reserved1;
+	uint8_t              flag_field[2];
+	int64_t              correction;
+	uint32_t             reserved2;
+	struct port_identity source_port_identity;
+	uint16_t             sequenceId;
+	uint8_t              control;
+	int8_t               log_message_interval;
+} __attribute__((packed));
+
+struct sync_msg {
+	struct ptp_header   hdr;
+	struct tstamp       origin_tstamp;
+} __attribute__((packed));
+
+struct follow_up_msg {
+	struct ptp_header   hdr;
+	struct tstamp       precise_origin_tstamp;
+	uint8_t             suffix[0];
+} __attribute__((packed));
+
+struct delay_req_msg {
+	struct ptp_header   hdr;
+	struct tstamp       origin_tstamp;
+} __attribute__((packed));
+
+struct delay_resp_msg {
+	struct ptp_header    hdr;
+	struct tstamp        rx_tstamp;
+	struct port_identity requesting_port_identity;
+	uint8_t              suffix[0];
+} __attribute__((packed));
+
+struct ptp_message {
+	union {
+		struct ptp_header          header;
+		struct sync_msg            sync;
+		struct delay_req_msg       delay_req;
+		struct follow_up_msg       follow_up;
+		struct delay_resp_msg      delay_resp;
+	} __attribute__((packed));
+};
+
+struct ptpv2_data_slave_ordinary {
+	struct rte_mbuf *m;
+	struct timespec tstamp1;
+	struct timespec tstamp2;
+	struct timespec tstamp3;
+	struct timespec tstamp4;
+	struct clock_identity client_clock_id;
+	struct clock_identity master_clock_id;
+	struct timeval new_adj;
+	int64_t delta;
+	uint8_t portid;
+	uint8_t clock_portid;
+	uint16_t seqID_SYNC;
+	uint16_t seqID_FOLLOWUP;
+	uint8_t ptpset;
+	uint8_t kernel_time_set;
+	uint8_t current_ptp_port;
+};
+
+static struct ptpv2_data_slave_ordinary ptp_data;
+
+static inline uint64_t timespec64_to_ns(const struct timespec *ts)
+{
+	return ((uint64_t) ts->tv_sec * NSEC_PER_SEC) + ts->tv_nsec;
+}
+
+static struct timeval
+ns_to_timeval(int64_t nsec)
+{
+	struct timespec t_spec = {0, 0};
+	struct timeval t_eval = {0, 0};
+	int32_t rem;
+
+	if (nsec == 0)
+		return t_eval;
+	rem = nsec % NSEC_PER_SEC;
+	t_spec.tv_sec = nsec / NSEC_PER_SEC;
+
+	if (rem < 0) {
+		t_spec.tv_sec--;
+		rem += NSEC_PER_SEC;
+	}
+
+	t_spec.tv_nsec = rem;
+	t_eval.tv_sec = t_spec.tv_sec;
+	t_eval.tv_usec = t_spec.tv_nsec / 1000;
+
+	return t_eval;
+}
+
+/*
+ * Initializes a given port using global settings and with the RX buffers
+ * coming from the mbuf_pool passed as a parameter.
+ */
+static inline int
+port_init(uint8_t port, struct rte_mempool *mbuf_pool)
+{
+	struct rte_eth_dev_info dev_info;
+	struct rte_eth_conf port_conf = port_conf_default;
+	const uint16_t rx_rings = 1;
+	const uint16_t tx_rings = 1;
+	int retval;
+	uint16_t q;
+
+	if (port >= rte_eth_dev_count())
+		return -1;
+
+	/* Configure the Ethernet device. */
+	retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf);
+	if (retval != 0)
+		return retval;
+
+	/* Allocate and set up 1 RX queue per Ethernet port. */
+	for (q = 0; q < rx_rings; q++) {
+		retval = rte_eth_rx_queue_setup(port, q, RX_RING_SIZE,
+				rte_eth_dev_socket_id(port), NULL, mbuf_pool);
+
+		if (retval < 0)
+			return retval;
+	}
+
+	/* Allocate and set up 1 TX queue per Ethernet port. */
+	for (q = 0; q < tx_rings; q++) {
+		/* Setup txq_flags */
+		struct rte_eth_txconf *txconf;
+
+		rte_eth_dev_info_get(q, &dev_info);
+		txconf = &dev_info.default_txconf;
+		txconf->txq_flags = 0;
+
+		retval = rte_eth_tx_queue_setup(port, q, TX_RING_SIZE,
+				rte_eth_dev_socket_id(port), txconf);
+		if (retval < 0)
+			return retval;
+	}
+
+	/* Start the Ethernet port. */
+	retval = rte_eth_dev_start(port);
+	if (retval < 0)
+		return retval;
+
+	/* Enable timesync timestamping for the Ethernet device */
+	rte_eth_timesync_enable(port);
+
+	/* Enable RX in promiscuous mode for the Ethernet device. */
+	rte_eth_promiscuous_enable(port);
+
+	return 0;
+}
+
+static void
+print_clock_info(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	int64_t nsec;
+	struct timespec net_time, sys_time;
+
+	printf("Master Clock id: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
+		ptp_data->master_clock_id.id[0],
+		ptp_data->master_clock_id.id[1],
+		ptp_data->master_clock_id.id[2],
+		ptp_data->master_clock_id.id[3],
+		ptp_data->master_clock_id.id[4],
+		ptp_data->master_clock_id.id[5],
+		ptp_data->master_clock_id.id[6],
+		ptp_data->master_clock_id.id[7]);
+
+	printf("\nT2 - Slave  Clock.  %lds %ldns",
+			(ptp_data->tstamp2.tv_sec),
+			(ptp_data->tstamp2.tv_nsec));
+
+	printf("\nT1 - Master Clock.  %lds %ldns ",
+			ptp_data->tstamp1.tv_sec,
+			(ptp_data->tstamp1.tv_nsec));
+
+	printf("\nT3 - Slave  Clock.  %lds %ldns",
+			ptp_data->tstamp3.tv_sec,
+			(ptp_data->tstamp3.tv_nsec));
+
+	printf("\nT4 - Master Clock.  %lds %ldns ",
+			ptp_data->tstamp4.tv_sec,
+			(ptp_data->tstamp4.tv_nsec));
+
+	printf("\nDelta between master and slave clocks:%"PRId64"ns\n",
+			ptp_data->delta);
+
+	clock_gettime(CLOCK_REALTIME, &sys_time);
+	rte_eth_timesync_time_get(ptp_data->clock_portid,
+			&net_time);
+	time_t ts = net_time.tv_sec;
+
+	printf("\n\nComparison between Linux kernel Time and PTP:");
+
+	printf("\nCurrent PTP Time: %.24s %.9ld ns",
+			ctime(&ts), net_time.tv_nsec);
+
+	nsec = (int64_t)timespec64_to_ns(&net_time) -
+			(int64_t)timespec64_to_ns(&sys_time);
+	ptp_data->new_adj = ns_to_timeval(nsec);
+
+	gettimeofday(&ptp_data->new_adj, NULL);
+	time_t tp = ptp_data->new_adj.tv_sec;
+
+	printf("\nCurrent SYS Time: %.24s %.6ld ns",
+				ctime(&tp), ptp_data->new_adj.tv_usec);
+
+	printf("\nDelta between PTP and Linux Kernel time:%"PRId64"ns\n",
+				nsec);
+	printf("[Ctrl+C to quit]\n");
+
+	/* Clear screen and put cursor in column 1, row 1 */
+	printf("\033[2J\033[1;1H");
+}
+
+static int64_t
+delta_eval(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	int64_t delta;
+	uint64_t t1 = 0;
+	uint64_t t2 = 0;
+	uint64_t t3 = 0;
+	uint64_t t4 = 0;
+
+	t1 = timespec64_to_ns(&ptp_data->tstamp1);
+	t2 = timespec64_to_ns(&ptp_data->tstamp2);
+	t3 = timespec64_to_ns(&ptp_data->tstamp3);
+	t4 = timespec64_to_ns(&ptp_data->tstamp4);
+
+	/* delta = -[(T2-T1) - (T4-T3)]/2 */
+	delta = -((int64_t)((t2 - t1) - (t4 - t3))) / 2;
+
+	return delta;
+}
+
+/*
+ * Parse PTP SYNC message
+ */
+static void
+parse_sync(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	struct ptp_header *ptp_hdr;
+
+	ptp_hdr = (struct ptp_header *)(rte_pktmbuf_mtod(ptp_data->m, char *)
+			+ sizeof(struct ether_hdr));
+	ptp_data->seqID_SYNC = rte_be_to_cpu_16(ptp_hdr->sequenceId);
+
+	if (ptp_data->ptpset == 0) {
+		rte_memcpy(&ptp_data->master_clock_id,
+				&ptp_hdr->source_port_identity.clock_identity,
+				sizeof(struct clock_identity));
+		ptp_data->ptpset = 1;
+	}
+
+	if (memcmp(&ptp_hdr->source_port_identity.clock_identity,
+			&ptp_hdr->source_port_identity.clock_identity,
+			sizeof(struct clock_identity)) == 0) {
+
+		if (ptp_data->ptpset == 1)
+			rte_eth_timesync_read_rx_timestamp(ptp_data->portid,
+					&ptp_data->tstamp2, 0);
+	}
+
+}
+
+/*
+ * Parse PTP FOLLOWUP message & Send DELAY_REQ to master clock.
+ */
+static void
+parse_fup(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	struct ether_hdr *eth_hdr;
+	struct ptp_header *ptp_hdr;
+	struct clock_identity *client_clkid;
+	struct ptp_message *ptp_msg;
+	struct rte_mbuf *created_pkt;
+	struct ether_addr eth_multicast = ether_multicast;
+	size_t pkt_size;
+	int wait_us;
+	struct rte_mbuf *m = ptp_data->m;
+
+	eth_hdr = rte_pktmbuf_mtod(m, struct ether_hdr *);
+	ptp_hdr = (struct ptp_header *)(rte_pktmbuf_mtod(m, char *)
+			+ sizeof(struct ether_hdr));
+	if (memcmp(&ptp_data->master_clock_id,
+			&ptp_hdr->source_port_identity.clock_identity,
+			sizeof(struct clock_identity)) != 0)
+		return;
+
+	ptp_data->seqID_FOLLOWUP = rte_be_to_cpu_16(ptp_hdr->sequenceId);
+	ptp_msg = (struct ptp_message *) (rte_pktmbuf_mtod(m, char *) +
+					  sizeof(struct ether_hdr));
+	ptp_data->tstamp1.tv_nsec =
+		ntohl(ptp_msg->follow_up.precise_origin_tstamp.ns);
+	ptp_data->tstamp1.tv_sec =
+		((uint64_t)ntohl(ptp_msg->follow_up.precise_origin_tstamp.sec_lsb)) |
+		(((uint64_t)ntohs
+				(ptp_msg->follow_up.precise_origin_tstamp.sec_msb))
+				<< 32);
+
+	if (ptp_data->seqID_FOLLOWUP == ptp_data->seqID_SYNC) {
+
+		created_pkt = rte_pktmbuf_alloc(mbuf_pool);
+		pkt_size = sizeof(struct ether_hdr) +
+			sizeof(struct ptp_message);
+		created_pkt->data_len = pkt_size;
+		created_pkt->pkt_len = pkt_size;
+		eth_hdr = rte_pktmbuf_mtod(created_pkt, struct ether_hdr *);
+		rte_eth_macaddr_get(ptp_data->portid, &eth_hdr->s_addr);
+
+		/* Set multicast address 01-1B-19-00-00-00 */
+		ether_addr_copy(&eth_multicast, &eth_hdr->d_addr);
+
+		eth_hdr->ether_type = htons(PTP_PROTOCOL);
+		ptp_msg = (struct ptp_message *)
+			(rte_pktmbuf_mtod(created_pkt, char *) +
+			sizeof(struct ether_hdr));
+
+		ptp_msg->delay_req.hdr.sequenceId = htons(ptp_data->seqID_SYNC);
+		ptp_msg->delay_req.hdr.msgtype = DELAY_REQ;
+		ptp_msg->delay_req.hdr.ver = 2;
+		ptp_msg->delay_req.hdr.control = 1;
+		ptp_msg->delay_req.hdr.log_message_interval = 127;
+
+		/* Set up clock id */
+		client_clkid =
+			&ptp_msg->delay_req.hdr.source_port_identity.clock_identity;
+
+		client_clkid->id[0] = eth_hdr->s_addr.addr_bytes[0];
+		client_clkid->id[1] = eth_hdr->s_addr.addr_bytes[1];
+		client_clkid->id[2] = eth_hdr->s_addr.addr_bytes[2];
+		client_clkid->id[3] = 0xFF;
+		client_clkid->id[4] = 0xFE;
+		client_clkid->id[5] = eth_hdr->s_addr.addr_bytes[3];
+		client_clkid->id[6] = eth_hdr->s_addr.addr_bytes[4];
+		client_clkid->id[7] = eth_hdr->s_addr.addr_bytes[5];
+
+		rte_memcpy(&ptp_data->client_clock_id,
+				client_clkid,
+				sizeof(struct clock_identity));
+
+		/* Enable Flag for Hardware Timestamping */
+		created_pkt->ol_flags |= PKT_TX_IEEE1588_TMST;
+
+		/* We read value from NIC to prevent latching with old value */
+		rte_eth_timesync_read_tx_timestamp(ptp_data->portid,
+				&ptp_data->tstamp3);
+
+		/* the packet is being transmitted */
+		rte_eth_tx_burst(ptp_data->portid, 0, &created_pkt, 1);
+
+		wait_us = 0;
+		ptp_data->tstamp3.tv_nsec = 0;
+		ptp_data->tstamp3.tv_sec = 0;
+
+		/* we must wait at least 1us to read TX Timestamp */
+		while ((rte_eth_timesync_read_tx_timestamp(ptp_data->portid,
+				&ptp_data->tstamp3) < 0) && (wait_us < 1000)) {
+			rte_delay_us(1);
+			wait_us++;
+		}
+	}
+}
+
+/*
+ * Update the kernel time with the difference
+ * between it and the current NIC time
+ */
+static inline void
+update_kernel_time(void)
+{
+	int64_t nsec;
+	struct timespec net_time, sys_time;
+
+	clock_gettime(CLOCK_REALTIME, &sys_time);
+	rte_eth_timesync_time_get(ptp_data.current_ptp_port, &net_time);
+
+	nsec = (int64_t)timespec64_to_ns(&net_time) -
+	       (int64_t)timespec64_to_ns(&sys_time);
+
+	ptp_data.new_adj = ns_to_timeval(nsec);
+
+	/*
+	 * If difference between kernel time and system time in NIC
+	 * is too big (more than +/- 20 microseconds), use clock_settime
+	 * to set directly the kernel time, as adjtime is better for
+	 * small adjustments (takes longer to adjust the time)
+	 */
+
+	if (nsec > KERNEL_TIME_ADJUST_LIMIT || nsec < -KERNEL_TIME_ADJUST_LIMIT)
+		clock_settime(CLOCK_REALTIME, &net_time);
+	else
+		adjtime(&ptp_data.new_adj, 0);
+
+
+}
+
+/*
+ * Parse DELAY_RESP message
+ */
+static void
+parse_drsp(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	struct rte_mbuf *m = ptp_data->m;
+	struct ptp_message *ptp_msg;
+	uint16_t seqid;
+
+	ptp_msg = (struct ptp_message *) (rte_pktmbuf_mtod(m, char *) +
+					sizeof(struct ether_hdr));
+	seqid =  rte_be_to_cpu_16(ptp_msg->delay_resp.hdr.sequenceId);
+	if (memcmp(&ptp_data->client_clock_id,
+		   &ptp_msg->delay_resp.requesting_port_identity.clock_identity,
+		   sizeof(struct clock_identity)) == 0) {
+		if (seqid == ptp_data->seqID_FOLLOWUP) {
+			ptp_data->tstamp4.tv_nsec =
+					ntohl(ptp_msg->delay_resp.rx_tstamp.ns);
+			ptp_data->tstamp4.tv_sec =
+				((uint64_t)ntohl
+					(ptp_msg->delay_resp.rx_tstamp.sec_lsb)) |
+				(((uint64_t)ntohs
+					(ptp_msg->delay_resp.rx_tstamp.sec_msb))
+					<< 32);
+
+			/* Evaluate delta for adjustment */
+			ptp_data->delta = delta_eval(ptp_data);
+
+			rte_eth_timesync_time_adjust(ptp_data->portid,
+						     ptp_data->delta);
+
+			/* If enabled in app parameters, update kernel time */
+			if (ptp_data->kernel_time_set == 1)
+				update_kernel_time();
+
+			ptp_data->clock_portid = ptp_data->portid;
+			ptp_data->current_ptp_port = ptp_data->clock_portid;
+
+		}
+	}
+}
+
+/* This function processes PTP packets, implementing
+ * slave PTP IEEE1588 L2 functionality
+ */
+static void
+parse_ptp_frames(uint8_t portid, struct rte_mbuf *m) {
+	struct ptp_header *ptp_hdr;
+	struct ether_hdr *eth_hdr;
+	uint16_t eth_type;
+
+	eth_hdr = rte_pktmbuf_mtod(m, struct ether_hdr *);
+	eth_type = rte_be_to_cpu_16(eth_hdr->ether_type);
+
+	if (eth_type == PTP_PROTOCOL) {
+		ptp_data.m = m;
+		ptp_data.portid = portid;
+		ptp_hdr = (struct ptp_header *)(rte_pktmbuf_mtod(m, char *)
+					+ sizeof(struct ether_hdr));
+
+		switch (ptp_hdr->msgtype) {
+		case SYNC:
+			parse_sync(&ptp_data);
+			break;
+		case FOLLOW_UP:
+			parse_fup(&ptp_data);
+			break;
+		case DELAY_RESP:
+			parse_drsp(&ptp_data);
+			print_clock_info(&ptp_data);
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+/*
+ * The lcore main. This is the main thread that does the work, reading from
+ * an input port and writing to an output port.
+ */
+static __attribute__((noreturn)) void
+lcore_main(void)
+{
+	uint8_t portid;
+	unsigned nb_rx;
+	struct rte_mbuf *m;
+
+	/*
+	 * Check that the port is on the same NUMA node as the polling thread
+	 * for best performance.
+	 */
+	printf("\nCore %u Waiting for SYNC packets. [Ctrl+C to quit]\n",
+			rte_lcore_id());
+
+	/* Run until the application is quit or killed. */
+
+	while (1) {
+		/*
+		 * Read packet from RX queues
+		 */
+		for (portid = 0; portid < ptp_enabled_port_nb; portid++) {
+
+			portid = ptp_enabled_ports[portid];
+			nb_rx = rte_eth_rx_burst(portid, 0, &m, 1);
+
+			if (likely(nb_rx == 0))
+				continue;
+
+			if (m->ol_flags & PKT_RX_IEEE1588_PTP)
+				parse_ptp_frames(portid, m);
+
+			rte_pktmbuf_free(m);
+		}
+	}
+}
+
+static void
+print_usage(const char *prgname)
+{
+	printf("%s [EAL options] -- -p PORTMASK -T VALUE\n"
+		"  -T VALUE: 0 - Disable, 1 - Enable Linux Clock "
+		" Synchronization (0 default)\n"
+		"  -p PORTMASK: hexadecimal bitmask of ports to configure\n",
+			prgname);
+}
+
+static int
+ptp_parse_portmask(const char *portmask)
+{
+	char *end = NULL;
+	unsigned long pm;
+
+	/* parse hexadecimal string */
+	pm = strtoul(portmask, &end, 16);
+	if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
+		return -1;
+
+	if (pm == 0)
+		return -1;
+
+	return pm;
+}
+
+static int
+parse_ptp_kernel(const char *param)
+{
+	char *end = NULL;
+	unsigned long pm;
+
+	pm = strtoul(param, &end, 16);
+	if ((param[0] == '\0') || (end == NULL) || (*end != '\0'))
+		return -1;
+	if (pm == 0)
+		return 0;
+
+	return 1;
+}
+
+/* Parse the argument given in the command line of the application */
+static int
+ptp_parse_args(int argc, char **argv)
+{
+	int opt, ret;
+	char **argvopt;
+	int option_index;
+	char *prgname = argv[0];
+	static struct option lgopts[] = { {NULL, 0, 0, 0} };
+
+	argvopt = argv;
+
+	while ((opt = getopt_long(argc, argvopt, "p:T:",
+				  lgopts, &option_index)) != EOF) {
+
+		switch (opt) {
+
+		/* portmask */
+		case 'p':
+			ptp_enabled_port_mask = ptp_parse_portmask(optarg);
+			if (ptp_enabled_port_mask == 0) {
+				printf("invalid portmask\n");
+				print_usage(prgname);
+				return -1;
+			}
+			break;
+		/* time synchronization */
+		case 'T':
+			ret = parse_ptp_kernel(optarg);
+			if (ret < 0) {
+				print_usage(prgname);
+				return -1;
+			}
+
+			ptp_data.kernel_time_set = ret;
+			break;
+
+		default:
+			print_usage(prgname);
+			return -1;
+		}
+	}
+
+	argv[optind-1] = prgname;
+
+	optind = 0; /* reset getopt lib */
+	return 0;
+}
+
+/*
+ * The main function, which does initialization and calls the per-lcore
+ * functions.
+ */
+int
+main(int argc, char *argv[])
+{
+	unsigned nb_ports;
+
+	uint8_t portid;
+
+	/* Initialize the Environment Abstraction Layer (EAL). */
+	int ret = rte_eal_init(argc, argv);
+
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
+
+	memset(&ptp_data, '\0', sizeof(struct ptpv2_data_slave_ordinary));
+
+	argc -= ret;
+	argv += ret;
+
+	ret = ptp_parse_args(argc, argv);
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "Error with PTP initialization\n");
+
+	/* Check that there is an even number of ports to send/receive on. */
+	nb_ports = rte_eth_dev_count();
+
+	/* Creates a new mempool in memory to hold the mbufs. */
+	mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", NUM_MBUFS * nb_ports,
+		MBUF_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
+
+	if (mbuf_pool == NULL)
+		rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
+
+	/* Initialize all ports. */
+	for (portid = 0; portid < nb_ports; portid++) {
+		if ((ptp_enabled_port_mask & (1 << portid)) != 0) {
+			if (port_init(portid, mbuf_pool) == 0) {
+				ptp_enabled_ports[ptp_enabled_port_nb] = portid;
+				ptp_enabled_port_nb++;
+			} else {
+				rte_exit(EXIT_FAILURE,
+					 "Cannot init port %"PRIu8 "\n",
+					 portid);
+			}
+		} else
+			printf("Skipping disabled port %u\n", portid);
+	}
+
+	if (ptp_enabled_port_nb == 0) {
+		rte_exit(EXIT_FAILURE,
+			"All available ports are disabled."
+			" Please set portmask.\n");
+	}
+
+	if (rte_lcore_count() > 1)
+		printf("\nWARNING: Too many lcores enabled. Only 1 used.\n");
+
+	/* Call lcore_main on the master core only. */
+	lcore_main();
+
+	return 0;
+}
-- 
2.5.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v5 7/7] doc: add a PTPCLIENT sample guide
  2015-11-05 14:05 ` [dpdk-dev] [PATCH v5 " Daniel Mrzyglod
                     ` (5 preceding siblings ...)
  2015-11-05 14:06   ` [dpdk-dev] [PATCH v5 6/7] example: PTP client slave minimal implementation Daniel Mrzyglod
@ 2015-11-05 14:06   ` Daniel Mrzyglod
  2015-11-05 14:10   ` [dpdk-dev] [PATCH v5 0/7] add sample ptp slave application Mrzyglod, DanielX T
  2015-11-12 12:55   ` [dpdk-dev] [PATCH v6 0/8] " Pablo de Lara
  8 siblings, 0 replies; 84+ messages in thread
From: Daniel Mrzyglod @ 2015-11-05 14:06 UTC (permalink / raw)
  To: dev

It includes:
 - Add the ptpclient picture with svg format.
 - Add the ptpclient.rst file
 - Change the index.rst file for the above pictures index.

Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
---
 doc/guides/sample_app_ug/img/ptpclient.svg | 520 +++++++++++++++++++++++++++++
 doc/guides/sample_app_ug/index.rst         |   3 +
 doc/guides/sample_app_ug/ptpclient.rst     | 306 +++++++++++++++++
 3 files changed, 829 insertions(+)
 create mode 100644 doc/guides/sample_app_ug/img/ptpclient.svg
 create mode 100644 doc/guides/sample_app_ug/ptpclient.rst

diff --git a/doc/guides/sample_app_ug/img/ptpclient.svg b/doc/guides/sample_app_ug/img/ptpclient.svg
new file mode 100644
index 0000000..55c134e
--- /dev/null
+++ b/doc/guides/sample_app_ug/img/ptpclient.svg
@@ -0,0 +1,520 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="105mm"
+   height="148mm"
+   viewBox="0 0 372.04724 524.40945"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="drawing3.svg">
+  <defs
+     id="defs4">
+    <marker
+       inkscape:stockid="Arrow2Mend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow2Mend"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4256"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="scale(-0.6,-0.6)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Lstart"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow2Lstart"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4247"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="matrix(1.1,0,0,1.1,1.1,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible"
+       id="marker6746"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="Arrow2Lend">
+      <path
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         id="path6748"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker6526"
+       style="overflow:visible"
+       inkscape:isstock="true"
+       inkscape:collect="always">
+      <path
+         id="path6528"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker6276"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path6278"
+         d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(-0.8,0,0,-0.8,-10,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible"
+       id="marker6128"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="DotM">
+      <path
+         transform="matrix(0.4,0,0,0.4,2.96,0.4)"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
+         id="path6130"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible"
+       id="marker6022"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="Arrow2Lend"
+       inkscape:collect="always">
+      <path
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         id="path6024"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible"
+       id="marker5922"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="DotM">
+      <path
+         transform="matrix(0.4,0,0,0.4,2.96,0.4)"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
+         id="path5924"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow2Lend"
+       style="overflow:visible"
+       inkscape:isstock="true"
+       inkscape:collect="always">
+      <path
+         id="path4250"
+         style="fill:#0000ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="DotM"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker5676"
+       style="overflow:visible"
+       inkscape:isstock="true"
+       inkscape:collect="always">
+      <path
+         id="path5678"
+         d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
+         style="fill:#0000ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(0.4,0,0,0.4,2.96,0.4)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lstart"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker5600"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path5602"
+         d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(0.8,0,0,0.8,10,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible"
+       id="marker5510"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="DotM"
+       inkscape:collect="always">
+      <path
+         transform="matrix(0.4,0,0,0.4,2.96,0.4)"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
+         id="path5512"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker4651"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4653"
+         d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(-0.8,0,0,-0.8,-10,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker4605"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4607"
+         d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(-0.8,0,0,-0.8,-10,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow1Lend"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4232"
+         d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(-0.8,0,0,-0.8,-10,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="DotM"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="DotM"
+       style="overflow:visible"
+       inkscape:isstock="true"
+       inkscape:collect="always">
+      <path
+         id="path4293"
+         d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(0.4,0,0,0.4,2.96,0.4)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lstart"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow1Lstart"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4229"
+         d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(0.8,0,0,0.8,10,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3402"
+       is_visible="true" />
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3398"
+       is_visible="true" />
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3394"
+       is_visible="true" />
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3378"
+       is_visible="true" />
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3374"
+       is_visible="true" />
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3370"
+       is_visible="true" />
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3366"
+       is_visible="true" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1.06"
+     inkscape:cx="280.7353"
+     inkscape:cy="266.181"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     showguides="false"
+     inkscape:window-width="1920"
+     inkscape:window-height="1053"
+     inkscape:window-x="1920"
+     inkscape:window-y="103"
+     inkscape:window-maximized="0" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-527.95276)">
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.44085264;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="m 64.094156,600.99321 c 0,60.57637 0,393.74649 0,393.74649"
+       id="path3388"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.63210523;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="m 291.64401,600.59617 0,394.60083"
+       id="path3390"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.28789771;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#DotM);marker-end:url(#marker6746)"
+       d="M 64.094156,623.68474 289.7829,665.38811"
+       id="path4223"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.28789771;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#marker5510);marker-end:url(#marker6526)"
+       d="M 63.199944,683.08006 290.44233,725.29448"
+       id="path5274"
+       inkscape:connector-curvature="0" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="295.22058"
+       y="665.10974"
+       id="text5424"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan5426"
+         x="295.22058"
+         y="665.10974">T2</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#800080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="242.44649"
+       y="657.08087"
+       id="text5428"
+       sodipodi:linespacing="100%"
+       transform="matrix(0.98289795,0.18415108,-0.18415108,0.98289795,0,0)"><tspan
+         sodipodi:role="line"
+         id="tspan5430"
+         x="242.44649"
+         y="657.08087">FOLLOW UP:T1</tspan></text>
+    <path
+       style="fill:#0000ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.28789771;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#marker5676);marker-end:url(#Arrow2Lend)"
+       d="M 291.5001,774.36878 64.400801,816.4401"
+       id="path5586"
+       inkscape:connector-curvature="0" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="-44.967201"
+       y="809.64362"
+       id="text5898"
+       sodipodi:linespacing="100%"
+       transform="matrix(0.98291896,-0.18403889,0.18403889,0.98291896,0,0)"><tspan
+         sodipodi:role="line"
+         id="tspan5900"
+         x="-44.967201"
+         y="809.64362">DELAY REQUEST</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="297.08099"
+       y="774.7981"
+       id="text5902"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan5904"
+         x="297.08099"
+         y="774.7981">T3</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#800080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="35.20845"
+       y="817.29871"
+       id="text5906"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan5908"
+         x="35.20845"
+         y="817.29871">T4</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#800080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="33.920555"
+       y="623.68475"
+       id="text5910"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan5912"
+         x="33.920555"
+         y="623.68475">T1</tspan></text>
+    <path
+       style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.28789771;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#marker6128);marker-end:url(#marker6022)"
+       d="M 63.971502,838.62056 290.6415,881.55049"
+       id="path5914"
+       inkscape:connector-curvature="0" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#800080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="223.59686"
+       y="811.73932"
+       id="text6858"
+       sodipodi:linespacing="100%"
+       transform="matrix(0.98367106,0.17997568,-0.17997568,0.98367106,0,0)"><tspan
+         sodipodi:role="line"
+         id="tspan6860"
+         x="223.59686"
+         y="811.73932">DELAY RESPONSE:T4</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="813.40204"
+       y="-298.41309"
+       id="text6862"
+       sodipodi:linespacing="100%"
+       transform="matrix(0.0147127,0.99989176,-0.99989176,0.0147127,0,0)"><tspan
+         sodipodi:role="line"
+         id="tspan6864"
+         x="813.40204"
+         y="-298.41309">time</tspan></text>
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2.1464963;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow2Mend)"
+       d="m 316.97185,863.23372 0,66.11208"
+       id="path6866"
+       inkscape:connector-curvature="0" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.42257881px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#800080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="38.764343"
+       y="590.47479"
+       id="text7094"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan7096"
+         x="38.764343"
+         y="590.47479">master</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:47.51625061px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="571.69812"
+       y="61.796165"
+       id="text7098"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan7100"
+         x="571.69812"
+         y="61.796165" /></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.42257881px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="271.23392"
+       y="593.71478"
+       id="text7102"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan7104"
+         x="271.23392"
+         y="593.71478">slave</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.3917141px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#800080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="244.40062"
+       y="602.10443"
+       id="text4317"
+       sodipodi:linespacing="125%"
+       transform="matrix(0.98605316,0.16643065,-0.16643065,0.98605316,0,0)"><tspan
+         sodipodi:role="line"
+         id="tspan4319"
+         x="244.40062"
+         y="602.10443">SYNC</tspan></text>
+  </g>
+</svg>
diff --git a/doc/guides/sample_app_ug/index.rst b/doc/guides/sample_app_ug/index.rst
index 9beedd9..8ae86c0 100644
--- a/doc/guides/sample_app_ug/index.rst
+++ b/doc/guides/sample_app_ug/index.rst
@@ -73,6 +73,7 @@ Sample Applications User Guide
     vm_power_management
     tep_termination
     proc_info
+    ptpclient
 
 **Figures**
 
@@ -136,6 +137,8 @@ Sample Applications User Guide
 :numref:`figure_overlay_networking` :ref:`figure_overlay_networking`
 :numref:`figure_tep_termination_arch` :ref:`figure_tep_termination_arch`
 
+:numref:`figure_ptpclient_highlevel` :ref:`figure_ptpclient_highlevel`
+
 **Tables**
 
 :numref:`table_qos_metering_1` :ref:`table_qos_metering_1`
diff --git a/doc/guides/sample_app_ug/ptpclient.rst b/doc/guides/sample_app_ug/ptpclient.rst
new file mode 100644
index 0000000..6e425b7
--- /dev/null
+++ b/doc/guides/sample_app_ug/ptpclient.rst
@@ -0,0 +1,306 @@
+..  BSD LICENSE
+    Copyright(c) 2015 Intel Corporation. All rights reserved.
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions
+    are met:
+
+    * Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.
+    * Neither the name of Intel Corporation nor the names of its
+    contributors may be used to endorse or promote products derived
+    from this software without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+    OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+PTP Client Sample Application
+=============================
+
+The PTP (Precision Time Protocol) client sample application is a simple
+example of using the DPDK IEEE1588 API to communicate with a PTP master clock
+to synchronize the time on the NIC and, optionally, on the Linux system.
+
+Note, PTP is a time syncing protocol and cannot be used within DPDK as a
+time-stamping mechanism. See the following for an explanation of the protocol:
+`Precision Time Protocol
+<https://en.wikipedia.org/wiki/Precision_Time_Protocol>`_.
+
+
+Limitations
+-----------
+
+The PTP sample application is intended as a simple reference implementation of
+a PTP client using the DPDK IEEE1588 API.
+In order to keep the application simple the following assumptions are made:
+
+* The first discovered master is the master for the session.
+* Only L2 PTP packets are supported.
+* Only the PTP v2 protocol is supported.
+* Only the slave clock is implemented.
+
+
+How the Application Works
+-------------------------
+
+.. _figure_ptpclient_highlevel:
+
+.. figure:: img/ptpclient.*
+
+   PTP Synchronization Protocol
+
+The PTP synchronization in the sample application works as follows:
+
+* Master sends *Sync* message - the slave saves it as T2.
+* Master sends *Follow Up* message and sends time of T1.
+* Slave sends *Delay Request* frame to PTP Master and stores T3.
+* Master sends *Delay Response* T4 time which is time of received T3.
+
+The adjustment for slave can be represented as:
+
+   adj = -[(T2-T1)-(T4 - T3)]/2
+
+If the command line parameter ``-T 1`` is used the application also
+synchronizes the PTP PHC clock with the Linux kernel clock.
+
+
+Compiling the Application
+-------------------------
+
+To compile the application, export the path to the DPDK source tree and edit
+the ``config/common_linuxapp`` configuration file to enable IEEE1588:
+
+.. code-block:: console
+
+    export RTE_SDK=/path/to/rte_sdk
+
+    # Edit  common_linuxapp and set the following options:
+    CONFIG_RTE_LIBRTE_IEEE1588=y
+
+Set the target, for example:
+
+.. code-block:: console
+
+    export RTE_TARGET=x86_64-native-linuxapp-gcc
+
+See the *DPDK Getting Started* Guide for possible ``RTE_TARGET`` values.
+
+Build the application as follows:
+
+.. code-block:: console
+
+    # Recompile DPDK.
+    make install T=$RTE_TARGET
+
+    # Compile the application.
+    cd ${RTE_SDK}/examples/ptpclient
+    make
+
+
+Running the Application
+-----------------------
+
+To run the example in a ``linuxapp`` environment:
+
+.. code-block:: console
+
+    ./build/ptpclient -c 2 -n 4 -- -p 0x1 -T 0
+
+Refer to *DPDK Getting Started Guide* for general information on running
+applications and the Environment Abstraction Layer (EAL) options.
+
+* ``-p portmask``: Hexadecimal portmask.
+* ``-T 0``: Update only the PTP slave clock.
+* ``-T 1``: Update the PTP slave clock and synchronize the Linux Kernel to the PTP clock.
+
+
+Code Explanation
+----------------
+
+The following sections provide an explanation of the main components of the
+code.
+
+All DPDK library functions used in the sample code are prefixed with ``rte_``
+and are explained in detail in the *DPDK API Documentation*.
+
+
+The Main Function
+~~~~~~~~~~~~~~~~~
+
+The ``main()`` function performs the initialization and calls the execution
+threads for each lcore.
+
+The first task is to initialize the Environment Abstraction Layer (EAL).  The
+``argc`` and ``argv`` arguments are provided to the ``rte_eal_init()``
+function. The value returned is the number of parsed arguments:
+
+.. code-block:: c
+
+    int ret = rte_eal_init(argc, argv);
+    if (ret < 0)
+        rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
+
+And than we parse application specific arguments
+
+.. code-block:: c
+
+    argc -= ret;
+    argv += ret;
+
+    ret = ptp_parse_args(argc, argv);
+    if (ret < 0)
+        rte_exit(EXIT_FAILURE, "Error with PTP initialization\n");
+
+The ``main()`` also allocates a mempool to hold the mbufs (Message Buffers)
+used by the application:
+
+.. code-block:: c
+
+    mbuf_pool = rte_mempool_create("MBUF_POOL",
+                                   NUM_MBUFS * nb_ports,
+                                   MBUF_SIZE,
+                                   MBUF_CACHE_SIZE,
+                                   sizeof(struct rte_pktmbuf_pool_private),
+                                   rte_pktmbuf_pool_init, NULL,
+                                   rte_pktmbuf_init,      NULL,
+                                   rte_socket_id(),
+                                   0);
+
+Mbufs are the packet buffer structure used by DPDK. They are explained in
+detail in the "Mbuf Library" section of the *DPDK Programmer's Guide*.
+
+The ``main()`` function also initializes all the ports using the user defined
+``port_init()`` function with portmask provided by user:
+
+.. code-block:: c
+
+    for (portid = 0; portid < nb_ports; portid++)
+        if ((ptp_enabled_port_mask & (1 << portid)) != 0) {
+
+            if (port_init(portid, mbuf_pool) == 0) {
+                ptp_enabled_ports[ptp_enabled_port_nb] = portid;
+                ptp_enabled_port_nb++;
+            } else {
+                rte_exit(EXIT_FAILURE, "Cannot init port %"PRIu8 "\n",
+                        portid);
+            }
+        }
+
+
+Once the initialization is complete, the application is ready to launch a
+function on an lcore. In this example ``lcore_main()`` is called on a single
+lcore.
+
+.. code-block:: c
+
+	lcore_main();
+
+The ``lcore_main()`` function is explained below.
+
+
+The Lcores Main
+~~~~~~~~~~~~~~~
+
+As we saw above the ``main()`` function calls an application function on the
+available lcores.
+
+The main work of the application is done within the loop:
+
+.. code-block:: c
+
+        for (portid = 0; portid < ptp_enabled_port_nb; portid++) {
+
+            portid = ptp_enabled_ports[portid];
+            nb_rx = rte_eth_rx_burst(portid, 0, &m, 1);
+
+            if (likely(nb_rx == 0))
+                continue;
+
+            if (m->ol_flags & PKT_RX_IEEE1588_PTP)
+                parse_ptp_frames(portid, m);
+
+            rte_pktmbuf_free(m);
+        }
+
+Packets are received one by one on the RX ports and, if required, PTP response
+packets are transmitted on the TX ports.
+
+If the offload flags in the mbuf indicate that the packet is a PTP packet then
+the packet is parsed to determine which type:
+
+.. code-block:: c
+
+            if (m->ol_flags & PKT_RX_IEEE1588_PTP)
+                 parse_ptp_frames(portid, m);
+
+
+All packets are freed explicitly using ``rte_pktmbuf_free()``.
+
+The forwarding loop can be interrupted and the application closed using
+``Ctrl-C``.
+
+
+PTP parsing
+~~~~~~~~~~~
+
+The ``parse_ptp_frames()`` function processes PTP packets, implementing slave
+PTP IEEE1588 L2 functionality.
+
+.. code-block:: c
+
+    void
+    parse_ptp_frames(uint8_t portid, struct rte_mbuf *m) {
+        struct ptp_header *ptp_hdr;
+        struct ether_hdr *eth_hdr;
+        uint16_t eth_type;
+
+        eth_hdr = rte_pktmbuf_mtod(m, struct ether_hdr *);
+        eth_type = rte_be_to_cpu_16(eth_hdr->ether_type);
+
+        if (eth_type == PTP_PROTOCOL) {
+            ptp_data.m = m;
+            ptp_data.portid = portid;
+            ptp_hdr = (struct ptp_header *)(rte_pktmbuf_mtod(m, char *)
+                        + sizeof(struct ether_hdr));
+
+            switch (ptp_hdr->msgtype) {
+            case SYNC:
+                parse_sync(&ptp_data);
+                break;
+            case FOLLOW_UP:
+                parse_fup(&ptp_data);
+                break;
+            case DELAY_RESP:
+                parse_drsp(&ptp_data);
+                print_clock_info(&ptp_data);
+                break;
+            default:
+                break;
+            }
+        }
+    }
+
+There are 3 types of packets on the RX path which we must parse to create a minimal
+implementation of the PTP slave client:
+
+* SYNC packet.
+* FOLLOW UP packet
+* DELAY RESPONSE packet.
+
+When we parse the *FOLLOW UP* packet we also create and send a *DELAY_REQUEST* packet.
+Also when we parse the *DELAY RESPONSE* packet, and all conditions are met we adjust the PTP slave clock.
-- 
2.5.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [dpdk-dev] [PATCH v5 0/7] add sample ptp slave application
  2015-11-05 14:05 ` [dpdk-dev] [PATCH v5 " Daniel Mrzyglod
                     ` (6 preceding siblings ...)
  2015-11-05 14:06   ` [dpdk-dev] [PATCH v5 7/7] doc: add a PTPCLIENT sample guide Daniel Mrzyglod
@ 2015-11-05 14:10   ` Mrzyglod, DanielX T
  2015-11-05 14:30     ` Mcnamara, John
  2015-11-12 12:55   ` [dpdk-dev] [PATCH v6 0/8] " Pablo de Lara
  8 siblings, 1 reply; 84+ messages in thread
From: Mrzyglod, DanielX T @ 2015-11-05 14:10 UTC (permalink / raw)
  To: Mrzyglod, DanielX T, dev



> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Daniel Mrzyglod
> Sent: Thursday, November 05, 2015 3:06 PM
> To: dev@dpdk.org
> Subject: [dpdk-dev] [PATCH v5 0/7] add sample ptp slave application
> 
> Add a sample application that acts as a PTP slave using the DPDK IEEE1588
> functions.
> 
> Also add some additional IEEE1588 support functions to enable getting,
> setting and adjusting the device time.
> 
> V4->v5:
>  - rebase to the current master

Previous series was Acked by John McNamara:
http://dpdk.org/ml/archives/dev/2015-November/027666.html

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [dpdk-dev] [PATCH v5 0/7] add sample ptp slave application
  2015-11-05 14:10   ` [dpdk-dev] [PATCH v5 0/7] add sample ptp slave application Mrzyglod, DanielX T
@ 2015-11-05 14:30     ` Mcnamara, John
  0 siblings, 0 replies; 84+ messages in thread
From: Mcnamara, John @ 2015-11-05 14:30 UTC (permalink / raw)
  To: Mrzyglod, DanielX T, Mrzyglod, DanielX T, dev

> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Mrzyglod, DanielX T
> Sent: Thursday, November 5, 2015 2:10 PM
> To: Mrzyglod, DanielX T; dev@dpdk.org
> Subject: Re: [dpdk-dev] [PATCH v5 0/7] add sample ptp slave application


Series Acked-by: John McNamara <john.mcnamara@intel.com>

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [dpdk-dev] [PATCH v4 0/7] add sample ptp slave application
  2015-11-05 12:46   ` [dpdk-dev] [PATCH v4 0/7] add sample ptp slave application Mcnamara, John
@ 2015-11-05 15:17     ` Thomas Monjalon
  2015-11-05 16:08       ` Mrzyglod, DanielX T
  0 siblings, 1 reply; 84+ messages in thread
From: Thomas Monjalon @ 2015-11-05 15:17 UTC (permalink / raw)
  To: Mcnamara, John; +Cc: dev

2015-11-05 12:46, Mcnamara, John:
> Why is this patchset marked as "Deferred"?
> 
> http://dpdk.org/dev/patchwork/project/dpdk/list/?state=10

Because it is too late to integrate new networking features in 2.2
(except for packet framework).
It will be welcome in the 2.3 timeframe starting in December.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [dpdk-dev] [PATCH v4 0/7] add sample ptp slave application
  2015-11-05 15:17     ` Thomas Monjalon
@ 2015-11-05 16:08       ` Mrzyglod, DanielX T
  0 siblings, 0 replies; 84+ messages in thread
From: Mrzyglod, DanielX T @ 2015-11-05 16:08 UTC (permalink / raw)
  To: Thomas Monjalon, Mcnamara, John; +Cc: dev



> -----Original Message-----
> From: Thomas Monjalon [mailto:thomas.monjalon@6wind.com]
> 2015-11-05 12:46, Mcnamara, John:
> > Why is this patchset marked as "Deferred"?
> >
> > http://dpdk.org/dev/patchwork/project/dpdk/list/?state=10
> 
> Because it is too late to integrate new networking features in 2.2
> (except for packet framework).
> It will be welcome in the 2.3 timeframe starting in December.

Those patches are basically fixes and supplement  to  aee686ea17290271d62308b0f559c46e33f6364b

V1 were send on merge window.
V2 were send send at review window.
V2 were Acked but there was Konstantin's request to move common structures.
V3 was send as fast as possible.

>From my point of view it should be added to RC2.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [dpdk-dev] [PATCH v5 1/7] ethdev: add additional ieee1588 support functions
  2015-11-05 14:06   ` [dpdk-dev] [PATCH v5 1/7] ethdev: add additional ieee1588 support functions Daniel Mrzyglod
@ 2015-11-10 11:03     ` Thomas Monjalon
  2015-11-10 11:36       ` Mcnamara, John
  0 siblings, 1 reply; 84+ messages in thread
From: Thomas Monjalon @ 2015-11-10 11:03 UTC (permalink / raw)
  To: Daniel Mrzyglod; +Cc: dev

Hi,

Sorry for not having followed closer this series.
It was submitted at the last minute and got too few comments.
I'll try to fix it now to be sure it will be one of the first series
ready for the 2.3 cycle.

2015-11-05 15:06, Daniel Mrzyglod:
> --- a/doc/guides/rel_notes/release_2_2.rst
> +++ b/doc/guides/rel_notes/release_2_2.rst
> @@ -222,6 +222,9 @@ API Changes
>  
>  * The devargs union field virtual is renamed to virt for C++ compatibility.
>  
> +* Add new functions in ethdev to support IEEE1588: rte_eth_timesync_time_adjust()
> +  rte_eth_timesync_time_get(), rte_eth_timesync_time_set()

No need to add an entry in API changes for new functions.

> +/**
> + * Read the time from the timesync clock on an Ethernet device.
> + *
> + * @param port_id
> + *   The port identifier of the Ethernet device.
> + * @param time
> + *   Pointer to the timespec struct.
> + *
> + * @return
> + *   - 0: Success.
> + */
> +extern int rte_eth_timesync_time_get(uint8_t port_id,
> +	      struct timespec *time);

How is it different from rte_eth_timesync_read_rx_timestamp() and
rte_eth_timesync_read_tx_timestamp()?

Why repeating the word time? Why not rte_eth_timesync_get()?

Not related to this patch, but in rte_eth_timesync_read_rx_timestamp(),
the flags parameter breaks the API layer separation with drivers:
 * @param flags
 *   Device specific flags. Used to pass the RX timesync register index to
 *   i40e. Unused in igb/ixgbe, pass 0 instead.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [dpdk-dev] [PATCH v5 2/7] net: Add common PTP structures and functions
  2015-11-05 14:06   ` [dpdk-dev] [PATCH v5 2/7] net: Add common PTP structures and functions Daniel Mrzyglod
@ 2015-11-10 11:25     ` Thomas Monjalon
  2015-11-11 10:45       ` Mcnamara, John
  0 siblings, 1 reply; 84+ messages in thread
From: Thomas Monjalon @ 2015-11-10 11:25 UTC (permalink / raw)
  To: Daniel Mrzyglod; +Cc: dev

2015-11-05 15:06, Daniel Mrzyglod:
> This patch add common functions and structures used for PTP processing.
> 
> Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
> ---
>  lib/librte_net/Makefile  |   2 +-
>  lib/librte_net/rte_ptp.h | 105 +++++++++++++++++++++++++++++++++++++++++++++++

The library librte_net gather some structures and functions for network headers/layers
parsing.
These PTP functions looks really generic. Why not add them in EAL?
Maybe they could benefit from specific implementations in the arch/ directory.

> +/*
> + * Structure for cyclecounter IEEE1588 functionality.
> + */
> +struct cyclecounter {
> +	uint64_t (*read)(struct rte_eth_dev *dev);

This field is not used.
Please avoid using a reference to ethdev here.

> +	uint64_t mask;
> +	uint32_t shift;
> +};
> +
> +/*
> + * Structure to hold and calculate Unix epoch time.
> + */
> +struct timecounter {
> +	struct cyclecounter *cc;
> +	uint64_t cycle_last;
> +	uint64_t nsec;
> +	uint64_t mask;
> +	uint64_t frac;
> +};

This structure is not used.

It is not clear what these structures are for, and what means each field.
When adding a new field in an API, it must be commented in doxygen.

> +static inline uint64_t
> +timespec_to_ns(const struct timespec *ts)
[...]
> +static inline struct timespec
> +ns_to_timespec(uint64_t nsec)
[...]
> +static inline uint64_t
> +cyclecounter_cycles_to_ns(const struct cyclecounter *cc,
> +		      uint64_t cycles, uint64_t mask, uint64_t *frac)
[...]
> +static inline uint64_t
> +cyclecounter_cycles_to_ns_backwards(const struct cyclecounter *cc,
> +			       uint64_t cycles, uint64_t frac)

They must be prefixed with rte_ with full doxygen comments.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [dpdk-dev] [PATCH v5 1/7] ethdev: add additional ieee1588 support functions
  2015-11-10 11:03     ` Thomas Monjalon
@ 2015-11-10 11:36       ` Mcnamara, John
  2015-11-10 11:58         ` Thomas Monjalon
  0 siblings, 1 reply; 84+ messages in thread
From: Mcnamara, John @ 2015-11-10 11:36 UTC (permalink / raw)
  To: Thomas Monjalon, Mrzyglod, DanielX T; +Cc: dev



> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Thomas Monjalon
> Sent: Tuesday, November 10, 2015 11:04 AM
> To: Mrzyglod, DanielX T
> Cc: dev@dpdk.org
> Subject: Re: [dpdk-dev] [PATCH v5 1/7] ethdev: add additional ieee1588
> support functions
> 
> Hi,
> 
> Sorry for not having followed closer this series.
> It was submitted at the last minute and got too few comments.

Hi,

The V2 version was submitted on the last day of the merge window but the API was available in the V1. Comments could have been made then.



> I'll try to fix it now to be sure it will be one of the first series ready
> for the 2.3 cycle.

These comments are minor and could be fixed now.


> 
> 2015-11-05 15:06, Daniel Mrzyglod:
> > --- a/doc/guides/rel_notes/release_2_2.rst
> > +++ b/doc/guides/rel_notes/release_2_2.rst
> > @@ -222,6 +222,9 @@ API Changes
> >
> >  * The devargs union field virtual is renamed to virt for C++
> compatibility.
> >
> > +* Add new functions in ethdev to support IEEE1588:
> > +rte_eth_timesync_time_adjust()
> > +  rte_eth_timesync_time_get(), rte_eth_timesync_time_set()
> 
> No need to add an entry in API changes for new functions.

OK. We can remove it now or I can remove it in the final release note edit.


> 
> > +/**
> > + * Read the time from the timesync clock on an Ethernet device.
> > + *
> > + * @param port_id
> > + *   The port identifier of the Ethernet device.
> > + * @param time
> > + *   Pointer to the timespec struct.
> > + *
> > + * @return
> > + *   - 0: Success.
> > + */
> > +extern int rte_eth_timesync_time_get(uint8_t port_id,
> > +	      struct timespec *time);
> 
> How is it different from rte_eth_timesync_read_rx_timestamp() and
> rte_eth_timesync_read_tx_timestamp()?
> 
> Why repeating the word time? Why not rte_eth_timesync_get()?


In the context of PTP there is a difference between the time (os or NIC) and the timestamp (either in the mbuf, a register or as part of the payload). 



> Not related to this patch, but in rte_eth_timesync_read_rx_timestamp(),
> the flags parameter breaks the API layer separation with drivers:
>  * @param flags
>  *   Device specific flags. Used to pass the RX timesync register index to
>  *   i40e. Unused in igb/ixgbe, pass 0 instead.


Yes. Unfortunately i40e has 4 RX registers and this information needs to be passed down to the function is some way. I'd imagine that some other nics might need similar config. I asked for feedback on this in the 2.1 cycle from other nic maintainers but didn't get any. The kernel uses a struct hwtstamp_config that I was initially going to use but didn't.


John

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [dpdk-dev] [PATCH v5 1/7] ethdev: add additional ieee1588 support functions
  2015-11-10 11:36       ` Mcnamara, John
@ 2015-11-10 11:58         ` Thomas Monjalon
  2015-11-10 14:12           ` Mcnamara, John
  0 siblings, 1 reply; 84+ messages in thread
From: Thomas Monjalon @ 2015-11-10 11:58 UTC (permalink / raw)
  To: Mcnamara, John; +Cc: dev

2015-11-10 11:36, Mcnamara, John:
> From: Thomas Monjalon
> > I'll try to fix it now to be sure it will be one of the first series ready
> > for the 2.3 cycle.
> 
> These comments are minor and could be fixed now.

After having a closer look in the drivers change, it seems to be restricted to
the PTP functions of the Intel drivers.
So you can ask to the Intel validation team if they are OK to add it in RC2.
I think it would be a wrong idea because we need to stop moving the ethdev and
drivers code, and focus on other DPDK areas for the RC2.

> > > +extern int rte_eth_timesync_time_get(uint8_t port_id,
> > > +	      struct timespec *time);
> > 
> > How is it different from rte_eth_timesync_read_rx_timestamp() and
> > rte_eth_timesync_read_tx_timestamp()?
> > 
> > Why repeating the word time? Why not rte_eth_timesync_get()?
> 
> In the context of PTP there is a difference between the time (os or NIC) and the timestamp (either in the mbuf, a register or as part of the payload).

Do you think we can make it clear in the definition of these functions?

More wording comments:
- rte_eth_timesync_time_get
- rte_eth_timesync_read_rx_timestamp
Why is it "get" in a case and "read" in another?
Why the verb is at the end in the first and before the complement in the latter?

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [dpdk-dev] [PATCH v5 1/7] ethdev: add additional ieee1588 support functions
  2015-11-10 11:58         ` Thomas Monjalon
@ 2015-11-10 14:12           ` Mcnamara, John
  2015-11-10 14:16             ` Thomas Monjalon
  2015-11-10 15:18             ` Liu, Yong
  0 siblings, 2 replies; 84+ messages in thread
From: Mcnamara, John @ 2015-11-10 14:12 UTC (permalink / raw)
  To: Thomas Monjalon; +Cc: dev

> -----Original Message-----
> From: Thomas Monjalon [mailto:thomas.monjalon@6wind.com]
> Sent: Tuesday, November 10, 2015 11:58 AM
> To: Mcnamara, John
> Cc: Mrzyglod, DanielX T; dev@dpdk.org
> Subject: Re: [dpdk-dev] [PATCH v5 1/7] ethdev: add additional ieee1588
> support functions
> 
> 2015-11-10 11:36, Mcnamara, John:
> > From: Thomas Monjalon
> > > I'll try to fix it now to be sure it will be one of the first series
> > > ready for the 2.3 cycle.
> >
> > These comments are minor and could be fixed now.
> 
> After having a closer look in the drivers change, it seems to be
> restricted to the PTP functions of the Intel drivers.
> So you can ask to the Intel validation team if they are OK to add it in
> RC2.
> I think it would be a wrong idea because we need to stop moving the ethdev
> and drivers code, and focus on other DPDK areas for the RC2.

Hi Thomas,

Ok. I'll ask the validation team to evaluate the effect of the patches.


> 
> > > > +extern int rte_eth_timesync_time_get(uint8_t port_id,
> > > > +	      struct timespec *time);
> > >
> > > How is it different from rte_eth_timesync_read_rx_timestamp() and
> > > rte_eth_timesync_read_tx_timestamp()?
> > >
> > > Why repeating the word time? Why not rte_eth_timesync_get()?
> >
> > In the context of PTP there is a difference between the time (os or NIC)
> and the timestamp (either in the mbuf, a register or as part of the
> payload).
> 
> Do you think we can make it clear in the definition of these functions?

Yes. I think that could be made clearer. I'll fix that and the others.


> 
> More wording comments:
> - rte_eth_timesync_time_get
> - rte_eth_timesync_read_rx_timestamp
> Why is it "get" in a case and "read" in another?
> Why the verb is at the end in the first and before the complement in the
> latter?

My preference would be for verb_noun but I think noun_verb was used for consistency with the rest of the Ethdev API (although that isn't quite consistent either). And yes, read() would be more consistent with the timesync API while get() is more consistent with the rest of the Ethdev API. I think it would be best, in this case, to try maintain self-consistency and use rte_eth_timesync_read_time().


John.
-- 

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [dpdk-dev] [PATCH v5 1/7] ethdev: add additional ieee1588 support functions
  2015-11-10 14:12           ` Mcnamara, John
@ 2015-11-10 14:16             ` Thomas Monjalon
  2015-11-10 15:18             ` Liu, Yong
  1 sibling, 0 replies; 84+ messages in thread
From: Thomas Monjalon @ 2015-11-10 14:16 UTC (permalink / raw)
  To: Mcnamara, John; +Cc: dev

2015-11-10 14:12, Mcnamara, John:
> My preference would be for verb_noun but I think noun_verb was used for consistency with the rest of the Ethdev API (although that isn't quite consistent either). And yes, read() would be more consistent with the timesync API while get() is more consistent with the rest of the Ethdev API. I think it would be best, in this case, to try maintain self-consistency and use rte_eth_timesync_read_time().

Yes, self consistency seems a good option.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [dpdk-dev] [PATCH v5 1/7] ethdev: add additional ieee1588 support functions
  2015-11-10 14:12           ` Mcnamara, John
  2015-11-10 14:16             ` Thomas Monjalon
@ 2015-11-10 15:18             ` Liu, Yong
  2015-11-11  1:40               ` Cao, Waterman
  1 sibling, 1 reply; 84+ messages in thread
From: Liu, Yong @ 2015-11-10 15:18 UTC (permalink / raw)
  To: Mcnamara, John, Thomas Monjalon; +Cc: dev

Hi Thomas& John,
Some update from validation team.

> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Mcnamara, John
> Sent: Tuesday, November 10, 2015 10:12 PM
> To: Thomas Monjalon
> Cc: dev@dpdk.org
> Subject: Re: [dpdk-dev] [PATCH v5 1/7] ethdev: add additional ieee1588
> support functions
> 
> > -----Original Message-----
> > From: Thomas Monjalon [mailto:thomas.monjalon@6wind.com]
> > Sent: Tuesday, November 10, 2015 11:58 AM
> > To: Mcnamara, John
> > Cc: Mrzyglod, DanielX T; dev@dpdk.org
> > Subject: Re: [dpdk-dev] [PATCH v5 1/7] ethdev: add additional ieee1588
> > support functions
> >
> > 2015-11-10 11:36, Mcnamara, John:
> > > From: Thomas Monjalon
> > > > I'll try to fix it now to be sure it will be one of the first series
> > > > ready for the 2.3 cycle.
> > >
> > > These comments are minor and could be fixed now.
> >
> > After having a closer look in the drivers change, it seems to be
> > restricted to the PTP functions of the Intel drivers.
> > So you can ask to the Intel validation team if they are OK to add it in
> > RC2.
> > I think it would be a wrong idea because we need to stop moving the
> ethdev
> > and drivers code, and focus on other DPDK areas for the RC2.
> 
> Hi Thomas,
> 
> Ok. I'll ask the validation team to evaluate the effect of the patches.
> 
> 

For our validation team's view, in this patch set implemented some APIs which used to support Precision Time Protocol.
The sample based those APIs can work as real ptp client. We have verified it work fine with linux ptp server.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [dpdk-dev] [PATCH v5 1/7] ethdev: add additional ieee1588 support functions
  2015-11-10 15:18             ` Liu, Yong
@ 2015-11-11  1:40               ` Cao, Waterman
  0 siblings, 0 replies; 84+ messages in thread
From: Cao, Waterman @ 2015-11-11  1:40 UTC (permalink / raw)
  To: Liu, Yong, Mcnamara, John, Thomas Monjalon; +Cc: dev

Hi Thomas,

Marvin has already tested the patches with a recent HEAD release of DPDK and are ready to test them as part of RC2. As you say the changes are localized to the Intel drivers and from our review they look like low risk of breaking anything else.

Thanks
Waterman

-----Original Message-----
From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Liu, Yong
Sent: Tuesday, November 10, 2015 11:18 PM
To: Mcnamara, John; Thomas Monjalon
Cc: dev@dpdk.org
Subject: Re: [dpdk-dev] [PATCH v5 1/7] ethdev: add additional ieee1588 support functions

Hi Thomas& John,
Some update from validation team.

> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Mcnamara, John
> Sent: Tuesday, November 10, 2015 10:12 PM
> To: Thomas Monjalon
> Cc: dev@dpdk.org
> Subject: Re: [dpdk-dev] [PATCH v5 1/7] ethdev: add additional ieee1588 
> support functions
> 	
> > -----Original Message-----
> > From: Thomas Monjalon [mailto:thomas.monjalon@6wind.com]
> > Sent: Tuesday, November 10, 2015 11:58 AM
> > To: Mcnamara, John
> > Cc: Mrzyglod, DanielX T; dev@dpdk.org
> > Subject: Re: [dpdk-dev] [PATCH v5 1/7] ethdev: add additional 
> > ieee1588 support functions
> >
> > 2015-11-10 11:36, Mcnamara, John:
> > > From: Thomas Monjalon
> > > > I'll try to fix it now to be sure it will be one of the first 
> > > > series ready for the 2.3 cycle.
> > >
> > > These comments are minor and could be fixed now.
> >
> > After having a closer look in the drivers change, it seems to be 
> > restricted to the PTP functions of the Intel drivers.
> > So you can ask to the Intel validation team if they are OK to add it 
> > in RC2.
> > I think it would be a wrong idea because we need to stop moving the
> ethdev
> > and drivers code, and focus on other DPDK areas for the RC2.
> 
> Hi Thomas,
> 
> Ok. I'll ask the validation team to evaluate the effect of the patches.
> 
> 

For our validation team's view, in this patch set implemented some APIs which used to support Precision Time Protocol.
The sample based those APIs can work as real ptp client. We have verified it work fine with linux ptp server.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [dpdk-dev] [PATCH v5 2/7] net: Add common PTP structures and functions
  2015-11-10 11:25     ` Thomas Monjalon
@ 2015-11-11 10:45       ` Mcnamara, John
  2015-11-11 11:24         ` Thomas Monjalon
  0 siblings, 1 reply; 84+ messages in thread
From: Mcnamara, John @ 2015-11-11 10:45 UTC (permalink / raw)
  To: Thomas Monjalon; +Cc: dev

> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Thomas Monjalon
> Sent: Tuesday, November 10, 2015 11:26 AM
> To: Mrzyglod, DanielX T
> Cc: dev@dpdk.org
> Subject: Re: [dpdk-dev] [PATCH v5 2/7] net: Add common PTP structures and
> functions
> 
> 2015-11-05 15:06, Daniel Mrzyglod:
> > This patch add common functions and structures used for PTP processing.
> >
> > Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
> > ---
> >  lib/librte_net/Makefile  |   2 +-
> >  lib/librte_net/rte_ptp.h | 105
> > +++++++++++++++++++++++++++++++++++++++++++++++
> 
> The library librte_net gather some structures and functions for network
> headers/layers parsing.
> These PTP functions looks really generic. Why not add them in EAL?
> Maybe they could benefit from specific implementations in the arch/
> directory.

Hi Thomas,

How about the following location and new header file?

    lib/librte_eal/common/include/rte_time.h


 
> > +/*
> > + * Structure for cyclecounter IEEE1588 functionality.
> > + */
> > +struct cyclecounter {
> > +	uint64_t (*read)(struct rte_eth_dev *dev);
> 
> This field is not used.
> Please avoid using a reference to ethdev here.

Ok. We'll try rework this to be more generic.


 
> > +	uint64_t mask;
> > +	uint32_t shift;
> > +};
> > +
> > +/*
> > + * Structure to hold and calculate Unix epoch time.
> > + */
> > +struct timecounter {
> > +	struct cyclecounter *cc;
> > +	uint64_t cycle_last;
> > +	uint64_t nsec;
> > +	uint64_t mask;
> > +	uint64_t frac;
> > +};
> 
> This structure is not used.
> 
> It is not clear what these structures are for, and what means each field.
> When adding a new field in an API, it must be commented in Doxygen.


The structure is used. We will add Doxygen comments.


 
> > +static inline uint64_t
> > +timespec_to_ns(const struct timespec *ts)
> [...]
> > +static inline struct timespec
> > +ns_to_timespec(uint64_t nsec)
> [...]
> > +static inline uint64_t
> > +cyclecounter_cycles_to_ns(const struct cyclecounter *cc,
> > +		      uint64_t cycles, uint64_t mask, uint64_t *frac)
> [...]
> > +static inline uint64_t
> > +cyclecounter_cycles_to_ns_backwards(const struct cyclecounter *cc,
> > +			       uint64_t cycles, uint64_t frac)
> 
> They must be prefixed with rte_ with full doxygen comments.


We can do that. But is the intention that these function are public? We really only need them internally. It is possible that they could be used somewhere else but probably not likely. How about if we document them but mark them as @internal in Doxygen. Then if they are require by some other libs they can be made external at a later stage.

John

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [dpdk-dev] [PATCH v5 2/7] net: Add common PTP structures and functions
  2015-11-11 10:45       ` Mcnamara, John
@ 2015-11-11 11:24         ` Thomas Monjalon
  0 siblings, 0 replies; 84+ messages in thread
From: Thomas Monjalon @ 2015-11-11 11:24 UTC (permalink / raw)
  To: Mcnamara, John; +Cc: dev

2015-11-11 10:45, Mcnamara, John:
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Thomas Monjalon
> > The library librte_net gather some structures and functions for network
> > headers/layers parsing.
> > These PTP functions looks really generic. Why not add them in EAL?
> > Maybe they could benefit from specific implementations in the arch/
> > directory.
> 
> Hi Thomas,
> 
> How about the following location and new header file?
> 
>     lib/librte_eal/common/include/rte_time.h

Yes

> > > +static inline uint64_t
> > > +timespec_to_ns(const struct timespec *ts)
> > [...]
> > > +static inline struct timespec
> > > +ns_to_timespec(uint64_t nsec)
> > [...]
> > > +static inline uint64_t
> > > +cyclecounter_cycles_to_ns(const struct cyclecounter *cc,
> > > +		      uint64_t cycles, uint64_t mask, uint64_t *frac)
> > [...]
> > > +static inline uint64_t
> > > +cyclecounter_cycles_to_ns_backwards(const struct cyclecounter *cc,
> > > +			       uint64_t cycles, uint64_t frac)
> > 
> > They must be prefixed with rte_ with full doxygen comments.
> 
> We can do that. But is the intention that these function are public? We really only need them internally. It is possible that they could be used somewhere else but probably not likely. How about if we document them but mark them as @internal in Doxygen. Then if they are require by some other libs they can be made external at a later stage.

Yes they can be marked @internal with rte_ prefix.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v6 0/8] add sample ptp slave application
  2015-11-05 14:05 ` [dpdk-dev] [PATCH v5 " Daniel Mrzyglod
                     ` (7 preceding siblings ...)
  2015-11-05 14:10   ` [dpdk-dev] [PATCH v5 0/7] add sample ptp slave application Mrzyglod, DanielX T
@ 2015-11-12 12:55   ` Pablo de Lara
  2015-11-12 12:55     ` [dpdk-dev] [PATCH v6 1/8] ethdev: add additional ieee1588 support functions Pablo de Lara
                       ` (9 more replies)
  8 siblings, 10 replies; 84+ messages in thread
From: Pablo de Lara @ 2015-11-12 12:55 UTC (permalink / raw)
  To: dev


Add a sample application that acts as a PTP slave using the DPDK IEEE1588
functions.

Also add some additional IEEE1588 support functions to enable getting,
setting and adjusting the device time.

V5->v6:
 - Moved common functionality for cyclecounter and time conversions
   functions to lib/librte_eal/common/include/rte_time.h, based on mailing
   list comments.
 - Prefixed functions with rte_ and added Doxygen comments.
 - Refactored cyclecounter structs from previous version to make it more
   generic.
 - Fix ieee1588 fwd output in testpmd.

V4->v5:
 - rebase to the current master

V3->V4:
Doc:
 - Update documentation for ptpclient
 - fix: put information about ptp application in correct place

V2->V3:
PMD:
 - move common structures and functions for PTP protocol to
   librte_net/rte_ptp.h

V1->V2:
PMDs:
 - add support for e1000
 - add support for ixgbe
 - add support for i40
ethdev:
 - change function names to more proper
Doc:
 - add documentation for ptpclient
sample:
 - add kernel adjustment option
 - add portmask option to provide portmask to application


Daniel Mrzyglod (5):
  ethdev: add additional ieee1588 support functions
  eal: add common time structures and functions
  ixgbe: add additional ieee1588 support functions
  doc: add a ptpclient sample guide
  example: minimal ptp client implementation

Pablo de Lara (3):
  igb: add additional ieee1588 support functions
  i40e: add additional ieee1588 support functions
  testpmd: add nanosecond output for ieee1588 fwd

 MAINTAINERS                                |   4 +
 app/test-pmd/ieee1588fwd.c                 |   8 +-
 doc/guides/rel_notes/release_2_2.rst       |   4 +
 doc/guides/sample_app_ug/img/ptpclient.svg | 524 +++++++++++++++++++
 doc/guides/sample_app_ug/index.rst         |   3 +
 doc/guides/sample_app_ug/ptpclient.rst     | 306 +++++++++++
 drivers/net/e1000/e1000_ethdev.h           |   2 +
 drivers/net/e1000/igb_ethdev.c             | 202 +++++++-
 drivers/net/i40e/i40e_ethdev.c             | 147 +++++-
 drivers/net/i40e/i40e_ethdev.h             |   6 +-
 drivers/net/ixgbe/ixgbe_ethdev.c           | 187 ++++++-
 drivers/net/ixgbe/ixgbe_ethdev.h           |   2 +
 examples/Makefile                          |   1 +
 examples/ptpclient/Makefile                |  56 +++
 examples/ptpclient/ptpclient.c             | 780 +++++++++++++++++++++++++++++
 lib/librte_eal/common/Makefile             |   2 +-
 lib/librte_eal/common/include/rte_time.h   | 210 ++++++++
 lib/librte_ether/rte_ethdev.c              |  36 ++
 lib/librte_ether/rte_ethdev.h              |  71 +++
 lib/librte_ether/rte_ether_version.map     |   3 +
 20 files changed, 2507 insertions(+), 47 deletions(-)
 create mode 100644 doc/guides/sample_app_ug/img/ptpclient.svg
 create mode 100644 doc/guides/sample_app_ug/ptpclient.rst
 create mode 100644 examples/ptpclient/Makefile
 create mode 100644 examples/ptpclient/ptpclient.c
 create mode 100644 lib/librte_eal/common/include/rte_time.h

--
1.8.1.4

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v6 1/8] ethdev: add additional ieee1588 support functions
  2015-11-12 12:55   ` [dpdk-dev] [PATCH v6 0/8] " Pablo de Lara
@ 2015-11-12 12:55     ` Pablo de Lara
  2015-11-12 12:55     ` [dpdk-dev] [PATCH v6 2/8] eal: add common time structures and functions Pablo de Lara
                       ` (8 subsequent siblings)
  9 siblings, 0 replies; 84+ messages in thread
From: Pablo de Lara @ 2015-11-12 12:55 UTC (permalink / raw)
  To: dev

From: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>

Add additional functions to support the existing IEEE1588
functionality.

  * rte_eth_timesync_write_time():  set the device clock time.
  * rte_eth_timesync_read_time():   get the device clock time.
  * rte_eth_timesync_adjust_time(): adjust the device clock time.

Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
Reviewed-by: John McNamara <john.mcnamara@intel.com>
---
 doc/guides/rel_notes/release_2_2.rst   |  4 ++
 lib/librte_ether/rte_ethdev.c          | 36 +++++++++++++++++
 lib/librte_ether/rte_ethdev.h          | 71 ++++++++++++++++++++++++++++++++++
 lib/librte_ether/rte_ether_version.map |  3 ++
 4 files changed, 114 insertions(+)

diff --git a/doc/guides/rel_notes/release_2_2.rst b/doc/guides/rel_notes/release_2_2.rst
index 59dda59..2ef6c29 100644
--- a/doc/guides/rel_notes/release_2_2.rst
+++ b/doc/guides/rel_notes/release_2_2.rst
@@ -94,6 +94,10 @@ New Features
 
 * **Added port hotplug support to xenvirt.**
 
+* **Added API in in ethdev to support IEEE1588.**
+
+  Added functions to read and write and adjust system time in the NIC.
+
 
 Resolved Issues
 ---------------
diff --git a/lib/librte_ether/rte_ethdev.c b/lib/librte_ether/rte_ethdev.c
index e0e1dca..daca6fa 100644
--- a/lib/librte_ether/rte_ethdev.c
+++ b/lib/librte_ether/rte_ethdev.c
@@ -3193,6 +3193,42 @@ rte_eth_timesync_read_tx_timestamp(uint8_t port_id, struct timespec *timestamp)
 }
 
 int
+rte_eth_timesync_adjust_time(uint8_t port_id, int64_t delta)
+{
+	struct rte_eth_dev *dev;
+
+	VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	FUNC_PTR_OR_ERR_RET(*dev->dev_ops->timesync_adjust_time, -ENOTSUP);
+	return (*dev->dev_ops->timesync_adjust_time)(dev, delta);
+}
+
+int
+rte_eth_timesync_read_time(uint8_t port_id, struct timespec *timestamp)
+{
+	struct rte_eth_dev *dev;
+
+	VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	FUNC_PTR_OR_ERR_RET(*dev->dev_ops->timesync_read_time, -ENOTSUP);
+	return (*dev->dev_ops->timesync_read_time)(dev, timestamp);
+}
+
+int
+rte_eth_timesync_write_time(uint8_t port_id, const struct timespec *timestamp)
+{
+	struct rte_eth_dev *dev;
+
+	VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	FUNC_PTR_OR_ERR_RET(*dev->dev_ops->timesync_write_time, -ENOTSUP);
+	return (*dev->dev_ops->timesync_write_time)(dev, timestamp);
+}
+
+int
 rte_eth_dev_get_reg_length(uint8_t port_id)
 {
 	struct rte_eth_dev *dev;
diff --git a/lib/librte_ether/rte_ethdev.h b/lib/librte_ether/rte_ethdev.h
index 48a540d..b7be4b8 100644
--- a/lib/librte_ether/rte_ethdev.h
+++ b/lib/librte_ether/rte_ethdev.h
@@ -1206,6 +1206,17 @@ typedef int (*eth_timesync_read_tx_timestamp_t)(struct rte_eth_dev *dev,
 						struct timespec *timestamp);
 /**< @internal Function used to read a TX IEEE1588/802.1AS timestamp. */
 
+typedef int (*eth_timesync_adjust_time)(struct rte_eth_dev *dev, int64_t);
+/**< @internal Function used to adjust the device clock */
+
+typedef int (*eth_timesync_read_time)(struct rte_eth_dev *dev,
+				      struct timespec *timestamp);
+/**< @internal Function used to get time from the device clock. */
+
+typedef int (*eth_timesync_write_time)(struct rte_eth_dev *dev,
+				       const struct timespec *timestamp);
+/**< @internal Function used to get time from the device clock */
+
 typedef int (*eth_get_reg_length_t)(struct rte_eth_dev *dev);
 /**< @internal Retrieve device register count  */
 
@@ -1400,6 +1411,12 @@ struct eth_dev_ops {
 
 	/** Get DCB information */
 	eth_get_dcb_info get_dcb_info;
+	/** Adjust the device clock.*/
+	eth_timesync_adjust_time timesync_adjust_time;
+	/** Get the device clock time. */
+	eth_timesync_read_time timesync_read_time;
+	/** Set the device clock time. */
+	eth_timesync_write_time timesync_write_time;
 };
 
 /**
@@ -3755,6 +3772,60 @@ extern int rte_eth_timesync_read_tx_timestamp(uint8_t port_id,
 					      struct timespec *timestamp);
 
 /**
+ * Adjust the timesync clock on an Ethernet device.
+ *
+ * This is usually used in conjunction with other Ethdev timesync functions to
+ * synchronize the device time using the IEEE1588/802.1AS protocol.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param delta
+ *   The adjustment in nanoseconds.
+ *
+ * @return
+ *   - 0: Success.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -ENOTSUP: The function is not supported by the Ethernet driver.
+ */
+extern int rte_eth_timesync_adjust_time(uint8_t port_id, int64_t delta);
+
+/**
+ * Read the time from the timesync clock on an Ethernet device.
+ *
+ * This is usually used in conjunction with other Ethdev timesync functions to
+ * synchronize the device time using the IEEE1588/802.1AS protocol.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param time
+ *   Pointer to the timespec struct that holds the time.
+ *
+ * @return
+ *   - 0: Success.
+ */
+extern int rte_eth_timesync_read_time(uint8_t port_id, struct timespec *time);
+
+/**
+ * Set the time of the timesync clock on an Ethernet device.
+ *
+ * This is usually used in conjunction with other Ethdev timesync functions to
+ * synchronize the device time using the IEEE1588/802.1AS protocol.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param time
+ *   Pointer to the timespec struct that holds the time.
+ *
+ * @return
+ *   - 0: Success.
+ *   - -EINVAL: No timestamp is available.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -ENOTSUP: The function is not supported by the Ethernet driver.
+ */
+extern int rte_eth_timesync_write_time(uint8_t port_id,
+				       const struct timespec *time);
+
+/**
  * Copy pci device info to the Ethernet device data.
  *
  * @param eth_dev
diff --git a/lib/librte_ether/rte_ether_version.map b/lib/librte_ether/rte_ether_version.map
index 9149aa7..57ad1ef 100644
--- a/lib/librte_ether/rte_ether_version.map
+++ b/lib/librte_ether/rte_ether_version.map
@@ -135,5 +135,8 @@ DPDK_2.2 {
 	rte_eth_dev_get_dcb_info;
 	rte_eth_rx_queue_info_get;
 	rte_eth_tx_queue_info_get;
+	rte_eth_timesync_adjust_time;
+	rte_eth_timesync_read_time;
+	rte_eth_timesync_write_time;
 
 } DPDK_2.1;
-- 
1.8.1.4

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v6 2/8] eal: add common time structures and functions
  2015-11-12 12:55   ` [dpdk-dev] [PATCH v6 0/8] " Pablo de Lara
  2015-11-12 12:55     ` [dpdk-dev] [PATCH v6 1/8] ethdev: add additional ieee1588 support functions Pablo de Lara
@ 2015-11-12 12:55     ` Pablo de Lara
  2015-11-12 12:55     ` [dpdk-dev] [PATCH v6 3/8] ixgbe: add additional ieee1588 support functions Pablo de Lara
                       ` (7 subsequent siblings)
  9 siblings, 0 replies; 84+ messages in thread
From: Pablo de Lara @ 2015-11-12 12:55 UTC (permalink / raw)
  To: dev

From: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>

Add common functions and structures to handle time, and cycle counts
which will be used for PTP processing.

Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
Reviewed-by: John McNamara <john.mcnamara@intel.com>
---
 lib/librte_eal/common/Makefile           |   2 +-
 lib/librte_eal/common/include/rte_time.h | 210 +++++++++++++++++++++++++++++++
 2 files changed, 211 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_eal/common/include/rte_time.h

diff --git a/lib/librte_eal/common/Makefile b/lib/librte_eal/common/Makefile
index 0c43d6a..8508473 100644
--- a/lib/librte_eal/common/Makefile
+++ b/lib/librte_eal/common/Makefile
@@ -40,7 +40,7 @@ INC += rte_string_fns.h rte_version.h
 INC += rte_eal_memconfig.h rte_malloc_heap.h
 INC += rte_hexdump.h rte_devargs.h rte_dev.h
 INC += rte_pci_dev_feature_defs.h rte_pci_dev_features.h
-INC += rte_malloc.h
+INC += rte_malloc.h rte_time.h
 
 ifeq ($(CONFIG_RTE_INSECURE_FUNCTION_WARNING),y)
 INC += rte_warnings.h
diff --git a/lib/librte_eal/common/include/rte_time.h b/lib/librte_eal/common/include/rte_time.h
new file mode 100644
index 0000000..33f3038
--- /dev/null
+++ b/lib/librte_eal/common/include/rte_time.h
@@ -0,0 +1,210 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define NSEC_PER_SEC 1000000000L
+
+/**
+ * @internal
+ *
+ * Structure to hold the parameters of a running cycle counter to assist
+ * in converting cycles to nanoseconds.
+ */
+struct rte_timecounter {
+	/** Last cycle counter value read. */
+	uint64_t cycle_last;
+	/** Nanoseconds count. */
+	uint64_t nsec;
+	/** Bitmask separating nanosecond and sub-nanoseconds. */
+	uint64_t nsec_mask;
+	/** Sub-nanoseconds count. */
+	uint64_t nsec_frac;
+	/** Reads the current cycle counter value. */
+	uint64_t (*read)(void *arg);
+	/** Bitmask for two's complement subtraction of non-64 bit counters. */
+	uint64_t cc_mask;
+	/** Cycle to nanosecond divisor (power of two). */
+	uint32_t cc_shift;
+	/** Argument of read() function pointer. */
+	void *arg;
+};
+
+/**
+ * @internal
+ *
+ * Initialize the rte_timecounter structure.
+ */
+static inline void
+rte_timecounter_init(struct rte_timecounter *tc, uint64_t start_time)
+{
+	tc->cycle_last = tc->read(tc->arg);
+	tc->nsec = start_time;
+	tc->nsec_mask = (1ULL << tc->cc_shift) - 1;
+	tc->nsec_frac = 0;
+}
+
+/**
+ * @internal
+ *
+ * Converts cyclecounter cycles to nanoseconds.
+ */
+static inline uint64_t
+rte_cyclecounter_cycles_to_ns(uint64_t cycles, uint64_t *frac,
+			      uint32_t shift, uint64_t mask)
+{
+	uint64_t ns;
+
+	/* Add fractional nanoseconds. */
+	ns = cycles + *frac;
+	*frac = ns & mask;
+
+	/* Shift to get only nanoseconds. */
+	return ns >> shift;
+}
+
+/**
+ * @internal
+ *
+ * Similar to rte_cyclecounter_cycles_to_ns(), but this is used when computing
+ * a time previous to the time stored in the cycle counter.
+ */
+static inline uint64_t
+rte_cyclecounter_cycles_to_ns_previous(uint64_t cycles, uint64_t frac,
+				       uint32_t shift)
+{
+	return ((cycles - frac) >> shift);
+}
+
+/**
+ * @internal
+ *
+ * Converts cycle units into nanoseconds and adds to the previous time stored.
+ */
+static inline uint64_t
+rte_timecounter_cycles_to_ns_time(struct rte_timecounter *tc,
+				  uint64_t cycles)
+{
+	uint64_t delta;
+	uint64_t nsec = tc->nsec;
+
+	delta = (cycles - tc->cycle_last) & tc->cc_mask;
+
+	if (delta > (tc->cc_mask / 2)) {
+		/* Handle cycle counts that have wrapped around . */
+		delta = (tc->cycle_last - cycles) & tc->cc_mask;
+		nsec -= rte_cyclecounter_cycles_to_ns_previous(delta,
+							       tc->nsec_frac,
+							       tc->cc_shift);
+	} else {
+		nsec += rte_cyclecounter_cycles_to_ns(delta,
+						      &tc->nsec_frac,
+						      tc->cc_shift,
+						      tc->nsec_mask);
+	}
+
+	return nsec;
+}
+
+/**
+ * @internal
+ *
+ * Get nanoseconds since the last call to this function.
+ */
+static inline uint64_t
+rte_timecounter_read_ns_delta(struct rte_timecounter *tc)
+{
+	uint64_t cycle_now, cycle_delta;
+	uint64_t ns_offset;
+
+	/* Read cycle counter. */
+	cycle_now = tc->read(tc->arg);
+
+	/* Calculate the delta since the last call. */
+	cycle_delta = (cycle_now - tc->cycle_last) & tc->cc_mask;
+
+	/* Convert to nanoseconds. */
+	ns_offset = rte_cyclecounter_cycles_to_ns(cycle_delta, &tc->nsec_frac,
+						  tc->cc_shift, tc->nsec_mask);
+
+	/* Store current cycle counter for next call. */
+	tc->cycle_last = cycle_now;
+
+	return ns_offset;
+}
+
+/**
+ * @internal
+ *
+ * Read timecounter and update the internal nanosecond count in the structure.
+ */
+static inline uint64_t
+rte_timecounter_read(struct rte_timecounter *tc)
+{
+	uint64_t nsec;
+
+	/* Increment time by nanoseconds since last call. */
+	nsec = rte_timecounter_read_ns_delta(tc);
+	nsec += tc->nsec;
+	tc->nsec = nsec;
+
+	return nsec;
+}
+
+/**
+ * @internal
+ *
+ * Convert from timespec structure into nanosecond units.
+ */
+static inline uint64_t
+rte_timespec_to_ns(const struct timespec *ts)
+{
+	return ((uint64_t) ts->tv_sec * NSEC_PER_SEC) + ts->tv_nsec;
+}
+
+/**
+ * @internal
+ *
+ * Convert from nanosecond units into timespec structure.
+ */
+static inline struct timespec
+rte_ns_to_timespec(uint64_t nsec)
+{
+	struct timespec ts = {0, 0};
+
+	if (nsec == 0)
+		return ts;
+
+	ts.tv_sec = nsec / NSEC_PER_SEC;
+	ts.tv_nsec = nsec % NSEC_PER_SEC;
+
+	return ts;
+}
-- 
1.8.1.4

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v6 3/8] ixgbe: add additional ieee1588 support functions
  2015-11-12 12:55   ` [dpdk-dev] [PATCH v6 0/8] " Pablo de Lara
  2015-11-12 12:55     ` [dpdk-dev] [PATCH v6 1/8] ethdev: add additional ieee1588 support functions Pablo de Lara
  2015-11-12 12:55     ` [dpdk-dev] [PATCH v6 2/8] eal: add common time structures and functions Pablo de Lara
@ 2015-11-12 12:55     ` Pablo de Lara
  2015-11-12 12:55     ` [dpdk-dev] [PATCH v6 4/8] igb: " Pablo de Lara
                       ` (6 subsequent siblings)
  9 siblings, 0 replies; 84+ messages in thread
From: Pablo de Lara @ 2015-11-12 12:55 UTC (permalink / raw)
  To: dev

From: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>

Add additional functions to support the existing IEEE1588
functionality and to enable getting, setting and adjusting
the device time.

Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
Reviewed-by: John McNamara <john.mcnamara@intel.com>
---
 drivers/net/ixgbe/ixgbe_ethdev.c | 187 ++++++++++++++++++++++++++++++++++++---
 drivers/net/ixgbe/ixgbe_ethdev.h |   2 +
 2 files changed, 178 insertions(+), 11 deletions(-)

diff --git a/drivers/net/ixgbe/ixgbe_ethdev.c b/drivers/net/ixgbe/ixgbe_ethdev.c
index 0b0bbcf..91a903d 100644
--- a/drivers/net/ixgbe/ixgbe_ethdev.c
+++ b/drivers/net/ixgbe/ixgbe_ethdev.c
@@ -126,10 +126,17 @@
 #define IXGBE_HKEY_MAX_INDEX 10
 
 /* Additional timesync values. */
-#define IXGBE_TIMINCA_16NS_SHIFT 24
-#define IXGBE_TIMINCA_INCVALUE   16000000
-#define IXGBE_TIMINCA_INIT       ((0x02 << IXGBE_TIMINCA_16NS_SHIFT) \
-				  | IXGBE_TIMINCA_INCVALUE)
+#define NSEC_PER_SEC             1000000000L
+#define IXGBE_INCVAL_10GB        0x66666666
+#define IXGBE_INCVAL_1GB         0x40000000
+#define IXGBE_INCVAL_100         0x50000000
+#define IXGBE_INCVAL_SHIFT_10GB  28
+#define IXGBE_INCVAL_SHIFT_1GB   24
+#define IXGBE_INCVAL_SHIFT_100   21
+#define IXGBE_INCVAL_SHIFT_82599 7
+#define IXGBE_INCPER_SHIFT_82599 24
+
+#define IXGBE_CYCLECOUNTER_MASK   0xffffffffffffffff
 
 static int eth_ixgbe_dev_init(struct rte_eth_dev *eth_dev);
 static int eth_ixgbe_dev_uninit(struct rte_eth_dev *eth_dev);
@@ -325,6 +332,11 @@ static int ixgbe_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 					    uint32_t flags);
 static int ixgbe_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 					    struct timespec *timestamp);
+static int ixgbe_timesync_adjust_time(struct rte_eth_dev *dev, int64_t delta);
+static int ixgbe_timesync_read_time(struct rte_eth_dev *dev,
+				   struct timespec *timestamp);
+static int ixgbe_timesync_write_time(struct rte_eth_dev *dev,
+				   const struct timespec *timestamp);
 
 /*
  * Define VF Stats MACRO for Non "cleared on read" register
@@ -480,6 +492,9 @@ static const struct eth_dev_ops ixgbe_eth_dev_ops = {
 	.get_eeprom           = ixgbe_get_eeprom,
 	.set_eeprom           = ixgbe_set_eeprom,
 	.get_dcb_info         = ixgbe_dev_get_dcb_info,
+	.timesync_adjust_time = ixgbe_timesync_adjust_time,
+	.timesync_read_time   = ixgbe_timesync_read_time,
+	.timesync_write_time  = ixgbe_timesync_write_time,
 };
 
 /*
@@ -5608,20 +5623,147 @@ ixgbe_dev_set_mc_addr_list(struct rte_eth_dev *dev,
 					 ixgbe_dev_addr_list_itr, TRUE);
 }
 
+static uint64_t
+ixgbe_read_cyclecounter(void *arg)
+{
+	struct rte_eth_dev *dev = (struct rte_eth_dev *) arg;
+	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	uint64_t systime_cycles = 0;
+
+	switch (hw->mac.type) {
+	case ixgbe_mac_X550:
+		/* SYSTIMEL stores ns and SYSTIMEH stores seconds. */
+		systime_cycles = (uint64_t)IXGBE_READ_REG(hw, IXGBE_SYSTIML);
+		systime_cycles += (uint64_t)IXGBE_READ_REG(hw, IXGBE_SYSTIMH)
+				* NSEC_PER_SEC;
+		break;
+	default:
+		systime_cycles |= (uint64_t)IXGBE_READ_REG(hw, IXGBE_SYSTIML);
+		systime_cycles |= (uint64_t)IXGBE_READ_REG(hw, IXGBE_SYSTIMH)
+				<< 32;
+	}
+
+	return systime_cycles;
+}
+
+static void
+ixgbe_start_cyclecounter(struct rte_eth_dev *dev)
+{
+	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct ixgbe_adapter *adapter =
+		(struct ixgbe_adapter *)dev->data->dev_private;
+	struct rte_eth_link link;
+	uint32_t incval = 0;
+	uint32_t shift = 0;
+
+	/* Get current link speed. */
+	memset(&link, 0, sizeof(link));
+	ixgbe_dev_link_update(dev, 1);
+	rte_ixgbe_dev_atomic_read_link_status(dev, &link);
+
+	switch (link.link_speed) {
+	case ETH_LINK_SPEED_100:
+		incval = IXGBE_INCVAL_100;
+		shift = IXGBE_INCVAL_SHIFT_100;
+		break;
+	case ETH_LINK_SPEED_1000:
+		incval = IXGBE_INCVAL_1GB;
+		shift = IXGBE_INCVAL_SHIFT_1GB;
+		break;
+	case ETH_LINK_SPEED_10000:
+	default:
+		incval = IXGBE_INCVAL_10GB;
+		shift = IXGBE_INCVAL_SHIFT_10GB;
+		break;
+	}
+
+	switch (hw->mac.type) {
+	case ixgbe_mac_X550:
+		/* Independent of link speed. */
+		incval = 1;
+		/* Cycles read will be interpreted as ns. */
+		shift = 0;
+		/* Fall-through */
+	case ixgbe_mac_X540:
+		IXGBE_WRITE_REG(hw, IXGBE_TIMINCA, incval);
+		break;
+	case ixgbe_mac_82599EB:
+		incval >>= IXGBE_INCVAL_SHIFT_82599;
+		shift -= IXGBE_INCVAL_SHIFT_82599;
+		IXGBE_WRITE_REG(hw, IXGBE_TIMINCA,
+				(1 << IXGBE_INCPER_SHIFT_82599) | incval);
+		break;
+	default:
+		/* Not supported. */
+		return;
+	}
+
+	memset(&adapter->tc, 0, sizeof(struct rte_timecounter));
+	adapter->tc.read = ixgbe_read_cyclecounter;
+	adapter->tc.cc_mask = IXGBE_CYCLECOUNTER_MASK;
+	adapter->tc.cc_shift = shift;
+	adapter->tc.arg = dev;
+}
+
+static int
+ixgbe_timesync_adjust_time(struct rte_eth_dev *dev, int64_t delta)
+{
+	struct ixgbe_adapter *adapter =
+			(struct ixgbe_adapter *)dev->data->dev_private;
+
+	adapter->tc.nsec += delta;
+
+	return 0;
+}
+
+static int
+ixgbe_timesync_write_time(struct rte_eth_dev *dev, const struct timespec *ts)
+{
+	uint64_t ns;
+	struct ixgbe_adapter *adapter =
+			(struct ixgbe_adapter *)dev->data->dev_private;
+
+	ns = rte_timespec_to_ns(ts);
+	/* Reset the timecounter. */
+	rte_timecounter_init(&adapter->tc, ns);
+
+	return 0;
+}
+
+static int
+ixgbe_timesync_read_time(struct rte_eth_dev *dev, struct timespec *ts)
+{
+	uint64_t ns;
+	struct ixgbe_adapter *adapter =
+			(struct ixgbe_adapter *)dev->data->dev_private;
+
+	ns = rte_timecounter_read(&adapter->tc);
+	*ts = rte_ns_to_timespec(ns);
+
+	return 0;
+}
+
 static int
 ixgbe_timesync_enable(struct rte_eth_dev *dev)
 {
 	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct ixgbe_adapter *adapter =
+			(struct ixgbe_adapter *)dev->data->dev_private;
 	uint32_t tsync_ctl;
 	uint32_t tsauxc;
+	uint64_t ns;
+	struct timespec zerotime = {0, 0};
 
 	/* Enable system time for platforms where it isn't on by default. */
 	tsauxc = IXGBE_READ_REG(hw, IXGBE_TSAUXC);
 	tsauxc &= ~IXGBE_TSAUXC_DISABLE_SYSTIME;
-	IXGBE_WRITE_REG(hw, IXGBE_TSAUXC, tsauxc);
 
-	/* Start incrementing the register used to timestamp PTP packets. */
-	IXGBE_WRITE_REG(hw, IXGBE_TIMINCA, IXGBE_TIMINCA_INIT);
+	/* Set 0.0 epoch time to initialize timecounter. */
+	ns = rte_timespec_to_ns(&zerotime);
+	ixgbe_start_cyclecounter(dev);
+	rte_timecounter_init(&adapter->tc, ns);
+
+	IXGBE_WRITE_REG(hw, IXGBE_TSAUXC, tsauxc);
 
 	/* Enable L2 filtering of IEEE1588/802.1AS Ethernet frame types. */
 	IXGBE_WRITE_REG(hw, IXGBE_ETQF(IXGBE_ETQF_FILTER_1588),
@@ -5639,6 +5781,8 @@ ixgbe_timesync_enable(struct rte_eth_dev *dev)
 	tsync_ctl |= IXGBE_TSYNCTXCTL_ENABLED;
 	IXGBE_WRITE_REG(hw, IXGBE_TSYNCTXCTL, tsync_ctl);
 
+	IXGBE_WRITE_FLUSH(hw);
+
 	return 0;
 }
 
@@ -5673,9 +5817,13 @@ ixgbe_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 				 uint32_t flags __rte_unused)
 {
 	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct ixgbe_adapter *adapter =
+		(struct ixgbe_adapter *)dev->data->dev_private;
+
 	uint32_t tsync_rxctl;
 	uint32_t rx_stmpl;
 	uint32_t rx_stmph;
+	uint64_t regival = 0;
 
 	tsync_rxctl = IXGBE_READ_REG(hw, IXGBE_TSYNCRXCTL);
 	if ((tsync_rxctl & IXGBE_TSYNCRXCTL_VALID) == 0)
@@ -5683,9 +5831,16 @@ ixgbe_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 
 	rx_stmpl = IXGBE_READ_REG(hw, IXGBE_RXSTMPL);
 	rx_stmph = IXGBE_READ_REG(hw, IXGBE_RXSTMPH);
+	rte_timecounter_read(&adapter->tc);
+
+	if (hw->mac.type == ixgbe_mac_X550)
+		regival = (uint64_t)((uint64_t)rx_stmph * NSEC_PER_SEC
+			+ rx_stmpl);
+	else
+		regival = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
 
-	timestamp->tv_sec = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
-	timestamp->tv_nsec = 0;
+	regival = rte_timecounter_cycles_to_ns_time(&adapter->tc, regival);
+	*timestamp = rte_ns_to_timespec(regival);
 
 	return  0;
 }
@@ -5695,9 +5850,13 @@ ixgbe_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 				 struct timespec *timestamp)
 {
 	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct ixgbe_adapter *adapter =
+		(struct ixgbe_adapter *)dev->data->dev_private;
+
 	uint32_t tsync_txctl;
 	uint32_t tx_stmpl;
 	uint32_t tx_stmph;
+	uint64_t regival = 0;
 
 	tsync_txctl = IXGBE_READ_REG(hw, IXGBE_TSYNCTXCTL);
 	if ((tsync_txctl & IXGBE_TSYNCTXCTL_VALID) == 0)
@@ -5705,9 +5864,15 @@ ixgbe_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 
 	tx_stmpl = IXGBE_READ_REG(hw, IXGBE_TXSTMPL);
 	tx_stmph = IXGBE_READ_REG(hw, IXGBE_TXSTMPH);
+	rte_timecounter_read(&adapter->tc);
 
-	timestamp->tv_sec = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
-	timestamp->tv_nsec = 0;
+	if (hw->mac.type == ixgbe_mac_X550)
+		regival = (uint64_t)((uint64_t)tx_stmph * NSEC_PER_SEC
+			+ tx_stmpl);
+	else
+		regival = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
+	regival = rte_timecounter_cycles_to_ns_time(&adapter->tc, regival);
+	*timestamp = rte_ns_to_timespec(regival);
 
 	return  0;
 }
diff --git a/drivers/net/ixgbe/ixgbe_ethdev.h b/drivers/net/ixgbe/ixgbe_ethdev.h
index 1856c42..0e309a2 100644
--- a/drivers/net/ixgbe/ixgbe_ethdev.h
+++ b/drivers/net/ixgbe/ixgbe_ethdev.h
@@ -37,6 +37,7 @@
 #include "base/ixgbe_dcb_82599.h"
 #include "base/ixgbe_dcb_82598.h"
 #include "ixgbe_bypass.h"
+#include <rte_time.h>
 
 /* need update link, bit flag */
 #define IXGBE_FLAG_NEED_LINK_UPDATE (uint32_t)(1 << 0)
@@ -282,6 +283,7 @@ struct ixgbe_adapter {
 
 	bool rx_bulk_alloc_allowed;
 	bool rx_vec_allowed;
+	struct rte_timecounter tc;
 };
 
 #define IXGBE_DEV_PRIVATE_TO_HW(adapter)\
-- 
1.8.1.4

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v6 4/8] igb: add additional ieee1588 support functions
  2015-11-12 12:55   ` [dpdk-dev] [PATCH v6 0/8] " Pablo de Lara
                       ` (2 preceding siblings ...)
  2015-11-12 12:55     ` [dpdk-dev] [PATCH v6 3/8] ixgbe: add additional ieee1588 support functions Pablo de Lara
@ 2015-11-12 12:55     ` Pablo de Lara
  2015-11-12 12:55     ` [dpdk-dev] [PATCH v6 5/8] i40e: " Pablo de Lara
                       ` (5 subsequent siblings)
  9 siblings, 0 replies; 84+ messages in thread
From: Pablo de Lara @ 2015-11-12 12:55 UTC (permalink / raw)
  To: dev

Add additional functions to support the existing IEEE1588
functionality and to enable getting, setting and adjusting
the device time.

Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
Reviewed-by: John McNamara <john.mcnamara@intel.com>
---
 drivers/net/e1000/e1000_ethdev.h |   2 +
 drivers/net/e1000/igb_ethdev.c   | 202 +++++++++++++++++++++++++++++++++++++--
 2 files changed, 194 insertions(+), 10 deletions(-)

diff --git a/drivers/net/e1000/e1000_ethdev.h b/drivers/net/e1000/e1000_ethdev.h
index a667a1a..5401277 100644
--- a/drivers/net/e1000/e1000_ethdev.h
+++ b/drivers/net/e1000/e1000_ethdev.h
@@ -33,6 +33,7 @@
 
 #ifndef _E1000_ETHDEV_H_
 #define _E1000_ETHDEV_H_
+#include <rte_time.h>
 
 /* need update link, bit flag */
 #define E1000_FLAG_NEED_LINK_UPDATE (uint32_t)(1 << 0)
@@ -257,6 +258,7 @@ struct e1000_adapter {
 	struct e1000_vf_info    *vfdata;
 	struct e1000_filter_info filter;
 	bool stopped;
+	struct rte_timecounter tc;
 };
 
 #define E1000_DEV_PRIVATE(adapter) \
diff --git a/drivers/net/e1000/igb_ethdev.c b/drivers/net/e1000/igb_ethdev.c
index 2cb115c..ec2e79c 100644
--- a/drivers/net/e1000/igb_ethdev.c
+++ b/drivers/net/e1000/igb_ethdev.c
@@ -78,10 +78,11 @@
 #define IGB_8_BIT_MASK   UINT8_MAX
 
 /* Additional timesync values. */
-#define E1000_ETQF_FILTER_1588 3
-#define E1000_TIMINCA_INCVALUE 16000000
-#define E1000_TIMINCA_INIT     ((0x02 << E1000_TIMINCA_16NS_SHIFT) \
-				| E1000_TIMINCA_INCVALUE)
+#define E1000_CYCLECOUNTER_MASK      0xffffffffffffffff
+#define E1000_ETQF_FILTER_1588       3
+#define IGB_82576_TSYNC_SHIFT        16
+#define E1000_INCPERIOD_82576        (1 << E1000_TIMINCA_16NS_SHIFT)
+#define E1000_INCVALUE_82576         (16 << IGB_82576_TSYNC_SHIFT)
 #define E1000_TSAUXC_DISABLE_SYSTIME 0x80000000
 
 static int  eth_igb_configure(struct rte_eth_dev *dev);
@@ -236,6 +237,11 @@ static int igb_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 					  uint32_t flags);
 static int igb_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 					  struct timespec *timestamp);
+static int igb_timesync_adjust_time(struct rte_eth_dev *dev, int64_t delta);
+static int igb_timesync_read_time(struct rte_eth_dev *dev,
+				  struct timespec *timestamp);
+static int igb_timesync_write_time(struct rte_eth_dev *dev,
+				   const struct timespec *timestamp);
 static int eth_igb_rx_queue_intr_enable(struct rte_eth_dev *dev,
 					uint16_t queue_id);
 static int eth_igb_rx_queue_intr_disable(struct rte_eth_dev *dev,
@@ -349,6 +355,9 @@ static const struct eth_dev_ops eth_igb_ops = {
 	.get_eeprom_length    = eth_igb_get_eeprom_length,
 	.get_eeprom           = eth_igb_get_eeprom,
 	.set_eeprom           = eth_igb_set_eeprom,
+	.timesync_adjust_time = igb_timesync_adjust_time,
+	.timesync_read_time   = igb_timesync_read_time,
+	.timesync_write_time  = igb_timesync_write_time,
 };
 
 /*
@@ -4182,20 +4191,151 @@ eth_igb_set_mc_addr_list(struct rte_eth_dev *dev,
 	return 0;
 }
 
+static uint64_t
+igb_read_cyclecounter(void *arg)
+{
+	struct rte_eth_dev *dev = (struct rte_eth_dev *) arg;
+	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	uint64_t systime_cycles = 0;
+
+	switch (hw->mac.type) {
+	case e1000_i210:
+	case e1000_i211:
+		/*
+		 * Need to read System Time Residue Register to be able
+		 * to read the other two registers.
+		 */
+		E1000_READ_REG(hw, E1000_SYSTIMR);
+		/* SYSTIMEL stores ns and SYSTIMEH stores seconds. */
+		systime_cycles = (uint64_t)E1000_READ_REG(hw, E1000_SYSTIML);
+		systime_cycles += (uint64_t)E1000_READ_REG(hw, E1000_SYSTIMH)
+				* NSEC_PER_SEC;
+		break;
+	case e1000_82580:
+	case e1000_i350:
+	case e1000_i354:
+		/*
+		 * Need to read System Time Residue Register to be able
+		 * to read the other two registers.
+		 */
+		E1000_READ_REG(hw, E1000_SYSTIMR);
+		systime_cycles |= (uint64_t)E1000_READ_REG(hw, E1000_SYSTIML);
+		/* Only the 8 LSB are valid. */
+		systime_cycles |= (uint64_t)(E1000_READ_REG(hw, E1000_SYSTIMH)
+				& 0xff) << 32;
+		break;
+	default:
+		systime_cycles |= (uint64_t)E1000_READ_REG(hw, E1000_SYSTIML);
+		systime_cycles |= (uint64_t)E1000_READ_REG(hw, E1000_SYSTIMH)
+				<< 32;
+		break;
+	}
+
+	return systime_cycles;
+}
+
+static void
+igb_start_cyclecounter(struct rte_eth_dev *dev)
+{
+	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct e1000_adapter *adapter =
+		(struct e1000_adapter *)dev->data->dev_private;
+	uint32_t incval = 1;
+	uint32_t shift = 0;
+	uint64_t mask = E1000_CYCLECOUNTER_MASK;
+
+	switch (hw->mac.type) {
+	case e1000_82580:
+	case e1000_i350:
+	case e1000_i354:
+		/* 32 LSB bits + 8 MSB bits = 40 bits */
+		mask = (1ULL << 40) - 1;
+		/* fall-through */
+	case e1000_i210:
+	case e1000_i211:
+		/*
+		 * Start incrementing the register
+		 * used to timestamp PTP packets.
+		 */
+		E1000_WRITE_REG(hw, E1000_TIMINCA, incval);
+		break;
+	case e1000_82576:
+		incval = E1000_INCVALUE_82576;
+		shift = IGB_82576_TSYNC_SHIFT;
+		E1000_WRITE_REG(hw, E1000_TIMINCA,
+				E1000_INCPERIOD_82576 | incval);
+		break;
+	default:
+		/* Not supported */
+		return;
+	}
+
+	memset(&adapter->tc, 0, sizeof(struct rte_timecounter));
+	adapter->tc.read = igb_read_cyclecounter;
+	adapter->tc.cc_mask = mask;
+	adapter->tc.cc_shift = shift;
+	adapter->tc.arg = dev;
+}
+
+static int
+igb_timesync_adjust_time(struct rte_eth_dev *dev, int64_t delta)
+{
+	struct e1000_adapter *adapter =
+			(struct e1000_adapter *)dev->data->dev_private;
+
+	adapter->tc.nsec += delta;
+
+	return 0;
+}
+
+static int
+igb_timesync_write_time(struct rte_eth_dev *dev, const struct timespec *ts)
+{
+	uint64_t ns;
+	struct e1000_adapter *adapter =
+			(struct e1000_adapter *)dev->data->dev_private;
+
+	ns = rte_timespec_to_ns(ts);
+
+	/* Reset the timecounter. */
+	rte_timecounter_init(&adapter->tc, ns);
+
+	return 0;
+}
+
+static int
+igb_timesync_read_time(struct rte_eth_dev *dev, struct timespec *ts)
+{
+	uint64_t ns;
+	struct e1000_adapter *adapter =
+			(struct e1000_adapter *)dev->data->dev_private;
+
+	ns = rte_timecounter_read(&adapter->tc);
+	*ts = rte_ns_to_timespec(ns);
+
+	return 0;
+}
+
 static int
 igb_timesync_enable(struct rte_eth_dev *dev)
 {
 	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct e1000_adapter *adapter =
+			(struct e1000_adapter *)dev->data->dev_private;
 	uint32_t tsync_ctl;
 	uint32_t tsauxc;
+	uint64_t ns;
+	struct timespec zerotime = {0, 0};
 
 	/* Enable system time for it isn't on by default. */
 	tsauxc = E1000_READ_REG(hw, E1000_TSAUXC);
 	tsauxc &= ~E1000_TSAUXC_DISABLE_SYSTIME;
 	E1000_WRITE_REG(hw, E1000_TSAUXC, tsauxc);
 
-	/* Start incrementing the register used to timestamp PTP packets. */
-	E1000_WRITE_REG(hw, E1000_TIMINCA, E1000_TIMINCA_INIT);
+	/* Set 0.0 epoch time to initialize timecounter. */
+	ns = rte_timespec_to_ns(&zerotime);
+	igb_start_cyclecounter(dev);
+	rte_timecounter_init(&adapter->tc, ns);
 
 	/* Enable L2 filtering of IEEE1588/802.1AS Ethernet frame types. */
 	E1000_WRITE_REG(hw, E1000_ETQF(E1000_ETQF_FILTER_1588),
@@ -4247,9 +4387,13 @@ igb_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 			       uint32_t flags __rte_unused)
 {
 	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct e1000_adapter *adapter =
+			(struct e1000_adapter *)dev->data->dev_private;
+
 	uint32_t tsync_rxctl;
 	uint32_t rx_stmpl;
 	uint32_t rx_stmph;
+	uint64_t regival = 0;
 
 	tsync_rxctl = E1000_READ_REG(hw, E1000_TSYNCRXCTL);
 	if ((tsync_rxctl & E1000_TSYNCRXCTL_VALID) == 0)
@@ -4257,9 +4401,26 @@ igb_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 
 	rx_stmpl = E1000_READ_REG(hw, E1000_RXSTMPL);
 	rx_stmph = E1000_READ_REG(hw, E1000_RXSTMPH);
+	rte_timecounter_read(&adapter->tc);
 
-	timestamp->tv_sec = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
-	timestamp->tv_nsec = 0;
+	switch (hw->mac.type) {
+	case e1000_82580:
+	case e1000_i350:
+	case e1000_i354:
+		regival = (uint64_t)((((uint64_t)(rx_stmph & 0xff)) << 32)
+			| rx_stmpl);
+		break;
+	case e1000_i210:
+	case e1000_i211:
+		regival = (uint64_t)((uint64_t)rx_stmph * NSEC_PER_SEC
+			+ rx_stmpl);
+		break;
+	default:
+		regival = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
+	}
+
+	regival = rte_timecounter_cycles_to_ns_time(&adapter->tc, regival);
+	*timestamp = rte_ns_to_timespec(regival);
 
 	return  0;
 }
@@ -4269,6 +4430,10 @@ igb_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 			       struct timespec *timestamp)
 {
 	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct e1000_adapter *adapter =
+			(struct e1000_adapter *)dev->data->dev_private;
+	uint64_t regival = 0;
+
 	uint32_t tsync_txctl;
 	uint32_t tx_stmpl;
 	uint32_t tx_stmph;
@@ -4279,9 +4444,26 @@ igb_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 
 	tx_stmpl = E1000_READ_REG(hw, E1000_TXSTMPL);
 	tx_stmph = E1000_READ_REG(hw, E1000_TXSTMPH);
+	rte_timecounter_read(&adapter->tc);
+
+	switch (hw->mac.type) {
+	case e1000_82580:
+	case e1000_i350:
+	case e1000_i354:
+		regival = (uint64_t)((((uint64_t)(tx_stmph & 0xff)) << 32)
+			| tx_stmpl);
+		break;
+	case e1000_i210:
+	case e1000_i211:
+		regival = (uint64_t)((uint64_t)tx_stmph * NSEC_PER_SEC
+			+ tx_stmpl);
+		break;
+	default:
+		regival = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
+	}
 
-	timestamp->tv_sec = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
-	timestamp->tv_nsec = 0;
+	regival = rte_timecounter_cycles_to_ns_time(&adapter->tc, regival);
+	*timestamp = rte_ns_to_timespec(regival);
 
 	return  0;
 }
-- 
1.8.1.4

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v6 5/8] i40e: add additional ieee1588 support functions
  2015-11-12 12:55   ` [dpdk-dev] [PATCH v6 0/8] " Pablo de Lara
                       ` (3 preceding siblings ...)
  2015-11-12 12:55     ` [dpdk-dev] [PATCH v6 4/8] igb: " Pablo de Lara
@ 2015-11-12 12:55     ` Pablo de Lara
  2015-11-12 12:55     ` [dpdk-dev] [PATCH v6 6/8] testpmd: add nanosecond output for ieee1588 fwd Pablo de Lara
                       ` (4 subsequent siblings)
  9 siblings, 0 replies; 84+ messages in thread
From: Pablo de Lara @ 2015-11-12 12:55 UTC (permalink / raw)
  To: dev

Add additional functions to support the existing IEEE1588
functionality and to enable getting, setting and adjusting
the device time.

Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
Reviewed-by: John McNamara <john.mcnamara@intel.com>
---
 drivers/net/i40e/i40e_ethdev.c | 147 +++++++++++++++++++++++++++++++++++------
 drivers/net/i40e/i40e_ethdev.h |   6 +-
 2 files changed, 132 insertions(+), 21 deletions(-)

diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index ddf3d38..d6b3311 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -125,11 +125,13 @@
 	(1UL << RTE_ETH_FLOW_NONFRAG_IPV6_OTHER) | \
 	(1UL << RTE_ETH_FLOW_L2_PAYLOAD))
 
-#define I40E_PTP_40GB_INCVAL  0x0199999999ULL
-#define I40E_PTP_10GB_INCVAL  0x0333333333ULL
-#define I40E_PTP_1GB_INCVAL   0x2000000000ULL
-#define I40E_PRTTSYN_TSYNENA  0x80000000
-#define I40E_PRTTSYN_TSYNTYPE 0x0e000000
+/* Additional timesync values. */
+#define I40E_PTP_40GB_INCVAL     0x0199999999ULL
+#define I40E_PTP_10GB_INCVAL     0x0333333333ULL
+#define I40E_PTP_1GB_INCVAL      0x2000000000ULL
+#define I40E_PRTTSYN_TSYNENA     0x80000000
+#define I40E_PRTTSYN_TSYNTYPE    0x0e000000
+#define I40E_CYCLECOUNTER_MASK   0xffffffffffffffff
 
 #define I40E_MAX_PERCENT            100
 #define I40E_DEFAULT_DCB_APP_NUM    1
@@ -400,11 +402,20 @@ static int i40e_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 static int i40e_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 					   struct timespec *timestamp);
 static void i40e_read_stats_registers(struct i40e_pf *pf, struct i40e_hw *hw);
+
+static int i40e_timesync_adjust_time(struct rte_eth_dev *dev, int64_t delta);
+
+static int i40e_timesync_read_time(struct rte_eth_dev *dev,
+				   struct timespec *timestamp);
+static int i40e_timesync_write_time(struct rte_eth_dev *dev,
+				    const struct timespec *timestamp);
+
 static int i40e_dev_rx_queue_intr_enable(struct rte_eth_dev *dev,
 					 uint16_t queue_id);
 static int i40e_dev_rx_queue_intr_disable(struct rte_eth_dev *dev,
 					  uint16_t queue_id);
 
+
 static const struct rte_pci_id pci_id_i40e_map[] = {
 #define RTE_PCI_DEV_ID_DECL_I40E(vend, dev) {RTE_PCI_DEVICE(vend, dev)},
 #include "rte_pci_dev_ids.h"
@@ -469,6 +480,9 @@ static const struct eth_dev_ops i40e_eth_dev_ops = {
 	.timesync_read_rx_timestamp   = i40e_timesync_read_rx_timestamp,
 	.timesync_read_tx_timestamp   = i40e_timesync_read_tx_timestamp,
 	.get_dcb_info                 = i40e_dev_get_dcb_info,
+	.timesync_adjust_time         = i40e_timesync_adjust_time,
+	.timesync_read_time           = i40e_timesync_read_time,
+	.timesync_write_time          = i40e_timesync_write_time,
 };
 
 /* store statistics names and its offset in stats structure */
@@ -7738,17 +7752,36 @@ i40e_mirror_rule_reset(struct rte_eth_dev *dev, uint8_t sw_id)
 	return 0;
 }
 
-static int
-i40e_timesync_enable(struct rte_eth_dev *dev)
+static uint64_t
+i40e_read_cyclecounter(void *arg)
 {
+	struct rte_eth_dev *dev = (struct rte_eth_dev *) arg;
 	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
-	struct rte_eth_link *link = &dev->data->dev_link;
-	uint32_t tsync_ctl_l;
-	uint32_t tsync_ctl_h;
+	uint64_t systim_cycles = 0;
+
+	systim_cycles |= (uint64_t)I40E_READ_REG(hw, I40E_PRTTSYN_TIME_L);
+	systim_cycles |= (uint64_t)I40E_READ_REG(hw, I40E_PRTTSYN_TIME_H)
+			<< 32;
+
+	return systim_cycles;
+}
+
+static void
+i40e_start_cyclecounter(struct rte_eth_dev *dev)
+{
+	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct i40e_adapter *adapter =
+			(struct i40e_adapter *)dev->data->dev_private;
+	struct rte_eth_link link;
 	uint32_t tsync_inc_l;
 	uint32_t tsync_inc_h;
 
-	switch (link->link_speed) {
+	/* Get current link speed. */
+	memset(&link, 0, sizeof(link));
+	i40e_dev_link_update(dev, 1);
+	rte_i40e_dev_atomic_read_link_status(dev, &link);
+
+	switch (link.link_speed) {
 	case ETH_LINK_SPEED_40G:
 		tsync_inc_l = I40E_PTP_40GB_INCVAL & 0xFFFFFFFF;
 		tsync_inc_h = I40E_PTP_40GB_INCVAL >> 32;
@@ -7766,6 +7799,72 @@ i40e_timesync_enable(struct rte_eth_dev *dev)
 		tsync_inc_h = 0x0;
 	}
 
+	/* Set the timesync increment value. */
+	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_L, tsync_inc_l);
+	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_H, tsync_inc_h);
+
+	memset(&adapter->tc, 0, sizeof(struct rte_timecounter));
+	adapter->tc.read = i40e_read_cyclecounter;
+	adapter->tc.cc_mask = I40E_CYCLECOUNTER_MASK;
+	adapter->tc.cc_shift = 0;
+	adapter->tc.arg = dev;
+}
+
+static int
+i40e_timesync_adjust_time(struct rte_eth_dev *dev, int64_t delta)
+{
+	struct i40e_adapter *adapter =
+			(struct i40e_adapter *)dev->data->dev_private;
+
+	adapter->tc.nsec += delta;
+
+	return 0;
+}
+
+static int
+i40e_timesync_write_time(struct rte_eth_dev *dev, const struct timespec *ts)
+{
+	uint64_t ns;
+	struct i40e_adapter *adapter =
+			(struct i40e_adapter *)dev->data->dev_private;
+
+	ns = rte_timespec_to_ns(ts);
+
+	/* Reset the timecounter. */
+	rte_timecounter_init(&adapter->tc, ns);
+
+	return 0;
+}
+
+static int
+i40e_timesync_read_time(struct rte_eth_dev *dev, struct timespec *ts)
+{
+	uint64_t ns;
+	struct i40e_adapter *adapter =
+			(struct i40e_adapter *)dev->data->dev_private;
+
+	ns = rte_timecounter_read(&adapter->tc);
+	*ts = rte_ns_to_timespec(ns);
+
+	return 0;
+}
+
+static int
+i40e_timesync_enable(struct rte_eth_dev *dev)
+{
+	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct i40e_adapter *adapter =
+			(struct i40e_adapter *)dev->data->dev_private;
+	uint32_t tsync_ctl_l;
+	uint32_t tsync_ctl_h;
+	uint64_t ns;
+	struct timespec zerotime = {0, 0};
+
+	/* Set 0.0 epoch time to initialize timecounter. */
+	ns = rte_timespec_to_ns(&zerotime);
+	i40e_start_cyclecounter(dev);
+	rte_timecounter_init(&adapter->tc, ns);
+
 	/* Clear timesync registers. */
 	I40E_READ_REG(hw, I40E_PRTTSYN_STAT_0);
 	I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_H);
@@ -7775,10 +7874,6 @@ i40e_timesync_enable(struct rte_eth_dev *dev)
 	I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_L(3));
 	I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_H);
 
-	/* Set the timesync increment value. */
-	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_L, tsync_inc_l);
-	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_H, tsync_inc_h);
-
 	/* Enable timestamping of PTP packets. */
 	tsync_ctl_l = I40E_READ_REG(hw, I40E_PRTTSYN_CTL0);
 	tsync_ctl_l |= I40E_PRTTSYN_TSYNENA;
@@ -7810,7 +7905,7 @@ i40e_timesync_disable(struct rte_eth_dev *dev)
 	I40E_WRITE_REG(hw, I40E_PRTTSYN_CTL0, tsync_ctl_l);
 	I40E_WRITE_REG(hw, I40E_PRTTSYN_CTL1, tsync_ctl_h);
 
-	/* Set the timesync increment value. */
+	/* Reset the timesync increment value. */
 	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_L, 0x0);
 	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_H, 0x0);
 
@@ -7822,10 +7917,14 @@ i40e_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 				struct timespec *timestamp, uint32_t flags)
 {
 	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct i40e_adapter *adapter =
+		(struct i40e_adapter *)dev->data->dev_private;
+
 	uint32_t sync_status;
 	uint32_t rx_stmpl;
 	uint32_t rx_stmph;
 	uint32_t index = flags & 0x03;
+	uint64_t regival = 0;
 
 	sync_status = I40E_READ_REG(hw, I40E_PRTTSYN_STAT_1);
 	if ((sync_status & (1 << index)) == 0)
@@ -7833,9 +7932,11 @@ i40e_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 
 	rx_stmpl = I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_L(index));
 	rx_stmph = I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_H(index));
+	rte_timecounter_read(&adapter->tc);
 
-	timestamp->tv_sec = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
-	timestamp->tv_nsec = 0;
+	regival = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
+	regival = rte_timecounter_cycles_to_ns_time(&adapter->tc, regival);
+	*timestamp = rte_ns_to_timespec(regival);
 
 	return  0;
 }
@@ -7845,9 +7946,13 @@ i40e_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 				struct timespec *timestamp)
 {
 	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct i40e_adapter *adapter =
+		(struct i40e_adapter *)dev->data->dev_private;
+
 	uint32_t sync_status;
 	uint32_t tx_stmpl;
 	uint32_t tx_stmph;
+	uint64_t regival = 0;
 
 	sync_status = I40E_READ_REG(hw, I40E_PRTTSYN_STAT_0);
 	if ((sync_status & I40E_PRTTSYN_STAT_0_TXTIME_MASK) == 0)
@@ -7855,9 +7960,11 @@ i40e_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 
 	tx_stmpl = I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_L);
 	tx_stmph = I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_H);
+	rte_timecounter_read(&adapter->tc);
 
-	timestamp->tv_sec = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
-	timestamp->tv_nsec = 0;
+	regival = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
+	regival = rte_timecounter_cycles_to_ns_time(&adapter->tc, regival);
+	*timestamp = rte_ns_to_timespec(regival);
 
 	return  0;
 }
diff --git a/drivers/net/i40e/i40e_ethdev.h b/drivers/net/i40e/i40e_ethdev.h
index d281935..b23d1a9 100644
--- a/drivers/net/i40e/i40e_ethdev.h
+++ b/drivers/net/i40e/i40e_ethdev.h
@@ -35,6 +35,7 @@
 #define _I40E_ETHDEV_H_
 
 #include <rte_eth_ctrl.h>
+#include <rte_time.h>
 
 #define I40E_VLAN_TAG_SIZE        4
 
@@ -516,11 +517,14 @@ struct i40e_adapter {
 		struct i40e_vf vf;
 	};
 
-	/* for vector PMD */
+	/* For vector PMD */
 	bool rx_bulk_alloc_allowed;
 	bool rx_vec_allowed;
 	bool tx_simple_allowed;
 	bool tx_vec_allowed;
+
+	/* For PTP */
+	struct rte_timecounter tc;
 };
 
 int i40e_dev_switch_queues(struct i40e_pf *pf, bool on);
-- 
1.8.1.4

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v6 6/8] testpmd: add nanosecond output for ieee1588 fwd
  2015-11-12 12:55   ` [dpdk-dev] [PATCH v6 0/8] " Pablo de Lara
                       ` (4 preceding siblings ...)
  2015-11-12 12:55     ` [dpdk-dev] [PATCH v6 5/8] i40e: " Pablo de Lara
@ 2015-11-12 12:55     ` Pablo de Lara
  2015-11-12 12:55     ` [dpdk-dev] [PATCH v6 7/8] example: minimal ptp client implementation Pablo de Lara
                       ` (3 subsequent siblings)
  9 siblings, 0 replies; 84+ messages in thread
From: Pablo de Lara @ 2015-11-12 12:55 UTC (permalink / raw)
  To: dev

Testpmd was only printing out second values when printing
RX/TX timestamp value, instead of both second and nanoseconds.
Since resolution of time counters is in nanoseconds,
testpmd should print out both.

Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
Reviewed-by: John McNamara <john.mcnamara@intel.com>
---
 app/test-pmd/ieee1588fwd.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/app/test-pmd/ieee1588fwd.c b/app/test-pmd/ieee1588fwd.c
index b1a301b..c69023a 100644
--- a/app/test-pmd/ieee1588fwd.c
+++ b/app/test-pmd/ieee1588fwd.c
@@ -89,8 +89,8 @@ port_ieee1588_rx_timestamp_check(portid_t pi, uint32_t index)
 		       (unsigned) pi);
 		return;
 	}
-	printf("Port %u RX timestamp value %lu\n",
-	       (unsigned) pi, timestamp.tv_sec);
+	printf("Port %u RX timestamp value %lu s %lu ns\n",
+	       (unsigned) pi, timestamp.tv_sec, timestamp.tv_nsec);
 }
 
 #define MAX_TX_TMST_WAIT_MICROSECS 1000 /**< 1 milli-second */
@@ -112,9 +112,9 @@ port_ieee1588_tx_timestamp_check(portid_t pi)
 		       (unsigned) pi, (unsigned) MAX_TX_TMST_WAIT_MICROSECS);
 		return;
 	}
-	printf("Port %u TX timestamp value %lu validated after "
+	printf("Port %u TX timestamp value %lu s %lu ns validated after "
 	       "%u micro-second%s\n",
-	       (unsigned) pi, timestamp.tv_sec, wait_us,
+	       (unsigned) pi, timestamp.tv_sec, timestamp.tv_nsec, wait_us,
 	       (wait_us == 1) ? "" : "s");
 }
 
-- 
1.8.1.4

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v6 7/8] example: minimal ptp client implementation
  2015-11-12 12:55   ` [dpdk-dev] [PATCH v6 0/8] " Pablo de Lara
                       ` (5 preceding siblings ...)
  2015-11-12 12:55     ` [dpdk-dev] [PATCH v6 6/8] testpmd: add nanosecond output for ieee1588 fwd Pablo de Lara
@ 2015-11-12 12:55     ` Pablo de Lara
  2015-11-12 12:55     ` [dpdk-dev] [PATCH v6 8/8] doc: add a ptpclient sample guide Pablo de Lara
                       ` (2 subsequent siblings)
  9 siblings, 0 replies; 84+ messages in thread
From: Pablo de Lara @ 2015-11-12 12:55 UTC (permalink / raw)
  To: dev

From: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>

Add a sample application that acts as a PTP slave using the
DPDK ieee1588 functions.

Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
Reviewed-by: John McNamara <john.mcnamara@intel.com>
---
 MAINTAINERS                    |   4 +
 examples/Makefile              |   1 +
 examples/ptpclient/Makefile    |  56 +++
 examples/ptpclient/ptpclient.c | 780 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 841 insertions(+)
 create mode 100644 examples/ptpclient/Makefile
 create mode 100644 examples/ptpclient/ptpclient.c

diff --git a/MAINTAINERS b/MAINTAINERS
index c8be5d2..28b04ae 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -520,3 +520,7 @@ F: examples/tep_termination/
 F: examples/vmdq/
 F: examples/vmdq_dcb/
 F: doc/guides/sample_app_ug/vmdq_dcb_forwarding.rst
+
+M: Pablo de Lara <pablo.de.lara.guarch@intel.com>
+M: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
+F: examples/ptpclient
diff --git a/examples/Makefile b/examples/Makefile
index b4eddbd..4672534 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -74,5 +74,6 @@ DIRS-$(CONFIG_RTE_LIBRTE_XEN_DOM0) += vhost_xen
 DIRS-y += vmdq
 DIRS-y += vmdq_dcb
 DIRS-$(CONFIG_RTE_LIBRTE_POWER) += vm_power_manager
+DIRS-$(CONFIG_RTE_LIBRTE_IEEE1588) += ptpclient
 
 include $(RTE_SDK)/mk/rte.extsubdir.mk
diff --git a/examples/ptpclient/Makefile b/examples/ptpclient/Makefile
new file mode 100644
index 0000000..b77cf71
--- /dev/null
+++ b/examples/ptpclient/Makefile
@@ -0,0 +1,56 @@
+#   BSD LICENSE
+#
+#   Copyright(c) 2015 Intel Corporation. All rights reserved.
+#   All rights reserved.
+#
+#   Redistribution and use in source and binary forms, with or without
+#   modification, are permitted provided that the following conditions
+#   are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in
+#       the documentation and/or other materials provided with the
+#       distribution.
+#     * Neither the name of Intel Corporation nor the names of its
+#       contributors may be used to endorse or promote products derived
+#       from this software without specific prior written permission.
+#
+#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+
+# Default target, can be overriddegitn by command line or environment
+RTE_TARGET ?= x86_64-native-linuxapp-gcc
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# binary name
+APP = ptpclient
+
+# all source are stored in SRCS-y
+SRCS-y := ptpclient.c
+
+CFLAGS += -O3
+CFLAGS += $(WERROR_FLAGS)
+
+# workaround for a gcc bug with noreturn attribute
+# http://gcc.gnu.org/bugzilla/show_bug.cgi?id=12603
+ifeq ($(CONFIG_RTE_TOOLCHAIN_GCC),y)
+CFLAGS_main.o += -Wno-return-type
+endif
+
+include $(RTE_SDK)/mk/rte.extapp.mk
diff --git a/examples/ptpclient/ptpclient.c b/examples/ptpclient/ptpclient.c
new file mode 100644
index 0000000..0af4f3b
--- /dev/null
+++ b/examples/ptpclient/ptpclient.c
@@ -0,0 +1,780 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This application is a simple Layer 2 PTP v2 client. It shows delta values
+ * which are used to synchronize the PHC clock. if the "-T 1" parameter is
+ * passed to the application the Linux kernel clock is also synchronized.
+ */
+
+#include <stdint.h>
+#include <inttypes.h>
+#include <rte_eal.h>
+#include <rte_ethdev.h>
+#include <rte_cycles.h>
+#include <rte_lcore.h>
+#include <rte_mbuf.h>
+#include <rte_ip.h>
+#include <limits.h>
+#include <sys/time.h>
+#include <getopt.h>
+
+#define RX_RING_SIZE 128
+#define TX_RING_SIZE 512
+
+#define NUM_MBUFS            8191
+#define MBUF_CACHE_SIZE       250
+
+/* Values for the PTP messageType field. */
+#define SYNC                  0x0
+#define DELAY_REQ             0x1
+#define PDELAY_REQ            0x2
+#define PDELAY_RESP           0x3
+#define FOLLOW_UP             0x8
+#define DELAY_RESP            0x9
+#define PDELAY_RESP_FOLLOW_UP 0xA
+#define ANNOUNCE              0xB
+#define SIGNALING             0xC
+#define MANAGEMENT            0xD
+
+#define NSEC_PER_SEC        1000000000L
+#define KERNEL_TIME_ADJUST_LIMIT  20000
+#define PTP_PROTOCOL             0x88F7
+
+struct rte_mempool *mbuf_pool;
+uint32_t ptp_enabled_port_mask;
+uint8_t ptp_enabled_port_nb;
+static uint8_t ptp_enabled_ports[RTE_MAX_ETHPORTS];
+
+static const struct rte_eth_conf port_conf_default = {
+	.rxmode = { .max_rx_pkt_len = ETHER_MAX_LEN }
+};
+
+static const struct ether_addr ether_multicast = {
+	.addr_bytes = {0x01, 0x1b, 0x19, 0x0, 0x0, 0x0}
+};
+
+/* Structs used for PTP handling. */
+struct tstamp {
+	uint16_t   sec_msb;
+	uint32_t   sec_lsb;
+	uint32_t   ns;
+}  __attribute__((packed));
+
+struct clock_id {
+	uint8_t id[8];
+};
+
+struct port_id {
+	struct clock_id        clock_id;
+	uint16_t               port_number;
+}  __attribute__((packed));
+
+struct ptp_header {
+	uint8_t              msg_type;
+	uint8_t              ver;
+	uint16_t             message_length;
+	uint8_t              domain_number;
+	uint8_t              reserved1;
+	uint8_t              flag_field[2];
+	int64_t              correction;
+	uint32_t             reserved2;
+	struct port_id       source_port_id;
+	uint16_t             seq_id;
+	uint8_t              control;
+	int8_t               log_message_interval;
+} __attribute__((packed));
+
+struct sync_msg {
+	struct ptp_header   hdr;
+	struct tstamp       origin_tstamp;
+} __attribute__((packed));
+
+struct follow_up_msg {
+	struct ptp_header   hdr;
+	struct tstamp       precise_origin_tstamp;
+	uint8_t             suffix[0];
+} __attribute__((packed));
+
+struct delay_req_msg {
+	struct ptp_header   hdr;
+	struct tstamp       origin_tstamp;
+} __attribute__((packed));
+
+struct delay_resp_msg {
+	struct ptp_header    hdr;
+	struct tstamp        rx_tstamp;
+	struct port_id       req_port_id;
+	uint8_t              suffix[0];
+} __attribute__((packed));
+
+struct ptp_message {
+	union {
+		struct ptp_header          header;
+		struct sync_msg            sync;
+		struct delay_req_msg       delay_req;
+		struct follow_up_msg       follow_up;
+		struct delay_resp_msg      delay_resp;
+	} __attribute__((packed));
+};
+
+struct ptpv2_data_slave_ordinary {
+	struct rte_mbuf *m;
+	struct timespec tstamp1;
+	struct timespec tstamp2;
+	struct timespec tstamp3;
+	struct timespec tstamp4;
+	struct clock_id client_clock_id;
+	struct clock_id master_clock_id;
+	struct timeval new_adj;
+	int64_t delta;
+	uint8_t portid;
+	uint16_t seqID_SYNC;
+	uint16_t seqID_FOLLOWUP;
+	uint8_t ptpset;
+	uint8_t kernel_time_set;
+	uint8_t current_ptp_port;
+};
+
+static struct ptpv2_data_slave_ordinary ptp_data;
+
+static inline uint64_t timespec64_to_ns(const struct timespec *ts)
+{
+	return ((uint64_t) ts->tv_sec * NSEC_PER_SEC) + ts->tv_nsec;
+}
+
+static struct timeval
+ns_to_timeval(int64_t nsec)
+{
+	struct timespec t_spec = {0, 0};
+	struct timeval t_eval = {0, 0};
+	int32_t rem;
+
+	if (nsec == 0)
+		return t_eval;
+	rem = nsec % NSEC_PER_SEC;
+	t_spec.tv_sec = nsec / NSEC_PER_SEC;
+
+	if (rem < 0) {
+		t_spec.tv_sec--;
+		rem += NSEC_PER_SEC;
+	}
+
+	t_spec.tv_nsec = rem;
+	t_eval.tv_sec = t_spec.tv_sec;
+	t_eval.tv_usec = t_spec.tv_nsec / 1000;
+
+	return t_eval;
+}
+
+/*
+ * Initializes a given port using global settings and with the RX buffers
+ * coming from the mbuf_pool passed as a parameter.
+ */
+static inline int
+port_init(uint8_t port, struct rte_mempool *mbuf_pool)
+{
+	struct rte_eth_dev_info dev_info;
+	struct rte_eth_conf port_conf = port_conf_default;
+	const uint16_t rx_rings = 1;
+	const uint16_t tx_rings = 1;
+	int retval;
+	uint16_t q;
+
+	if (port >= rte_eth_dev_count())
+		return -1;
+
+	/* Configure the Ethernet device. */
+	retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf);
+	if (retval != 0)
+		return retval;
+
+	/* Allocate and set up 1 RX queue per Ethernet port. */
+	for (q = 0; q < rx_rings; q++) {
+		retval = rte_eth_rx_queue_setup(port, q, RX_RING_SIZE,
+				rte_eth_dev_socket_id(port), NULL, mbuf_pool);
+
+		if (retval < 0)
+			return retval;
+	}
+
+	/* Allocate and set up 1 TX queue per Ethernet port. */
+	for (q = 0; q < tx_rings; q++) {
+		/* Setup txq_flags */
+		struct rte_eth_txconf *txconf;
+
+		rte_eth_dev_info_get(q, &dev_info);
+		txconf = &dev_info.default_txconf;
+		txconf->txq_flags = 0;
+
+		retval = rte_eth_tx_queue_setup(port, q, TX_RING_SIZE,
+				rte_eth_dev_socket_id(port), txconf);
+		if (retval < 0)
+			return retval;
+	}
+
+	/* Start the Ethernet port. */
+	retval = rte_eth_dev_start(port);
+	if (retval < 0)
+		return retval;
+
+	/* Enable timesync timestamping for the Ethernet device */
+	rte_eth_timesync_enable(port);
+
+	/* Enable RX in promiscuous mode for the Ethernet device. */
+	rte_eth_promiscuous_enable(port);
+
+	return 0;
+}
+
+static void
+print_clock_info(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	int64_t nsec;
+	struct timespec net_time, sys_time;
+
+	printf("Master Clock id: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
+		ptp_data->master_clock_id.id[0],
+		ptp_data->master_clock_id.id[1],
+		ptp_data->master_clock_id.id[2],
+		ptp_data->master_clock_id.id[3],
+		ptp_data->master_clock_id.id[4],
+		ptp_data->master_clock_id.id[5],
+		ptp_data->master_clock_id.id[6],
+		ptp_data->master_clock_id.id[7]);
+
+	printf("\nT2 - Slave  Clock.  %lds %ldns",
+			(ptp_data->tstamp2.tv_sec),
+			(ptp_data->tstamp2.tv_nsec));
+
+	printf("\nT1 - Master Clock.  %lds %ldns ",
+			ptp_data->tstamp1.tv_sec,
+			(ptp_data->tstamp1.tv_nsec));
+
+	printf("\nT3 - Slave  Clock.  %lds %ldns",
+			ptp_data->tstamp3.tv_sec,
+			(ptp_data->tstamp3.tv_nsec));
+
+	printf("\nT4 - Master Clock.  %lds %ldns ",
+			ptp_data->tstamp4.tv_sec,
+			(ptp_data->tstamp4.tv_nsec));
+
+	printf("\nDelta between master and slave clocks:%"PRId64"ns\n",
+			ptp_data->delta);
+
+	clock_gettime(CLOCK_REALTIME, &sys_time);
+	rte_eth_timesync_read_time(ptp_data->current_ptp_port, &net_time);
+
+	time_t ts = net_time.tv_sec;
+
+	printf("\n\nComparison between Linux kernel Time and PTP:");
+
+	printf("\nCurrent PTP Time: %.24s %.9ld ns",
+			ctime(&ts), net_time.tv_nsec);
+
+	nsec = (int64_t)timespec64_to_ns(&net_time) -
+			(int64_t)timespec64_to_ns(&sys_time);
+	ptp_data->new_adj = ns_to_timeval(nsec);
+
+	gettimeofday(&ptp_data->new_adj, NULL);
+
+	time_t tp = ptp_data->new_adj.tv_sec;
+
+	printf("\nCurrent SYS Time: %.24s %.6ld ns",
+				ctime(&tp), ptp_data->new_adj.tv_usec);
+
+	printf("\nDelta between PTP and Linux Kernel time:%"PRId64"ns\n",
+				nsec);
+
+	printf("[Ctrl+C to quit]\n");
+
+	/* Clear screen and put cursor in column 1, row 1 */
+	printf("\033[2J\033[1;1H");
+}
+
+static int64_t
+delta_eval(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	int64_t delta;
+	uint64_t t1 = 0;
+	uint64_t t2 = 0;
+	uint64_t t3 = 0;
+	uint64_t t4 = 0;
+
+	t1 = timespec64_to_ns(&ptp_data->tstamp1);
+	t2 = timespec64_to_ns(&ptp_data->tstamp2);
+	t3 = timespec64_to_ns(&ptp_data->tstamp3);
+	t4 = timespec64_to_ns(&ptp_data->tstamp4);
+
+	delta = -((int64_t)((t2 - t1) - (t4 - t3))) / 2;
+
+	return delta;
+}
+
+/*
+ * Parse the PTP SYNC message.
+ */
+static void
+parse_sync(struct ptpv2_data_slave_ordinary *ptp_data, uint16_t rx_tstamp_idx)
+{
+	struct ptp_header *ptp_hdr;
+
+	ptp_hdr = (struct ptp_header *)(rte_pktmbuf_mtod(ptp_data->m, char *)
+			+ sizeof(struct ether_hdr));
+	ptp_data->seqID_SYNC = rte_be_to_cpu_16(ptp_hdr->seq_id);
+
+	if (ptp_data->ptpset == 0) {
+		rte_memcpy(&ptp_data->master_clock_id,
+				&ptp_hdr->source_port_id.clock_id,
+				sizeof(struct clock_id));
+		ptp_data->ptpset = 1;
+	}
+
+	if (memcmp(&ptp_hdr->source_port_id.clock_id,
+			&ptp_hdr->source_port_id.clock_id,
+			sizeof(struct clock_id)) == 0) {
+
+		if (ptp_data->ptpset == 1)
+			rte_eth_timesync_read_rx_timestamp(ptp_data->portid,
+					&ptp_data->tstamp2, rx_tstamp_idx);
+	}
+
+}
+
+/*
+ * Parse the PTP FOLLOWUP message and send DELAY_REQ to the master clock.
+ */
+static void
+parse_fup(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	struct ether_hdr *eth_hdr;
+	struct ptp_header *ptp_hdr;
+	struct clock_id *client_clkid;
+	struct ptp_message *ptp_msg;
+	struct rte_mbuf *created_pkt;
+	struct tstamp *origin_tstamp;
+	struct ether_addr eth_multicast = ether_multicast;
+	size_t pkt_size;
+	int wait_us;
+	struct rte_mbuf *m = ptp_data->m;
+
+	eth_hdr = rte_pktmbuf_mtod(m, struct ether_hdr *);
+	ptp_hdr = (struct ptp_header *)(rte_pktmbuf_mtod(m, char *)
+			+ sizeof(struct ether_hdr));
+	if (memcmp(&ptp_data->master_clock_id,
+			&ptp_hdr->source_port_id.clock_id,
+			sizeof(struct clock_id)) != 0)
+		return;
+
+	ptp_data->seqID_FOLLOWUP = rte_be_to_cpu_16(ptp_hdr->seq_id);
+	ptp_msg = (struct ptp_message *) (rte_pktmbuf_mtod(m, char *) +
+					  sizeof(struct ether_hdr));
+
+	origin_tstamp = &ptp_msg->follow_up.precise_origin_tstamp;
+	ptp_data->tstamp1.tv_nsec = ntohl(origin_tstamp->ns);
+	ptp_data->tstamp1.tv_sec =
+		((uint64_t)ntohl(origin_tstamp->sec_lsb)) |
+		(((uint64_t)ntohs(origin_tstamp->sec_msb)) << 32);
+
+	if (ptp_data->seqID_FOLLOWUP == ptp_data->seqID_SYNC) {
+
+		created_pkt = rte_pktmbuf_alloc(mbuf_pool);
+		pkt_size = sizeof(struct ether_hdr) +
+			sizeof(struct ptp_message);
+		created_pkt->data_len = pkt_size;
+		created_pkt->pkt_len = pkt_size;
+		eth_hdr = rte_pktmbuf_mtod(created_pkt, struct ether_hdr *);
+		rte_eth_macaddr_get(ptp_data->portid, &eth_hdr->s_addr);
+
+		/* Set multicast address 01-1B-19-00-00-00. */
+		ether_addr_copy(&eth_multicast, &eth_hdr->d_addr);
+
+		eth_hdr->ether_type = htons(PTP_PROTOCOL);
+		ptp_msg = (struct ptp_message *)
+			(rte_pktmbuf_mtod(created_pkt, char *) +
+			sizeof(struct ether_hdr));
+
+		ptp_msg->delay_req.hdr.seq_id = htons(ptp_data->seqID_SYNC);
+		ptp_msg->delay_req.hdr.msg_type = DELAY_REQ;
+		ptp_msg->delay_req.hdr.ver = 2;
+		ptp_msg->delay_req.hdr.control = 1;
+		ptp_msg->delay_req.hdr.log_message_interval = 127;
+
+		/* Set up clock id. */
+		client_clkid =
+			&ptp_msg->delay_req.hdr.source_port_id.clock_id;
+
+		client_clkid->id[0] = eth_hdr->s_addr.addr_bytes[0];
+		client_clkid->id[1] = eth_hdr->s_addr.addr_bytes[1];
+		client_clkid->id[2] = eth_hdr->s_addr.addr_bytes[2];
+		client_clkid->id[3] = 0xFF;
+		client_clkid->id[4] = 0xFE;
+		client_clkid->id[5] = eth_hdr->s_addr.addr_bytes[3];
+		client_clkid->id[6] = eth_hdr->s_addr.addr_bytes[4];
+		client_clkid->id[7] = eth_hdr->s_addr.addr_bytes[5];
+
+		rte_memcpy(&ptp_data->client_clock_id,
+			   client_clkid,
+			   sizeof(struct clock_id));
+
+		/* Enable flag for hardware timestamping. */
+		created_pkt->ol_flags |= PKT_TX_IEEE1588_TMST;
+
+		/*Read value from NIC to prevent latching with old value. */
+		rte_eth_timesync_read_tx_timestamp(ptp_data->portid,
+				&ptp_data->tstamp3);
+
+		/* Transmit the packet. */
+		rte_eth_tx_burst(ptp_data->portid, 0, &created_pkt, 1);
+
+		wait_us = 0;
+		ptp_data->tstamp3.tv_nsec = 0;
+		ptp_data->tstamp3.tv_sec = 0;
+
+		/* Wait at least 1 us to read TX timestamp. */
+		while ((rte_eth_timesync_read_tx_timestamp(ptp_data->portid,
+				&ptp_data->tstamp3) < 0) && (wait_us < 1000)) {
+			rte_delay_us(1);
+			wait_us++;
+		}
+	}
+}
+
+/*
+ * Update the kernel time with the difference between it and the current NIC
+ * time.
+ */
+static inline void
+update_kernel_time(void)
+{
+	int64_t nsec;
+	struct timespec net_time, sys_time;
+
+	clock_gettime(CLOCK_REALTIME, &sys_time);
+	rte_eth_timesync_read_time(ptp_data.current_ptp_port, &net_time);
+
+	nsec = (int64_t)timespec64_to_ns(&net_time) -
+	       (int64_t)timespec64_to_ns(&sys_time);
+
+	ptp_data.new_adj = ns_to_timeval(nsec);
+
+	/*
+	 * If difference between kernel time and system time in NIC is too big
+	 * (more than +/- 20 microseconds), use clock_settime to set directly
+	 * the kernel time, as adjtime is better for small adjustments (takes
+	 * longer to adjust the time).
+	 */
+
+	if (nsec > KERNEL_TIME_ADJUST_LIMIT || nsec < -KERNEL_TIME_ADJUST_LIMIT)
+		clock_settime(CLOCK_REALTIME, &net_time);
+	else
+		adjtime(&ptp_data.new_adj, 0);
+
+
+}
+
+/*
+ * Parse the DELAY_RESP message.
+ */
+static void
+parse_drsp(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	struct rte_mbuf *m = ptp_data->m;
+	struct ptp_message *ptp_msg;
+	struct tstamp *rx_tstamp;
+	uint16_t seq_id;
+
+	ptp_msg = (struct ptp_message *) (rte_pktmbuf_mtod(m, char *) +
+					sizeof(struct ether_hdr));
+	seq_id = rte_be_to_cpu_16(ptp_msg->delay_resp.hdr.seq_id);
+	if (memcmp(&ptp_data->client_clock_id,
+		   &ptp_msg->delay_resp.req_port_id.clock_id,
+		   sizeof(struct clock_id)) == 0) {
+		if (seq_id == ptp_data->seqID_FOLLOWUP) {
+			rx_tstamp = &ptp_msg->delay_resp.rx_tstamp;
+			ptp_data->tstamp4.tv_nsec = ntohl(rx_tstamp->ns);
+			ptp_data->tstamp4.tv_sec =
+				((uint64_t)ntohl(rx_tstamp->sec_lsb)) |
+				(((uint64_t)ntohs(rx_tstamp->sec_msb)) << 32);
+
+			/* Evaluate the delta for adjustment. */
+			ptp_data->delta = delta_eval(ptp_data);
+
+			rte_eth_timesync_adjust_time(ptp_data->portid,
+						     ptp_data->delta);
+
+			ptp_data->current_ptp_port = ptp_data->portid;
+
+			/* Update kernel time if enabled in app parameters. */
+			if (ptp_data->kernel_time_set == 1)
+				update_kernel_time();
+
+
+
+		}
+	}
+}
+
+/* This function processes PTP packets, implementing slave PTP IEEE1588 L2
+ * functionality.
+ */
+static void
+parse_ptp_frames(uint8_t portid, struct rte_mbuf *m) {
+	struct ptp_header *ptp_hdr;
+	struct ether_hdr *eth_hdr;
+	uint16_t eth_type;
+
+	eth_hdr = rte_pktmbuf_mtod(m, struct ether_hdr *);
+	eth_type = rte_be_to_cpu_16(eth_hdr->ether_type);
+
+	if (eth_type == PTP_PROTOCOL) {
+		ptp_data.m = m;
+		ptp_data.portid = portid;
+		ptp_hdr = (struct ptp_header *)(rte_pktmbuf_mtod(m, char *)
+					+ sizeof(struct ether_hdr));
+
+		switch (ptp_hdr->msg_type) {
+		case SYNC:
+			parse_sync(&ptp_data, m->timesync);
+			break;
+		case FOLLOW_UP:
+			parse_fup(&ptp_data);
+			break;
+		case DELAY_RESP:
+			parse_drsp(&ptp_data);
+			print_clock_info(&ptp_data);
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+/*
+ * The lcore main. This is the main thread that does the work, reading from an
+ * input port and writing to an output port.
+ */
+static __attribute__((noreturn)) void
+lcore_main(void)
+{
+	uint8_t portid;
+	unsigned nb_rx;
+	struct rte_mbuf *m;
+
+	/*
+	 * Check that the port is on the same NUMA node as the polling thread
+	 * for best performance.
+	 */
+	printf("\nCore %u Waiting for SYNC packets. [Ctrl+C to quit]\n",
+			rte_lcore_id());
+
+	/* Run until the application is quit or killed. */
+
+	while (1) {
+		/* Read packet from RX queues. */
+		for (portid = 0; portid < ptp_enabled_port_nb; portid++) {
+
+			portid = ptp_enabled_ports[portid];
+			nb_rx = rte_eth_rx_burst(portid, 0, &m, 1);
+
+			if (likely(nb_rx == 0))
+				continue;
+
+			if (m->ol_flags & PKT_RX_IEEE1588_PTP)
+				parse_ptp_frames(portid, m);
+
+			rte_pktmbuf_free(m);
+		}
+	}
+}
+
+static void
+print_usage(const char *prgname)
+{
+	printf("%s [EAL options] -- -p PORTMASK -T VALUE\n"
+		" -T VALUE: 0 - Disable, 1 - Enable Linux Clock"
+		" Synchronization (0 default)\n"
+		" -p PORTMASK: hexadecimal bitmask of ports to configure\n",
+		prgname);
+}
+
+static int
+ptp_parse_portmask(const char *portmask)
+{
+	char *end = NULL;
+	unsigned long pm;
+
+	/* Parse the hexadecimal string. */
+	pm = strtoul(portmask, &end, 16);
+
+	if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
+		return -1;
+
+	if (pm == 0)
+		return -1;
+
+	return pm;
+}
+
+static int
+parse_ptp_kernel(const char *param)
+{
+	char *end = NULL;
+	unsigned long pm;
+
+	/* Parse the hexadecimal string. */
+	pm = strtoul(param, &end, 16);
+
+	if ((param[0] == '\0') || (end == NULL) || (*end != '\0'))
+		return -1;
+	if (pm == 0)
+		return 0;
+
+	return 1;
+}
+
+/* Parse the commandline arguments. */
+static int
+ptp_parse_args(int argc, char **argv)
+{
+	int opt, ret;
+	char **argvopt;
+	int option_index;
+	char *prgname = argv[0];
+	static struct option lgopts[] = { {NULL, 0, 0, 0} };
+
+	argvopt = argv;
+
+	while ((opt = getopt_long(argc, argvopt, "p:T:",
+				  lgopts, &option_index)) != EOF) {
+
+		switch (opt) {
+
+		/* Portmask. */
+		case 'p':
+			ptp_enabled_port_mask = ptp_parse_portmask(optarg);
+			if (ptp_enabled_port_mask == 0) {
+				printf("invalid portmask\n");
+				print_usage(prgname);
+				return -1;
+			}
+			break;
+		/* Time synchronization. */
+		case 'T':
+			ret = parse_ptp_kernel(optarg);
+			if (ret < 0) {
+				print_usage(prgname);
+				return -1;
+			}
+
+			ptp_data.kernel_time_set = ret;
+			break;
+
+		default:
+			print_usage(prgname);
+			return -1;
+		}
+	}
+
+	argv[optind-1] = prgname;
+
+	optind = 0; /* Reset getopt lib. */
+
+	return 0;
+}
+
+/*
+ * The main function, which does initialization and calls the per-lcore
+ * functions.
+ */
+int
+main(int argc, char *argv[])
+{
+	unsigned nb_ports;
+
+	uint8_t portid;
+
+	/* Initialize the Environment Abstraction Layer (EAL). */
+	int ret = rte_eal_init(argc, argv);
+
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
+
+	memset(&ptp_data, '\0', sizeof(struct ptpv2_data_slave_ordinary));
+
+	argc -= ret;
+	argv += ret;
+
+	ret = ptp_parse_args(argc, argv);
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "Error with PTP initialization\n");
+
+	/* Check that there is an even number of ports to send/receive on. */
+	nb_ports = rte_eth_dev_count();
+
+	/* Creates a new mempool in memory to hold the mbufs. */
+	mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", NUM_MBUFS * nb_ports,
+		MBUF_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
+
+	if (mbuf_pool == NULL)
+		rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
+
+	/* Initialize all ports. */
+	for (portid = 0; portid < nb_ports; portid++) {
+		if ((ptp_enabled_port_mask & (1 << portid)) != 0) {
+			if (port_init(portid, mbuf_pool) == 0) {
+				ptp_enabled_ports[ptp_enabled_port_nb] = portid;
+				ptp_enabled_port_nb++;
+			} else {
+				rte_exit(EXIT_FAILURE,
+					 "Cannot init port %"PRIu8 "\n",
+					 portid);
+			}
+		} else
+			printf("Skipping disabled port %u\n", portid);
+	}
+
+	if (ptp_enabled_port_nb == 0) {
+		rte_exit(EXIT_FAILURE,
+			"All available ports are disabled."
+			" Please set portmask.\n");
+	}
+
+	if (rte_lcore_count() > 1)
+		printf("\nWARNING: Too many lcores enabled. Only 1 used.\n");
+
+	/* Call lcore_main on the master core only. */
+	lcore_main();
+
+	return 0;
+}
-- 
1.8.1.4

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v6 8/8] doc: add a ptpclient sample guide
  2015-11-12 12:55   ` [dpdk-dev] [PATCH v6 0/8] " Pablo de Lara
                       ` (6 preceding siblings ...)
  2015-11-12 12:55     ` [dpdk-dev] [PATCH v6 7/8] example: minimal ptp client implementation Pablo de Lara
@ 2015-11-12 12:55     ` Pablo de Lara
  2015-11-13 14:38       ` Thomas Monjalon
  2015-11-12 13:20     ` [dpdk-dev] [PATCH v6 0/8] add sample ptp slave application Mcnamara, John
  2015-11-13 16:09     ` [dpdk-dev] [PATCH v7 " Pablo de Lara
  9 siblings, 1 reply; 84+ messages in thread
From: Pablo de Lara @ 2015-11-12 12:55 UTC (permalink / raw)
  To: dev

From: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>

Add a sample app guide for the ptpclient application.

Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
Reviewed-by: John McNamara <john.mcnamara@intel.com>
---
 doc/guides/sample_app_ug/img/ptpclient.svg | 524 +++++++++++++++++++++++++++++
 doc/guides/sample_app_ug/index.rst         |   3 +
 doc/guides/sample_app_ug/ptpclient.rst     | 306 +++++++++++++++++
 3 files changed, 833 insertions(+)
 create mode 100644 doc/guides/sample_app_ug/img/ptpclient.svg
 create mode 100644 doc/guides/sample_app_ug/ptpclient.rst

diff --git a/doc/guides/sample_app_ug/img/ptpclient.svg b/doc/guides/sample_app_ug/img/ptpclient.svg
new file mode 100644
index 0000000..84f9c22
--- /dev/null
+++ b/doc/guides/sample_app_ug/img/ptpclient.svg
@@ -0,0 +1,524 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="105mm"
+   height="148mm"
+   viewBox="0 0 372.04724 524.40945"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="ptpclient.svg">
+  <defs
+     id="defs4">
+    <marker
+       inkscape:stockid="Arrow2Mend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow2Mend"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4256"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="scale(-0.6,-0.6)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Lstart"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow2Lstart"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4247"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="matrix(1.1,0,0,1.1,1.1,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible"
+       id="marker6746"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="Arrow2Lend">
+      <path
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         id="path6748"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker6526"
+       style="overflow:visible"
+       inkscape:isstock="true"
+       inkscape:collect="always">
+      <path
+         id="path6528"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker6276"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path6278"
+         d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(-0.8,0,0,-0.8,-10,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible"
+       id="marker6128"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="DotM">
+      <path
+         transform="matrix(0.4,0,0,0.4,2.96,0.4)"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
+         id="path6130"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible"
+       id="marker6022"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="Arrow2Lend"
+       inkscape:collect="always">
+      <path
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         id="path6024"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible"
+       id="marker5922"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="DotM">
+      <path
+         transform="matrix(0.4,0,0,0.4,2.96,0.4)"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
+         id="path5924"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow2Lend"
+       style="overflow:visible"
+       inkscape:isstock="true"
+       inkscape:collect="always">
+      <path
+         id="path4250"
+         style="fill:#0000ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="DotM"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker5676"
+       style="overflow:visible"
+       inkscape:isstock="true"
+       inkscape:collect="always">
+      <path
+         id="path5678"
+         d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
+         style="fill:#0000ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(0.4,0,0,0.4,2.96,0.4)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lstart"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker5600"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path5602"
+         d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(0.8,0,0,0.8,10,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible"
+       id="marker5510"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="DotM"
+       inkscape:collect="always">
+      <path
+         transform="matrix(0.4,0,0,0.4,2.96,0.4)"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
+         id="path5512"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker4651"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4653"
+         d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(-0.8,0,0,-0.8,-10,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker4605"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4607"
+         d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(-0.8,0,0,-0.8,-10,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow1Lend"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4232"
+         d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(-0.8,0,0,-0.8,-10,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="DotM"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="DotM"
+       style="overflow:visible"
+       inkscape:isstock="true"
+       inkscape:collect="always">
+      <path
+         id="path4293"
+         d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(0.4,0,0,0.4,2.96,0.4)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lstart"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow1Lstart"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4229"
+         d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(0.8,0,0,0.8,10,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3402"
+       is_visible="true" />
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3398"
+       is_visible="true" />
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3394"
+       is_visible="true" />
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3378"
+       is_visible="true" />
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3374"
+       is_visible="true" />
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3370"
+       is_visible="true" />
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3366"
+       is_visible="true" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1.06"
+     inkscape:cx="280.7353"
+     inkscape:cy="266.181"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     showguides="false"
+     inkscape:window-width="1920"
+     inkscape:window-height="1053"
+     inkscape:window-x="94"
+     inkscape:window-y="69"
+     inkscape:window-maximized="0" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-527.95276)">
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.30149066;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="m 64.094156,600.92012 c 0,49.42495 0,321.26225 0,321.26225"
+       id="path3388"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.47424495;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="m 291.64401,600.59617 0,321.95932"
+       id="path3390"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.28789771;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#DotM);marker-end:url(#marker6746)"
+       d="M 64.094156,623.68474 289.7829,665.38811"
+       id="path4223"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.28789771;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#marker5510);marker-end:url(#marker6526)"
+       d="M 63.199944,683.08006 290.44233,725.29448"
+       id="path5274"
+       inkscape:connector-curvature="0" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="295.22058"
+       y="665.10974"
+       id="text5424"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan5426"
+         x="295.22058"
+         y="665.10974">T2</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#800080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="242.44649"
+       y="657.08087"
+       id="text5428"
+       sodipodi:linespacing="100%"
+       transform="matrix(0.98289795,0.18415108,-0.18415108,0.98289795,0,0)"><tspan
+         sodipodi:role="line"
+         id="tspan5430"
+         x="242.44649"
+         y="657.08087"
+         style="font-size:17.5px">FOLLOW UP:T1</tspan></text>
+    <path
+       style="fill:#0000ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.28789771;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#marker5676);marker-end:url(#Arrow2Lend)"
+       d="M 291.5001,774.36878 64.400801,816.4401"
+       id="path5586"
+       inkscape:connector-curvature="0" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="-44.967201"
+       y="809.64362"
+       id="text5898"
+       sodipodi:linespacing="100%"
+       transform="matrix(0.98291896,-0.18403889,0.18403889,0.98291896,0,0)"><tspan
+         sodipodi:role="line"
+         id="tspan5900"
+         x="-44.967201"
+         y="809.64362"
+         style="font-size:17.50000004px">DELAY REQUEST</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="297.08099"
+       y="774.7981"
+       id="text5902"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan5904"
+         x="297.08099"
+         y="774.7981">T3</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#800080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="35.20845"
+       y="817.29871"
+       id="text5906"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan5908"
+         x="35.20845"
+         y="817.29871">T4</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#800080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="33.920555"
+       y="623.68475"
+       id="text5910"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan5912"
+         x="33.920555"
+         y="623.68475">T1</tspan></text>
+    <path
+       style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.28789771;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#marker6128);marker-end:url(#marker6022)"
+       d="M 63.971502,838.62056 290.6415,881.55049"
+       id="path5914"
+       inkscape:connector-curvature="0" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#800080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="223.59686"
+       y="811.73932"
+       id="text6858"
+       sodipodi:linespacing="100%"
+       transform="matrix(0.98367106,0.17997568,-0.17997568,0.98367106,0,0)"><tspan
+         sodipodi:role="line"
+         id="tspan6860"
+         x="223.59686"
+         y="811.73932"
+         style="font-size:17.5px">DELAY RESPONSE:T4</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="698.62549"
+       y="-320.85892"
+       id="text6862"
+       sodipodi:linespacing="100%"
+       transform="matrix(0.0147127,0.99989176,-0.99989176,0.0147127,0,0)"><tspan
+         sodipodi:role="line"
+         id="tspan6864"
+         x="698.62549"
+         y="-320.85892">time</tspan></text>
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2.1464963;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow2Mend)"
+       d="m 337.72657,748.13938 0,66.11208"
+       id="path6866"
+       inkscape:connector-curvature="0" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.42257881px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#800080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="38.764343"
+       y="590.47479"
+       id="text7094"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan7096"
+         x="38.764343"
+         y="590.47479">master</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:47.51625061px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="571.69812"
+       y="61.796165"
+       id="text7098"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan7100"
+         x="571.69812"
+         y="61.796165" /></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.42257881px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="271.23392"
+       y="593.71478"
+       id="text7102"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan7104"
+         x="271.23392"
+         y="593.71478">slave</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.3917141px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#800080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="244.40062"
+       y="602.10443"
+       id="text4317"
+       sodipodi:linespacing="125%"
+       transform="matrix(0.98605316,0.16643065,-0.16643065,0.98605316,0,0)"><tspan
+         sodipodi:role="line"
+         id="tspan4319"
+         x="244.40062"
+         y="602.10443"
+         style="font-size:17.50000004px">SYNC</tspan></text>
+  </g>
+</svg>
diff --git a/doc/guides/sample_app_ug/index.rst b/doc/guides/sample_app_ug/index.rst
index 9beedd9..8ae86c0 100644
--- a/doc/guides/sample_app_ug/index.rst
+++ b/doc/guides/sample_app_ug/index.rst
@@ -73,6 +73,7 @@ Sample Applications User Guide
     vm_power_management
     tep_termination
     proc_info
+    ptpclient
 
 **Figures**
 
@@ -136,6 +137,8 @@ Sample Applications User Guide
 :numref:`figure_overlay_networking` :ref:`figure_overlay_networking`
 :numref:`figure_tep_termination_arch` :ref:`figure_tep_termination_arch`
 
+:numref:`figure_ptpclient_highlevel` :ref:`figure_ptpclient_highlevel`
+
 **Tables**
 
 :numref:`table_qos_metering_1` :ref:`table_qos_metering_1`
diff --git a/doc/guides/sample_app_ug/ptpclient.rst b/doc/guides/sample_app_ug/ptpclient.rst
new file mode 100644
index 0000000..6e425b7
--- /dev/null
+++ b/doc/guides/sample_app_ug/ptpclient.rst
@@ -0,0 +1,306 @@
+..  BSD LICENSE
+    Copyright(c) 2015 Intel Corporation. All rights reserved.
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions
+    are met:
+
+    * Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.
+    * Neither the name of Intel Corporation nor the names of its
+    contributors may be used to endorse or promote products derived
+    from this software without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+    OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+PTP Client Sample Application
+=============================
+
+The PTP (Precision Time Protocol) client sample application is a simple
+example of using the DPDK IEEE1588 API to communicate with a PTP master clock
+to synchronize the time on the NIC and, optionally, on the Linux system.
+
+Note, PTP is a time syncing protocol and cannot be used within DPDK as a
+time-stamping mechanism. See the following for an explanation of the protocol:
+`Precision Time Protocol
+<https://en.wikipedia.org/wiki/Precision_Time_Protocol>`_.
+
+
+Limitations
+-----------
+
+The PTP sample application is intended as a simple reference implementation of
+a PTP client using the DPDK IEEE1588 API.
+In order to keep the application simple the following assumptions are made:
+
+* The first discovered master is the master for the session.
+* Only L2 PTP packets are supported.
+* Only the PTP v2 protocol is supported.
+* Only the slave clock is implemented.
+
+
+How the Application Works
+-------------------------
+
+.. _figure_ptpclient_highlevel:
+
+.. figure:: img/ptpclient.*
+
+   PTP Synchronization Protocol
+
+The PTP synchronization in the sample application works as follows:
+
+* Master sends *Sync* message - the slave saves it as T2.
+* Master sends *Follow Up* message and sends time of T1.
+* Slave sends *Delay Request* frame to PTP Master and stores T3.
+* Master sends *Delay Response* T4 time which is time of received T3.
+
+The adjustment for slave can be represented as:
+
+   adj = -[(T2-T1)-(T4 - T3)]/2
+
+If the command line parameter ``-T 1`` is used the application also
+synchronizes the PTP PHC clock with the Linux kernel clock.
+
+
+Compiling the Application
+-------------------------
+
+To compile the application, export the path to the DPDK source tree and edit
+the ``config/common_linuxapp`` configuration file to enable IEEE1588:
+
+.. code-block:: console
+
+    export RTE_SDK=/path/to/rte_sdk
+
+    # Edit  common_linuxapp and set the following options:
+    CONFIG_RTE_LIBRTE_IEEE1588=y
+
+Set the target, for example:
+
+.. code-block:: console
+
+    export RTE_TARGET=x86_64-native-linuxapp-gcc
+
+See the *DPDK Getting Started* Guide for possible ``RTE_TARGET`` values.
+
+Build the application as follows:
+
+.. code-block:: console
+
+    # Recompile DPDK.
+    make install T=$RTE_TARGET
+
+    # Compile the application.
+    cd ${RTE_SDK}/examples/ptpclient
+    make
+
+
+Running the Application
+-----------------------
+
+To run the example in a ``linuxapp`` environment:
+
+.. code-block:: console
+
+    ./build/ptpclient -c 2 -n 4 -- -p 0x1 -T 0
+
+Refer to *DPDK Getting Started Guide* for general information on running
+applications and the Environment Abstraction Layer (EAL) options.
+
+* ``-p portmask``: Hexadecimal portmask.
+* ``-T 0``: Update only the PTP slave clock.
+* ``-T 1``: Update the PTP slave clock and synchronize the Linux Kernel to the PTP clock.
+
+
+Code Explanation
+----------------
+
+The following sections provide an explanation of the main components of the
+code.
+
+All DPDK library functions used in the sample code are prefixed with ``rte_``
+and are explained in detail in the *DPDK API Documentation*.
+
+
+The Main Function
+~~~~~~~~~~~~~~~~~
+
+The ``main()`` function performs the initialization and calls the execution
+threads for each lcore.
+
+The first task is to initialize the Environment Abstraction Layer (EAL).  The
+``argc`` and ``argv`` arguments are provided to the ``rte_eal_init()``
+function. The value returned is the number of parsed arguments:
+
+.. code-block:: c
+
+    int ret = rte_eal_init(argc, argv);
+    if (ret < 0)
+        rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
+
+And than we parse application specific arguments
+
+.. code-block:: c
+
+    argc -= ret;
+    argv += ret;
+
+    ret = ptp_parse_args(argc, argv);
+    if (ret < 0)
+        rte_exit(EXIT_FAILURE, "Error with PTP initialization\n");
+
+The ``main()`` also allocates a mempool to hold the mbufs (Message Buffers)
+used by the application:
+
+.. code-block:: c
+
+    mbuf_pool = rte_mempool_create("MBUF_POOL",
+                                   NUM_MBUFS * nb_ports,
+                                   MBUF_SIZE,
+                                   MBUF_CACHE_SIZE,
+                                   sizeof(struct rte_pktmbuf_pool_private),
+                                   rte_pktmbuf_pool_init, NULL,
+                                   rte_pktmbuf_init,      NULL,
+                                   rte_socket_id(),
+                                   0);
+
+Mbufs are the packet buffer structure used by DPDK. They are explained in
+detail in the "Mbuf Library" section of the *DPDK Programmer's Guide*.
+
+The ``main()`` function also initializes all the ports using the user defined
+``port_init()`` function with portmask provided by user:
+
+.. code-block:: c
+
+    for (portid = 0; portid < nb_ports; portid++)
+        if ((ptp_enabled_port_mask & (1 << portid)) != 0) {
+
+            if (port_init(portid, mbuf_pool) == 0) {
+                ptp_enabled_ports[ptp_enabled_port_nb] = portid;
+                ptp_enabled_port_nb++;
+            } else {
+                rte_exit(EXIT_FAILURE, "Cannot init port %"PRIu8 "\n",
+                        portid);
+            }
+        }
+
+
+Once the initialization is complete, the application is ready to launch a
+function on an lcore. In this example ``lcore_main()`` is called on a single
+lcore.
+
+.. code-block:: c
+
+	lcore_main();
+
+The ``lcore_main()`` function is explained below.
+
+
+The Lcores Main
+~~~~~~~~~~~~~~~
+
+As we saw above the ``main()`` function calls an application function on the
+available lcores.
+
+The main work of the application is done within the loop:
+
+.. code-block:: c
+
+        for (portid = 0; portid < ptp_enabled_port_nb; portid++) {
+
+            portid = ptp_enabled_ports[portid];
+            nb_rx = rte_eth_rx_burst(portid, 0, &m, 1);
+
+            if (likely(nb_rx == 0))
+                continue;
+
+            if (m->ol_flags & PKT_RX_IEEE1588_PTP)
+                parse_ptp_frames(portid, m);
+
+            rte_pktmbuf_free(m);
+        }
+
+Packets are received one by one on the RX ports and, if required, PTP response
+packets are transmitted on the TX ports.
+
+If the offload flags in the mbuf indicate that the packet is a PTP packet then
+the packet is parsed to determine which type:
+
+.. code-block:: c
+
+            if (m->ol_flags & PKT_RX_IEEE1588_PTP)
+                 parse_ptp_frames(portid, m);
+
+
+All packets are freed explicitly using ``rte_pktmbuf_free()``.
+
+The forwarding loop can be interrupted and the application closed using
+``Ctrl-C``.
+
+
+PTP parsing
+~~~~~~~~~~~
+
+The ``parse_ptp_frames()`` function processes PTP packets, implementing slave
+PTP IEEE1588 L2 functionality.
+
+.. code-block:: c
+
+    void
+    parse_ptp_frames(uint8_t portid, struct rte_mbuf *m) {
+        struct ptp_header *ptp_hdr;
+        struct ether_hdr *eth_hdr;
+        uint16_t eth_type;
+
+        eth_hdr = rte_pktmbuf_mtod(m, struct ether_hdr *);
+        eth_type = rte_be_to_cpu_16(eth_hdr->ether_type);
+
+        if (eth_type == PTP_PROTOCOL) {
+            ptp_data.m = m;
+            ptp_data.portid = portid;
+            ptp_hdr = (struct ptp_header *)(rte_pktmbuf_mtod(m, char *)
+                        + sizeof(struct ether_hdr));
+
+            switch (ptp_hdr->msgtype) {
+            case SYNC:
+                parse_sync(&ptp_data);
+                break;
+            case FOLLOW_UP:
+                parse_fup(&ptp_data);
+                break;
+            case DELAY_RESP:
+                parse_drsp(&ptp_data);
+                print_clock_info(&ptp_data);
+                break;
+            default:
+                break;
+            }
+        }
+    }
+
+There are 3 types of packets on the RX path which we must parse to create a minimal
+implementation of the PTP slave client:
+
+* SYNC packet.
+* FOLLOW UP packet
+* DELAY RESPONSE packet.
+
+When we parse the *FOLLOW UP* packet we also create and send a *DELAY_REQUEST* packet.
+Also when we parse the *DELAY RESPONSE* packet, and all conditions are met we adjust the PTP slave clock.
-- 
1.8.1.4

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [dpdk-dev] [PATCH v6 0/8] add sample ptp slave application
  2015-11-12 12:55   ` [dpdk-dev] [PATCH v6 0/8] " Pablo de Lara
                       ` (7 preceding siblings ...)
  2015-11-12 12:55     ` [dpdk-dev] [PATCH v6 8/8] doc: add a ptpclient sample guide Pablo de Lara
@ 2015-11-12 13:20     ` Mcnamara, John
  2015-11-13 16:09     ` [dpdk-dev] [PATCH v7 " Pablo de Lara
  9 siblings, 0 replies; 84+ messages in thread
From: Mcnamara, John @ 2015-11-12 13:20 UTC (permalink / raw)
  To: De Lara Guarch, Pablo, dev

> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Pablo de Lara
> Sent: Thursday, November 12, 2015 12:56 PM
> To: dev@dpdk.org
> Subject: [dpdk-dev] [PATCH v6 0/8] add sample ptp slave application
> 
> 
> Add a sample application that acts as a PTP slave using the DPDK IEEE1588
> functions.
> 
> Also add some additional IEEE1588 support functions to enable getting,
> setting and adjusting the device time.
> 
> V5->v6:
>  - Moved common functionality for cyclecounter and time conversions
>    functions to lib/librte_eal/common/include/rte_time.h, based on mailing
>    list comments.
>  - Prefixed functions with rte_ and added Doxygen comments.
>  - Refactored cyclecounter structs from previous version to make it more
>    generic.
>  - Fix ieee1588 fwd output in testpmd.

Series Acked-by: John McNamara <john.mcnamara@intel.com>

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [dpdk-dev] [PATCH v6 8/8] doc: add a ptpclient sample guide
  2015-11-12 12:55     ` [dpdk-dev] [PATCH v6 8/8] doc: add a ptpclient sample guide Pablo de Lara
@ 2015-11-13 14:38       ` Thomas Monjalon
  2015-11-13 14:58         ` De Lara Guarch, Pablo
  0 siblings, 1 reply; 84+ messages in thread
From: Thomas Monjalon @ 2015-11-13 14:38 UTC (permalink / raw)
  To: Pablo de Lara, Daniel Mrzyglod, john.mcnamara; +Cc: dev

>  doc/guides/sample_app_ug/img/ptpclient.svg | 524 +++++++++++++++++++++++++++++
[...]
> +   width="105mm"
> +   height="148mm"

Tip: the extra white margins can be stripped in inkscape with ctrl+shift+d
and "resize page to content".

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [dpdk-dev] [PATCH v6 8/8] doc: add a ptpclient sample guide
  2015-11-13 14:38       ` Thomas Monjalon
@ 2015-11-13 14:58         ` De Lara Guarch, Pablo
  2015-11-13 15:10           ` Thomas Monjalon
  0 siblings, 1 reply; 84+ messages in thread
From: De Lara Guarch, Pablo @ 2015-11-13 14:58 UTC (permalink / raw)
  To: Thomas Monjalon, Mrzyglod, DanielX T, Mcnamara, John; +Cc: dev



> -----Original Message-----
> From: Thomas Monjalon [mailto:thomas.monjalon@6wind.com]
> Sent: Friday, November 13, 2015 2:39 PM
> To: De Lara Guarch, Pablo; Mrzyglod, DanielX T; Mcnamara, John
> Cc: dev@dpdk.org; Ananyev, Konstantin
> Subject: Re: [PATCH v6 8/8] doc: add a ptpclient sample guide
> 
> >  doc/guides/sample_app_ug/img/ptpclient.svg | 524
> +++++++++++++++++++++++++++++
> [...]
> > +   width="105mm"
> > +   height="148mm"
> 
> Tip: the extra white margins can be stripped in inkscape with ctrl+shift+d
> and "resize page to content".

Thanks for the tip! I will change it and send a new version shortly today.

P.S.: I have done more refactoring, to make it simpler and more generic, will send it with the v7. 

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [dpdk-dev] [PATCH v6 8/8] doc: add a ptpclient sample guide
  2015-11-13 14:58         ` De Lara Guarch, Pablo
@ 2015-11-13 15:10           ` Thomas Monjalon
  2015-11-13 15:15             ` De Lara Guarch, Pablo
  0 siblings, 1 reply; 84+ messages in thread
From: Thomas Monjalon @ 2015-11-13 15:10 UTC (permalink / raw)
  To: De Lara Guarch, Pablo; +Cc: dev

2015-11-13 14:58, De Lara Guarch, Pablo:
> From: Thomas Monjalon [mailto:thomas.monjalon@6wind.com]
> > Tip: the extra white margins can be stripped in inkscape with ctrl+shift+d
> > and "resize page to content".
> 
> Thanks for the tip! I will change it and send a new version shortly today.
> 
> P.S.: I have done more refactoring, to make it simpler and more generic, will send it with the v7. 

Oh no!
Please send a nack when working on a new version.
I was going to push it after some small cleanups.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [dpdk-dev] [PATCH v6 8/8] doc: add a ptpclient sample guide
  2015-11-13 15:10           ` Thomas Monjalon
@ 2015-11-13 15:15             ` De Lara Guarch, Pablo
  2015-11-13 15:19               ` Thomas Monjalon
  0 siblings, 1 reply; 84+ messages in thread
From: De Lara Guarch, Pablo @ 2015-11-13 15:15 UTC (permalink / raw)
  To: Thomas Monjalon; +Cc: dev



> -----Original Message-----
> From: Thomas Monjalon [mailto:thomas.monjalon@6wind.com]
> Sent: Friday, November 13, 2015 3:11 PM
> To: De Lara Guarch, Pablo
> Cc: Mrzyglod, DanielX T; Mcnamara, John; dev@dpdk.org; Ananyev,
> Konstantin
> Subject: Re: [PATCH v6 8/8] doc: add a ptpclient sample guide
> 
> 2015-11-13 14:58, De Lara Guarch, Pablo:
> > From: Thomas Monjalon [mailto:thomas.monjalon@6wind.com]
> > > Tip: the extra white margins can be stripped in inkscape with
> ctrl+shift+d
> > > and "resize page to content".
> >
> > Thanks for the tip! I will change it and send a new version shortly today.
> >
> > P.S.: I have done more refactoring, to make it simpler and more generic,
> will send it with the v7.
> 
> Oh no!
> Please send a nack when working on a new version.
> I was going to push it after some small cleanups.

Sorry Thomas, my bad. I just changed the state of the patches to superseded.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [dpdk-dev] [PATCH v6 8/8] doc: add a ptpclient sample guide
  2015-11-13 15:15             ` De Lara Guarch, Pablo
@ 2015-11-13 15:19               ` Thomas Monjalon
  0 siblings, 0 replies; 84+ messages in thread
From: Thomas Monjalon @ 2015-11-13 15:19 UTC (permalink / raw)
  To: De Lara Guarch, Pablo; +Cc: dev

2015-11-13 15:15, De Lara Guarch, Pablo:
> From: Thomas Monjalon [mailto:thomas.monjalon@6wind.com]
> > 2015-11-13 14:58, De Lara Guarch, Pablo:
> > > P.S.: I have done more refactoring, to make it simpler and more generic,
> > will send it with the v7.
> > 
> > Oh no!
> > Please send a nack when working on a new version.
> > I was going to push it after some small cleanups.
> 
> Sorry Thomas, my bad. I just changed the state of the patches to superseded.

Please integrate these changes:

1/ Moved and fixed release notes:

@@ -9,6 +9,10 @@ New Features
   New function rte_ring_free() allows the user to free a ring
   if it was created with rte_ring_create().
 
+* **Added ethdev API to support IEEE1588.**
+
+  Added functions to read and write and adjust system time in the NIC.
+
 * **Extended Statistics**
 
   Define extended statistics naming scheme to store metadata in the name

2/ Alphabetical order in symbols:

 	rte_eth_rx_queue_info_get;
+	rte_eth_timesync_adjust_time;
+	rte_eth_timesync_read_time;
+	rte_eth_timesync_write_time;
 	rte_eth_tx_queue_info_get;

3/ Slash at the end of directory in MAINTAINERS:

+M: Pablo de Lara <pablo.de.lara.guarch@intel.com>
+M: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
+F: examples/ptpclient/

4/ titles:

examples/ptpclient: add minimal PTP client
app/testpmd: add nanosecond output for ieee1588
i40e: support ieee1588 functions for device time
igb: support ieee1588 functions for device time
ixgbe: support ieee1588 functions for device time
eal: add helpers for time conversions
ethdev: add ieee1588 functions for device clock time

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v7 0/8] add sample ptp slave application
  2015-11-12 12:55   ` [dpdk-dev] [PATCH v6 0/8] " Pablo de Lara
                       ` (8 preceding siblings ...)
  2015-11-12 13:20     ` [dpdk-dev] [PATCH v6 0/8] add sample ptp slave application Mcnamara, John
@ 2015-11-13 16:09     ` Pablo de Lara
  2015-11-13 16:09       ` [dpdk-dev] [PATCH v7 1/8] ethdev: add ieee1588 functions for device clock time Pablo de Lara
                         ` (9 more replies)
  9 siblings, 10 replies; 84+ messages in thread
From: Pablo de Lara @ 2015-11-13 16:09 UTC (permalink / raw)
  To: dev; +Cc: konstatin.ananyev

Add a sample application that acts as a PTP slave using the DPDK IEEE1588
functions.

Also add some additional IEEE1588 support functions to enable getting,
setting and adjusting the device time.

V6->v7:
 - Simplified common functionality for timecounters and make it more generic.
 - Reset time counters at initialization.
 - Stripped extra margins in doc image.
 - Fixed release notes, commit titles, rte_ether_version.map and MAINTAINERS file. 

V5->v6:
 - Moved common functionality for cyclecounter and time conversions
   functions to lib/librte_eal/common/include/rte_time.h, based on mailing
   list comments.
 - Prefixed functions with rte_ and added Doxygen comments.
 - Refactored cyclecounter structs from previous version to make it more
   generic.
 - Fix ieee1588 fwd output in testpmd.

V4->v5:
 - rebase to the current master

V3->V4:
Doc:
 - Update documentation for ptpclient
 - fix: put information about ptp application in correct place

V2->V3:
PMD:
 - move common structures and functions for PTP protocol to
   librte_net/rte_ptp.h

V1->V2:
PMDs:
 - add support for e1000
 - add support for ixgbe
 - add support for i40
ethdev:
 - change function names to more proper
Doc:
 - add documentation for ptpclient
sample:
 - add kernel adjustment option
 - add portmask option to provide portmask to application


Daniel Mrzyglod (5):
  ethdev: add additional ieee1588 support functions
  eal: add common time structures and functions
  ixgbe: add additional ieee1588 support functions
  example: minimal ptp client implementation
  doc: add a ptpclient sample guide

Pablo de Lara (3):
  igb: add additional ieee1588 support functions
  i40e: add additional ieee1588 support functions
  testpmd: add nanosecond output for ieee1588 fwd

 MAINTAINERS                                |   4 +
 app/test-pmd/ieee1588fwd.c                 |   8 +-
 doc/guides/rel_notes/release_2_2.rst       |   4 +
 doc/guides/sample_app_ug/img/ptpclient.svg | 524 +++++++++++++++++++
 doc/guides/sample_app_ug/index.rst         |   3 +
 doc/guides/sample_app_ug/ptpclient.rst     | 306 +++++++++++
 drivers/net/e1000/e1000_ethdev.h           |   4 +
 drivers/net/e1000/igb_ethdev.c             | 271 +++++++++-
 drivers/net/i40e/i40e_ethdev.c             | 210 ++++++--
 drivers/net/i40e/i40e_ethdev.h             |   8 +-
 drivers/net/ixgbe/ixgbe_ethdev.c           | 244 ++++++++-
 drivers/net/ixgbe/ixgbe_ethdev.h           |   4 +
 examples/Makefile                          |   1 +
 examples/ptpclient/Makefile                |  56 +++
 examples/ptpclient/ptpclient.c             | 780 +++++++++++++++++++++++++++++
 lib/librte_eal/common/Makefile             |   2 +-
 lib/librte_eal/common/include/rte_time.h   | 122 +++++
 lib/librte_ether/rte_ethdev.c              |  36 ++
 lib/librte_ether/rte_ethdev.h              |  71 +++
 lib/librte_ether/rte_ether_version.map     |   3 +
 20 files changed, 2577 insertions(+), 84 deletions(-)
 create mode 100644 doc/guides/sample_app_ug/img/ptpclient.svg
 create mode 100644 doc/guides/sample_app_ug/ptpclient.rst
 create mode 100644 examples/ptpclient/Makefile
 create mode 100644 examples/ptpclient/ptpclient.c
 create mode 100644 lib/librte_eal/common/include/rte_time.h

-- 
2.5.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v7 1/8] ethdev: add ieee1588 functions for device clock time
  2015-11-13 16:09     ` [dpdk-dev] [PATCH v7 " Pablo de Lara
@ 2015-11-13 16:09       ` Pablo de Lara
  2015-11-13 16:09       ` [dpdk-dev] [PATCH v7 2/8] eal: add helpers for time conversions Pablo de Lara
                         ` (8 subsequent siblings)
  9 siblings, 0 replies; 84+ messages in thread
From: Pablo de Lara @ 2015-11-13 16:09 UTC (permalink / raw)
  To: dev; +Cc: konstatin.ananyev

From: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>

Add additional functions to support the existing IEEE1588
functionality.

  * rte_eth_timesync_write_time():  set the device clock time.
  * rte_eth_timesync_read_time():   get the device clock time.
  * rte_eth_timesync_adjust_time(): adjust the device clock time.

Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
Reviewed-by: John McNamara <john.mcnamara@intel.com>
---
 doc/guides/rel_notes/release_2_2.rst   |  4 ++
 lib/librte_ether/rte_ethdev.c          | 36 +++++++++++++++++
 lib/librte_ether/rte_ethdev.h          | 71 ++++++++++++++++++++++++++++++++++
 lib/librte_ether/rte_ether_version.map |  3 ++
 4 files changed, 114 insertions(+)

diff --git a/doc/guides/rel_notes/release_2_2.rst b/doc/guides/rel_notes/release_2_2.rst
index 1c02ff6..0781ae6 100644
--- a/doc/guides/rel_notes/release_2_2.rst
+++ b/doc/guides/rel_notes/release_2_2.rst
@@ -9,6 +9,10 @@ New Features
   New function rte_ring_free() allows the user to free a ring
   if it was created with rte_ring_create().
 
+* **Added ethdev API to support IEEE1588.**
+
+  Added functions to read, write and adjust system time in the NIC.
+
 * **Extended Statistics**
 
   Define extended statistics naming scheme to store metadata in the name
diff --git a/lib/librte_ether/rte_ethdev.c b/lib/librte_ether/rte_ethdev.c
index 0e41387..b19ac9a 100644
--- a/lib/librte_ether/rte_ethdev.c
+++ b/lib/librte_ether/rte_ethdev.c
@@ -3211,6 +3211,42 @@ rte_eth_timesync_read_tx_timestamp(uint8_t port_id, struct timespec *timestamp)
 }
 
 int
+rte_eth_timesync_adjust_time(uint8_t port_id, int64_t delta)
+{
+	struct rte_eth_dev *dev;
+
+	VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	FUNC_PTR_OR_ERR_RET(*dev->dev_ops->timesync_adjust_time, -ENOTSUP);
+	return (*dev->dev_ops->timesync_adjust_time)(dev, delta);
+}
+
+int
+rte_eth_timesync_read_time(uint8_t port_id, struct timespec *timestamp)
+{
+	struct rte_eth_dev *dev;
+
+	VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	FUNC_PTR_OR_ERR_RET(*dev->dev_ops->timesync_read_time, -ENOTSUP);
+	return (*dev->dev_ops->timesync_read_time)(dev, timestamp);
+}
+
+int
+rte_eth_timesync_write_time(uint8_t port_id, const struct timespec *timestamp)
+{
+	struct rte_eth_dev *dev;
+
+	VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	FUNC_PTR_OR_ERR_RET(*dev->dev_ops->timesync_write_time, -ENOTSUP);
+	return (*dev->dev_ops->timesync_write_time)(dev, timestamp);
+}
+
+int
 rte_eth_dev_get_reg_length(uint8_t port_id)
 {
 	struct rte_eth_dev *dev;
diff --git a/lib/librte_ether/rte_ethdev.h b/lib/librte_ether/rte_ethdev.h
index 5b51fe6..e92bf8d 100644
--- a/lib/librte_ether/rte_ethdev.h
+++ b/lib/librte_ether/rte_ethdev.h
@@ -1206,6 +1206,17 @@ typedef int (*eth_timesync_read_tx_timestamp_t)(struct rte_eth_dev *dev,
 						struct timespec *timestamp);
 /**< @internal Function used to read a TX IEEE1588/802.1AS timestamp. */
 
+typedef int (*eth_timesync_adjust_time)(struct rte_eth_dev *dev, int64_t);
+/**< @internal Function used to adjust the device clock */
+
+typedef int (*eth_timesync_read_time)(struct rte_eth_dev *dev,
+				      struct timespec *timestamp);
+/**< @internal Function used to get time from the device clock. */
+
+typedef int (*eth_timesync_write_time)(struct rte_eth_dev *dev,
+				       const struct timespec *timestamp);
+/**< @internal Function used to get time from the device clock */
+
 typedef int (*eth_get_reg_length_t)(struct rte_eth_dev *dev);
 /**< @internal Retrieve device register count  */
 
@@ -1400,6 +1411,12 @@ struct eth_dev_ops {
 
 	/** Get DCB information */
 	eth_get_dcb_info get_dcb_info;
+	/** Adjust the device clock.*/
+	eth_timesync_adjust_time timesync_adjust_time;
+	/** Get the device clock time. */
+	eth_timesync_read_time timesync_read_time;
+	/** Set the device clock time. */
+	eth_timesync_write_time timesync_write_time;
 };
 
 /**
@@ -3755,6 +3772,60 @@ extern int rte_eth_timesync_read_tx_timestamp(uint8_t port_id,
 					      struct timespec *timestamp);
 
 /**
+ * Adjust the timesync clock on an Ethernet device.
+ *
+ * This is usually used in conjunction with other Ethdev timesync functions to
+ * synchronize the device time using the IEEE1588/802.1AS protocol.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param delta
+ *   The adjustment in nanoseconds.
+ *
+ * @return
+ *   - 0: Success.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -ENOTSUP: The function is not supported by the Ethernet driver.
+ */
+extern int rte_eth_timesync_adjust_time(uint8_t port_id, int64_t delta);
+
+/**
+ * Read the time from the timesync clock on an Ethernet device.
+ *
+ * This is usually used in conjunction with other Ethdev timesync functions to
+ * synchronize the device time using the IEEE1588/802.1AS protocol.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param time
+ *   Pointer to the timespec struct that holds the time.
+ *
+ * @return
+ *   - 0: Success.
+ */
+extern int rte_eth_timesync_read_time(uint8_t port_id, struct timespec *time);
+
+/**
+ * Set the time of the timesync clock on an Ethernet device.
+ *
+ * This is usually used in conjunction with other Ethdev timesync functions to
+ * synchronize the device time using the IEEE1588/802.1AS protocol.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param time
+ *   Pointer to the timespec struct that holds the time.
+ *
+ * @return
+ *   - 0: Success.
+ *   - -EINVAL: No timestamp is available.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -ENOTSUP: The function is not supported by the Ethernet driver.
+ */
+extern int rte_eth_timesync_write_time(uint8_t port_id,
+				       const struct timespec *time);
+
+/**
  * Copy pci device info to the Ethernet device data.
  *
  * @param eth_dev
diff --git a/lib/librte_ether/rte_ether_version.map b/lib/librte_ether/rte_ether_version.map
index 9149aa7..11c4e94 100644
--- a/lib/librte_ether/rte_ether_version.map
+++ b/lib/librte_ether/rte_ether_version.map
@@ -134,6 +134,9 @@ DPDK_2.2 {
 	rte_eth_copy_pci_info;
 	rte_eth_dev_get_dcb_info;
 	rte_eth_rx_queue_info_get;
+	rte_eth_timesync_adjust_time;
+	rte_eth_timesync_read_time;
+	rte_eth_timesync_write_time;
 	rte_eth_tx_queue_info_get;
 
 } DPDK_2.1;
-- 
2.5.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v7 2/8] eal: add helpers for time conversions
  2015-11-13 16:09     ` [dpdk-dev] [PATCH v7 " Pablo de Lara
  2015-11-13 16:09       ` [dpdk-dev] [PATCH v7 1/8] ethdev: add ieee1588 functions for device clock time Pablo de Lara
@ 2015-11-13 16:09       ` Pablo de Lara
  2015-11-13 16:09       ` [dpdk-dev] [PATCH v7 3/8] ixgbe: support ieee1588 functions for device time Pablo de Lara
                         ` (7 subsequent siblings)
  9 siblings, 0 replies; 84+ messages in thread
From: Pablo de Lara @ 2015-11-13 16:09 UTC (permalink / raw)
  To: dev; +Cc: konstatin.ananyev

From: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>

Add common functions and structures to handle time, and cycle counts
which will be used for PTP processing.

Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
Reviewed-by: John McNamara <john.mcnamara@intel.com>
---
 lib/librte_eal/common/Makefile           |   2 +-
 lib/librte_eal/common/include/rte_time.h | 122 +++++++++++++++++++++++++++++++
 2 files changed, 123 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_eal/common/include/rte_time.h

diff --git a/lib/librte_eal/common/Makefile b/lib/librte_eal/common/Makefile
index 0c43d6a..8508473 100644
--- a/lib/librte_eal/common/Makefile
+++ b/lib/librte_eal/common/Makefile
@@ -40,7 +40,7 @@ INC += rte_string_fns.h rte_version.h
 INC += rte_eal_memconfig.h rte_malloc_heap.h
 INC += rte_hexdump.h rte_devargs.h rte_dev.h
 INC += rte_pci_dev_feature_defs.h rte_pci_dev_features.h
-INC += rte_malloc.h
+INC += rte_malloc.h rte_time.h
 
 ifeq ($(CONFIG_RTE_INSECURE_FUNCTION_WARNING),y)
 INC += rte_warnings.h
diff --git a/lib/librte_eal/common/include/rte_time.h b/lib/librte_eal/common/include/rte_time.h
new file mode 100644
index 0000000..4b13b9c
--- /dev/null
+++ b/lib/librte_eal/common/include/rte_time.h
@@ -0,0 +1,122 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define NSEC_PER_SEC             1000000000L
+
+/**
+ * Structure to hold the parameters of a running cycle counter to assist
+ * in converting cycles to nanoseconds.
+ */
+struct rte_timecounter {
+	/** Last cycle counter value read. */
+	uint64_t cycle_last;
+	/** Nanoseconds count. */
+	uint64_t nsec;
+	/** Bitmask separating nanosecond and sub-nanoseconds. */
+	uint64_t nsec_mask;
+	/** Sub-nanoseconds count. */
+	uint64_t nsec_frac;
+	/** Bitmask for two's complement substraction of non-64 bit counters. */
+	uint64_t cc_mask;
+	/** Cycle to nanosecond divisor (power of two). */
+	uint32_t cc_shift;
+};
+
+/**
+ * Converts cyclecounter cycles to nanoseconds.
+ */
+static inline uint64_t
+rte_cyclecounter_cycles_to_ns(struct rte_timecounter *tc, uint64_t cycles)
+{
+	uint64_t ns;
+
+	/* Add fractional nanoseconds. */
+	ns = cycles + tc->nsec_frac;
+	tc->nsec_frac = ns & tc->nsec_mask;
+
+	/* Shift to get only nanoseconds. */
+	return ns >> tc->cc_shift;
+}
+
+/**
+ * Update the internal nanosecond count in the structure.
+ */
+static inline uint64_t
+rte_timecounter_update(struct rte_timecounter *tc, uint64_t cycle_now)
+{
+	uint64_t cycle_delta, ns_offset;
+
+	/* Calculate the delta since the last call. */
+	if (tc->cycle_last <= cycle_now)
+		cycle_delta = (cycle_now - tc->cycle_last) & tc->cc_mask;
+	else
+		/* Handle cycle counts that have wrapped around . */
+		cycle_delta = (~(tc->cycle_last - cycle_now) & tc->cc_mask) + 1;
+
+	/* Convert to nanoseconds. */
+	ns_offset = rte_cyclecounter_cycles_to_ns(tc, cycle_delta);
+
+	/* Store current cycle counter for next call. */
+	tc->cycle_last = cycle_now;
+
+	/* Update the nanosecond count. */
+	tc->nsec += ns_offset;
+
+	return tc->nsec;
+}
+
+/**
+ * Convert from timespec structure into nanosecond units.
+ */
+static inline uint64_t
+rte_timespec_to_ns(const struct timespec *ts)
+{
+	return ((uint64_t) ts->tv_sec * NSEC_PER_SEC) + ts->tv_nsec;
+}
+
+/**
+ * Convert from nanosecond units into timespec structure.
+ */
+static inline struct timespec
+rte_ns_to_timespec(uint64_t nsec)
+{
+	struct timespec ts = {0, 0};
+
+	if (nsec == 0)
+		return ts;
+
+	ts.tv_sec = nsec / NSEC_PER_SEC;
+	ts.tv_nsec = nsec % NSEC_PER_SEC;
+
+	return ts;
+}
-- 
2.5.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v7 3/8] ixgbe: support ieee1588 functions for device time
  2015-11-13 16:09     ` [dpdk-dev] [PATCH v7 " Pablo de Lara
  2015-11-13 16:09       ` [dpdk-dev] [PATCH v7 1/8] ethdev: add ieee1588 functions for device clock time Pablo de Lara
  2015-11-13 16:09       ` [dpdk-dev] [PATCH v7 2/8] eal: add helpers for time conversions Pablo de Lara
@ 2015-11-13 16:09       ` Pablo de Lara
  2015-11-13 16:09       ` [dpdk-dev] [PATCH v7 4/8] igb: " Pablo de Lara
                         ` (6 subsequent siblings)
  9 siblings, 0 replies; 84+ messages in thread
From: Pablo de Lara @ 2015-11-13 16:09 UTC (permalink / raw)
  To: dev; +Cc: konstatin.ananyev

From: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>

Add additional functions to support the existing IEEE1588
functionality and to enable getting, setting and adjusting
the device time.

Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
Reviewed-by: John McNamara <john.mcnamara@intel.com>
---
 drivers/net/ixgbe/ixgbe_ethdev.c | 244 +++++++++++++++++++++++++++++++++++----
 drivers/net/ixgbe/ixgbe_ethdev.h |   4 +
 2 files changed, 227 insertions(+), 21 deletions(-)

diff --git a/drivers/net/ixgbe/ixgbe_ethdev.c b/drivers/net/ixgbe/ixgbe_ethdev.c
index 19ddb52..80801f0 100644
--- a/drivers/net/ixgbe/ixgbe_ethdev.c
+++ b/drivers/net/ixgbe/ixgbe_ethdev.c
@@ -126,10 +126,17 @@
 #define IXGBE_HKEY_MAX_INDEX 10
 
 /* Additional timesync values. */
-#define IXGBE_TIMINCA_16NS_SHIFT 24
-#define IXGBE_TIMINCA_INCVALUE   16000000
-#define IXGBE_TIMINCA_INIT       ((0x02 << IXGBE_TIMINCA_16NS_SHIFT) \
-				  | IXGBE_TIMINCA_INCVALUE)
+#define NSEC_PER_SEC             1000000000L
+#define IXGBE_INCVAL_10GB        0x66666666
+#define IXGBE_INCVAL_1GB         0x40000000
+#define IXGBE_INCVAL_100         0x50000000
+#define IXGBE_INCVAL_SHIFT_10GB  28
+#define IXGBE_INCVAL_SHIFT_1GB   24
+#define IXGBE_INCVAL_SHIFT_100   21
+#define IXGBE_INCVAL_SHIFT_82599 7
+#define IXGBE_INCPER_SHIFT_82599 24
+
+#define IXGBE_CYCLECOUNTER_MASK   0xffffffffffffffff
 
 static int eth_ixgbe_dev_init(struct rte_eth_dev *eth_dev);
 static int eth_ixgbe_dev_uninit(struct rte_eth_dev *eth_dev);
@@ -325,6 +332,11 @@ static int ixgbe_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 					    uint32_t flags);
 static int ixgbe_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 					    struct timespec *timestamp);
+static int ixgbe_timesync_adjust_time(struct rte_eth_dev *dev, int64_t delta);
+static int ixgbe_timesync_read_time(struct rte_eth_dev *dev,
+				   struct timespec *timestamp);
+static int ixgbe_timesync_write_time(struct rte_eth_dev *dev,
+				   const struct timespec *timestamp);
 
 /*
  * Define VF Stats MACRO for Non "cleared on read" register
@@ -480,6 +492,9 @@ static const struct eth_dev_ops ixgbe_eth_dev_ops = {
 	.get_eeprom           = ixgbe_get_eeprom,
 	.set_eeprom           = ixgbe_set_eeprom,
 	.get_dcb_info         = ixgbe_dev_get_dcb_info,
+	.timesync_adjust_time = ixgbe_timesync_adjust_time,
+	.timesync_read_time   = ixgbe_timesync_read_time,
+	.timesync_write_time  = ixgbe_timesync_write_time,
 };
 
 /*
@@ -5582,6 +5597,186 @@ ixgbe_dev_set_mc_addr_list(struct rte_eth_dev *dev,
 					 ixgbe_dev_addr_list_itr, TRUE);
 }
 
+static uint64_t
+ixgbe_read_systime_cyclecounter(struct rte_eth_dev *dev)
+{
+	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	uint64_t systime_cycles;
+
+	switch (hw->mac.type) {
+	case ixgbe_mac_X550:
+		/* SYSTIMEL stores ns and SYSTIMEH stores seconds. */
+		systime_cycles = (uint64_t)IXGBE_READ_REG(hw, IXGBE_SYSTIML);
+		systime_cycles += (uint64_t)IXGBE_READ_REG(hw, IXGBE_SYSTIMH)
+				* NSEC_PER_SEC;
+		break;
+	default:
+		systime_cycles = (uint64_t)IXGBE_READ_REG(hw, IXGBE_SYSTIML);
+		systime_cycles |= (uint64_t)IXGBE_READ_REG(hw, IXGBE_SYSTIMH)
+				<< 32;
+	}
+
+	return systime_cycles;
+}
+
+static uint64_t
+ixgbe_read_rx_tstamp_cyclecounter(struct rte_eth_dev *dev)
+{
+	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	uint64_t rx_tstamp_cycles;
+
+	switch (hw->mac.type) {
+	case ixgbe_mac_X550:
+		/* RXSTMPL stores ns and RXSTMPH stores seconds. */
+		rx_tstamp_cycles = (uint64_t)IXGBE_READ_REG(hw, IXGBE_RXSTMPL);
+		rx_tstamp_cycles += (uint64_t)IXGBE_READ_REG(hw, IXGBE_RXSTMPH)
+				* NSEC_PER_SEC;
+		break;
+	default:
+		/* RXSTMPL stores ns and RXSTMPH stores seconds. */
+		rx_tstamp_cycles = (uint64_t)IXGBE_READ_REG(hw, IXGBE_RXSTMPL);
+		rx_tstamp_cycles |= (uint64_t)IXGBE_READ_REG(hw, IXGBE_RXSTMPH)
+				<< 32;
+	}
+
+	return rx_tstamp_cycles;
+}
+
+static uint64_t
+ixgbe_read_tx_tstamp_cyclecounter(struct rte_eth_dev *dev)
+{
+	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	uint64_t tx_tstamp_cycles;
+
+	switch (hw->mac.type) {
+	case ixgbe_mac_X550:
+		/* TXSTMPL stores ns and TXSTMPH stores seconds. */
+		tx_tstamp_cycles = (uint64_t)IXGBE_READ_REG(hw, IXGBE_TXSTMPL);
+		tx_tstamp_cycles += (uint64_t)IXGBE_READ_REG(hw, IXGBE_TXSTMPH)
+				* NSEC_PER_SEC;
+		break;
+	default:
+		/* TXSTMPL stores ns and TXSTMPH stores seconds. */
+		tx_tstamp_cycles = (uint64_t)IXGBE_READ_REG(hw, IXGBE_TXSTMPL);
+		tx_tstamp_cycles |= (uint64_t)IXGBE_READ_REG(hw, IXGBE_TXSTMPH)
+				<< 32;
+	}
+
+	return tx_tstamp_cycles;
+}
+
+static void
+ixgbe_start_timecounters(struct rte_eth_dev *dev)
+{
+	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct ixgbe_adapter *adapter =
+		(struct ixgbe_adapter *)dev->data->dev_private;
+	struct rte_eth_link link;
+	uint32_t incval = 0;
+	uint32_t shift = 0;
+
+	/* Get current link speed. */
+	memset(&link, 0, sizeof(link));
+	ixgbe_dev_link_update(dev, 1);
+	rte_ixgbe_dev_atomic_read_link_status(dev, &link);
+
+	switch (link.link_speed) {
+	case ETH_LINK_SPEED_100:
+		incval = IXGBE_INCVAL_100;
+		shift = IXGBE_INCVAL_SHIFT_100;
+		break;
+	case ETH_LINK_SPEED_1000:
+		incval = IXGBE_INCVAL_1GB;
+		shift = IXGBE_INCVAL_SHIFT_1GB;
+		break;
+	case ETH_LINK_SPEED_10000:
+	default:
+		incval = IXGBE_INCVAL_10GB;
+		shift = IXGBE_INCVAL_SHIFT_10GB;
+		break;
+	}
+
+	switch (hw->mac.type) {
+	case ixgbe_mac_X550:
+		/* Independent of link speed. */
+		incval = 1;
+		/* Cycles read will be interpreted as ns. */
+		shift = 0;
+		/* Fall-through */
+	case ixgbe_mac_X540:
+		IXGBE_WRITE_REG(hw, IXGBE_TIMINCA, incval);
+		break;
+	case ixgbe_mac_82599EB:
+		incval >>= IXGBE_INCVAL_SHIFT_82599;
+		shift -= IXGBE_INCVAL_SHIFT_82599;
+		IXGBE_WRITE_REG(hw, IXGBE_TIMINCA,
+				(1 << IXGBE_INCPER_SHIFT_82599) | incval);
+		break;
+	default:
+		/* Not supported. */
+		return;
+	}
+
+	memset(&adapter->systime_tc, 0, sizeof(struct rte_timecounter));
+	memset(&adapter->rx_tstamp_tc, 0, sizeof(struct rte_timecounter));
+	memset(&adapter->tx_tstamp_tc, 0, sizeof(struct rte_timecounter));
+
+	adapter->systime_tc.cc_mask = IXGBE_CYCLECOUNTER_MASK;
+	adapter->systime_tc.cc_shift = shift;
+	adapter->systime_tc.nsec_mask = (1ULL << shift) - 1;
+
+	adapter->rx_tstamp_tc.cc_mask = IXGBE_CYCLECOUNTER_MASK;
+	adapter->rx_tstamp_tc.cc_shift = shift;
+	adapter->rx_tstamp_tc.nsec_mask = (1ULL << shift) - 1;
+
+	adapter->tx_tstamp_tc.cc_mask = IXGBE_CYCLECOUNTER_MASK;
+	adapter->tx_tstamp_tc.cc_shift = shift;
+	adapter->tx_tstamp_tc.nsec_mask = (1ULL << shift) - 1;
+}
+
+static int
+ixgbe_timesync_adjust_time(struct rte_eth_dev *dev, int64_t delta)
+{
+	struct ixgbe_adapter *adapter =
+			(struct ixgbe_adapter *)dev->data->dev_private;
+
+	adapter->systime_tc.nsec += delta;
+	adapter->rx_tstamp_tc.nsec += delta;
+	adapter->tx_tstamp_tc.nsec += delta;
+
+	return 0;
+}
+
+static int
+ixgbe_timesync_write_time(struct rte_eth_dev *dev, const struct timespec *ts)
+{
+	uint64_t ns;
+	struct ixgbe_adapter *adapter =
+			(struct ixgbe_adapter *)dev->data->dev_private;
+
+	ns = rte_timespec_to_ns(ts);
+	/* Set the timecounters to a new value. */
+	adapter->systime_tc.nsec = ns;
+	adapter->rx_tstamp_tc.nsec = ns;
+	adapter->tx_tstamp_tc.nsec = ns;
+
+	return 0;
+}
+
+static int
+ixgbe_timesync_read_time(struct rte_eth_dev *dev, struct timespec *ts)
+{
+	uint64_t ns, systime_cycles;
+	struct ixgbe_adapter *adapter =
+			(struct ixgbe_adapter *)dev->data->dev_private;
+
+	systime_cycles = ixgbe_read_systime_cyclecounter(dev);
+	ns = rte_timecounter_update(&adapter->systime_tc, systime_cycles);
+	*ts = rte_ns_to_timespec(ns);
+
+	return 0;
+}
+
 static int
 ixgbe_timesync_enable(struct rte_eth_dev *dev)
 {
@@ -5589,13 +5784,18 @@ ixgbe_timesync_enable(struct rte_eth_dev *dev)
 	uint32_t tsync_ctl;
 	uint32_t tsauxc;
 
+	/* Stop the timesync system time. */
+	IXGBE_WRITE_REG(hw, IXGBE_TIMINCA, 0x0);
+	/* Reset the timesync system time value. */
+	IXGBE_WRITE_REG(hw, IXGBE_SYSTIML, 0x0);
+	IXGBE_WRITE_REG(hw, IXGBE_SYSTIMH, 0x0);
+
 	/* Enable system time for platforms where it isn't on by default. */
 	tsauxc = IXGBE_READ_REG(hw, IXGBE_TSAUXC);
 	tsauxc &= ~IXGBE_TSAUXC_DISABLE_SYSTIME;
 	IXGBE_WRITE_REG(hw, IXGBE_TSAUXC, tsauxc);
 
-	/* Start incrementing the register used to timestamp PTP packets. */
-	IXGBE_WRITE_REG(hw, IXGBE_TIMINCA, IXGBE_TIMINCA_INIT);
+	ixgbe_start_timecounters(dev);
 
 	/* Enable L2 filtering of IEEE1588/802.1AS Ethernet frame types. */
 	IXGBE_WRITE_REG(hw, IXGBE_ETQF(IXGBE_ETQF_FILTER_1588),
@@ -5613,6 +5813,8 @@ ixgbe_timesync_enable(struct rte_eth_dev *dev)
 	tsync_ctl |= IXGBE_TSYNCTXCTL_ENABLED;
 	IXGBE_WRITE_REG(hw, IXGBE_TSYNCTXCTL, tsync_ctl);
 
+	IXGBE_WRITE_FLUSH(hw);
+
 	return 0;
 }
 
@@ -5647,19 +5849,19 @@ ixgbe_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 				 uint32_t flags __rte_unused)
 {
 	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct ixgbe_adapter *adapter =
+		(struct ixgbe_adapter *)dev->data->dev_private;
 	uint32_t tsync_rxctl;
-	uint32_t rx_stmpl;
-	uint32_t rx_stmph;
+	uint64_t rx_tstamp_cycles;
+	uint64_t ns;
 
 	tsync_rxctl = IXGBE_READ_REG(hw, IXGBE_TSYNCRXCTL);
 	if ((tsync_rxctl & IXGBE_TSYNCRXCTL_VALID) == 0)
 		return -EINVAL;
 
-	rx_stmpl = IXGBE_READ_REG(hw, IXGBE_RXSTMPL);
-	rx_stmph = IXGBE_READ_REG(hw, IXGBE_RXSTMPH);
-
-	timestamp->tv_sec = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
-	timestamp->tv_nsec = 0;
+	rx_tstamp_cycles = ixgbe_read_rx_tstamp_cyclecounter(dev);
+	ns = rte_timecounter_update(&adapter->rx_tstamp_tc, rx_tstamp_cycles);
+	*timestamp = rte_ns_to_timespec(ns);
 
 	return  0;
 }
@@ -5669,21 +5871,21 @@ ixgbe_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 				 struct timespec *timestamp)
 {
 	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct ixgbe_adapter *adapter =
+		(struct ixgbe_adapter *)dev->data->dev_private;
 	uint32_t tsync_txctl;
-	uint32_t tx_stmpl;
-	uint32_t tx_stmph;
+	uint64_t tx_tstamp_cycles;
+	uint64_t ns;
 
 	tsync_txctl = IXGBE_READ_REG(hw, IXGBE_TSYNCTXCTL);
 	if ((tsync_txctl & IXGBE_TSYNCTXCTL_VALID) == 0)
 		return -EINVAL;
 
-	tx_stmpl = IXGBE_READ_REG(hw, IXGBE_TXSTMPL);
-	tx_stmph = IXGBE_READ_REG(hw, IXGBE_TXSTMPH);
+	tx_tstamp_cycles = ixgbe_read_tx_tstamp_cyclecounter(dev);
+	ns = rte_timecounter_update(&adapter->tx_tstamp_tc, tx_tstamp_cycles);
+	*timestamp = rte_ns_to_timespec(ns);
 
-	timestamp->tv_sec = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
-	timestamp->tv_nsec = 0;
-
-	return  0;
+	return 0;
 }
 
 static int
diff --git a/drivers/net/ixgbe/ixgbe_ethdev.h b/drivers/net/ixgbe/ixgbe_ethdev.h
index 1856c42..d26771a 100644
--- a/drivers/net/ixgbe/ixgbe_ethdev.h
+++ b/drivers/net/ixgbe/ixgbe_ethdev.h
@@ -37,6 +37,7 @@
 #include "base/ixgbe_dcb_82599.h"
 #include "base/ixgbe_dcb_82598.h"
 #include "ixgbe_bypass.h"
+#include <rte_time.h>
 
 /* need update link, bit flag */
 #define IXGBE_FLAG_NEED_LINK_UPDATE (uint32_t)(1 << 0)
@@ -282,6 +283,9 @@ struct ixgbe_adapter {
 
 	bool rx_bulk_alloc_allowed;
 	bool rx_vec_allowed;
+	struct rte_timecounter      systime_tc;
+	struct rte_timecounter      rx_tstamp_tc;
+	struct rte_timecounter      tx_tstamp_tc;
 };
 
 #define IXGBE_DEV_PRIVATE_TO_HW(adapter)\
-- 
2.5.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v7 4/8] igb: support ieee1588 functions for device time
  2015-11-13 16:09     ` [dpdk-dev] [PATCH v7 " Pablo de Lara
                         ` (2 preceding siblings ...)
  2015-11-13 16:09       ` [dpdk-dev] [PATCH v7 3/8] ixgbe: support ieee1588 functions for device time Pablo de Lara
@ 2015-11-13 16:09       ` Pablo de Lara
  2015-11-13 16:09       ` [dpdk-dev] [PATCH v7 5/8] i40e: " Pablo de Lara
                         ` (5 subsequent siblings)
  9 siblings, 0 replies; 84+ messages in thread
From: Pablo de Lara @ 2015-11-13 16:09 UTC (permalink / raw)
  To: dev; +Cc: konstatin.ananyev

Add additional functions to support the existing IEEE1588
functionality and to enable getting, setting and adjusting
the device time.

Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
Reviewed-by: John McNamara <john.mcnamara@intel.com>
---
 drivers/net/e1000/e1000_ethdev.h |   4 +
 drivers/net/e1000/igb_ethdev.c   | 271 ++++++++++++++++++++++++++++++++++++---
 2 files changed, 255 insertions(+), 20 deletions(-)

diff --git a/drivers/net/e1000/e1000_ethdev.h b/drivers/net/e1000/e1000_ethdev.h
index a667a1a..e8bf8da 100644
--- a/drivers/net/e1000/e1000_ethdev.h
+++ b/drivers/net/e1000/e1000_ethdev.h
@@ -33,6 +33,7 @@
 
 #ifndef _E1000_ETHDEV_H_
 #define _E1000_ETHDEV_H_
+#include <rte_time.h>
 
 /* need update link, bit flag */
 #define E1000_FLAG_NEED_LINK_UPDATE (uint32_t)(1 << 0)
@@ -257,6 +258,9 @@ struct e1000_adapter {
 	struct e1000_vf_info    *vfdata;
 	struct e1000_filter_info filter;
 	bool stopped;
+	struct rte_timecounter  systime_tc;
+	struct rte_timecounter  rx_tstamp_tc;
+	struct rte_timecounter  tx_tstamp_tc;
 };
 
 #define E1000_DEV_PRIVATE(adapter) \
diff --git a/drivers/net/e1000/igb_ethdev.c b/drivers/net/e1000/igb_ethdev.c
index 4bf4e52..88995b0 100644
--- a/drivers/net/e1000/igb_ethdev.c
+++ b/drivers/net/e1000/igb_ethdev.c
@@ -78,10 +78,11 @@
 #define IGB_8_BIT_MASK   UINT8_MAX
 
 /* Additional timesync values. */
-#define E1000_ETQF_FILTER_1588 3
-#define E1000_TIMINCA_INCVALUE 16000000
-#define E1000_TIMINCA_INIT     ((0x02 << E1000_TIMINCA_16NS_SHIFT) \
-				| E1000_TIMINCA_INCVALUE)
+#define E1000_CYCLECOUNTER_MASK      0xffffffffffffffff
+#define E1000_ETQF_FILTER_1588       3
+#define IGB_82576_TSYNC_SHIFT        16
+#define E1000_INCPERIOD_82576        (1 << E1000_TIMINCA_16NS_SHIFT)
+#define E1000_INCVALUE_82576         (16 << IGB_82576_TSYNC_SHIFT)
 #define E1000_TSAUXC_DISABLE_SYSTIME 0x80000000
 
 static int  eth_igb_configure(struct rte_eth_dev *dev);
@@ -236,6 +237,11 @@ static int igb_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 					  uint32_t flags);
 static int igb_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 					  struct timespec *timestamp);
+static int igb_timesync_adjust_time(struct rte_eth_dev *dev, int64_t delta);
+static int igb_timesync_read_time(struct rte_eth_dev *dev,
+				  struct timespec *timestamp);
+static int igb_timesync_write_time(struct rte_eth_dev *dev,
+				   const struct timespec *timestamp);
 static int eth_igb_rx_queue_intr_enable(struct rte_eth_dev *dev,
 					uint16_t queue_id);
 static int eth_igb_rx_queue_intr_disable(struct rte_eth_dev *dev,
@@ -349,6 +355,9 @@ static const struct eth_dev_ops eth_igb_ops = {
 	.get_eeprom_length    = eth_igb_get_eeprom_length,
 	.get_eeprom           = eth_igb_get_eeprom,
 	.set_eeprom           = eth_igb_set_eeprom,
+	.timesync_adjust_time = igb_timesync_adjust_time,
+	.timesync_read_time   = igb_timesync_read_time,
+	.timesync_write_time  = igb_timesync_write_time,
 };
 
 /*
@@ -4188,6 +4197,209 @@ eth_igb_set_mc_addr_list(struct rte_eth_dev *dev,
 	return 0;
 }
 
+static uint64_t
+igb_read_systime_cyclecounter(struct rte_eth_dev *dev)
+{
+	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	uint64_t systime_cycles;
+
+	switch (hw->mac.type) {
+	case e1000_i210:
+	case e1000_i211:
+		/*
+		 * Need to read System Time Residue Register to be able
+		 * to read the other two registers.
+		 */
+		E1000_READ_REG(hw, E1000_SYSTIMR);
+		/* SYSTIMEL stores ns and SYSTIMEH stores seconds. */
+		systime_cycles = (uint64_t)E1000_READ_REG(hw, E1000_SYSTIML);
+		systime_cycles += (uint64_t)E1000_READ_REG(hw, E1000_SYSTIMH)
+				* NSEC_PER_SEC;
+		break;
+	case e1000_82580:
+	case e1000_i350:
+	case e1000_i354:
+		/*
+		 * Need to read System Time Residue Register to be able
+		 * to read the other two registers.
+		 */
+		E1000_READ_REG(hw, E1000_SYSTIMR);
+		systime_cycles = (uint64_t)E1000_READ_REG(hw, E1000_SYSTIML);
+		/* Only the 8 LSB are valid. */
+		systime_cycles |= (uint64_t)(E1000_READ_REG(hw, E1000_SYSTIMH)
+				& 0xff) << 32;
+		break;
+	default:
+		systime_cycles = (uint64_t)E1000_READ_REG(hw, E1000_SYSTIML);
+		systime_cycles |= (uint64_t)E1000_READ_REG(hw, E1000_SYSTIMH)
+				<< 32;
+		break;
+	}
+
+	return systime_cycles;
+}
+
+static uint64_t
+igb_read_rx_tstamp_cyclecounter(struct rte_eth_dev *dev)
+{
+	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	uint64_t rx_tstamp_cycles;
+
+	switch (hw->mac.type) {
+	case e1000_i210:
+	case e1000_i211:
+		/* RXSTMPL stores ns and RXSTMPH stores seconds. */
+		rx_tstamp_cycles = (uint64_t)E1000_READ_REG(hw, E1000_RXSTMPL);
+		rx_tstamp_cycles += (uint64_t)E1000_READ_REG(hw, E1000_RXSTMPH)
+				* NSEC_PER_SEC;
+		break;
+	case e1000_82580:
+	case e1000_i350:
+	case e1000_i354:
+		rx_tstamp_cycles = (uint64_t)E1000_READ_REG(hw, E1000_RXSTMPL);
+		/* Only the 8 LSB are valid. */
+		rx_tstamp_cycles |= (uint64_t)(E1000_READ_REG(hw, E1000_RXSTMPH)
+				& 0xff) << 32;
+		break;
+	default:
+		rx_tstamp_cycles = (uint64_t)E1000_READ_REG(hw, E1000_RXSTMPL);
+		rx_tstamp_cycles |= (uint64_t)E1000_READ_REG(hw, E1000_RXSTMPH)
+				<< 32;
+		break;
+	}
+
+	return rx_tstamp_cycles;
+}
+
+static uint64_t
+igb_read_tx_tstamp_cyclecounter(struct rte_eth_dev *dev)
+{
+	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	uint64_t tx_tstamp_cycles;
+
+	switch (hw->mac.type) {
+	case e1000_i210:
+	case e1000_i211:
+		/* RXSTMPL stores ns and RXSTMPH stores seconds. */
+		tx_tstamp_cycles = (uint64_t)E1000_READ_REG(hw, E1000_TXSTMPL);
+		tx_tstamp_cycles += (uint64_t)E1000_READ_REG(hw, E1000_TXSTMPH)
+				* NSEC_PER_SEC;
+		break;
+	case e1000_82580:
+	case e1000_i350:
+	case e1000_i354:
+		tx_tstamp_cycles = (uint64_t)E1000_READ_REG(hw, E1000_TXSTMPL);
+		/* Only the 8 LSB are valid. */
+		tx_tstamp_cycles |= (uint64_t)(E1000_READ_REG(hw, E1000_TXSTMPH)
+				& 0xff) << 32;
+		break;
+	default:
+		tx_tstamp_cycles = (uint64_t)E1000_READ_REG(hw, E1000_TXSTMPL);
+		tx_tstamp_cycles |= (uint64_t)E1000_READ_REG(hw, E1000_TXSTMPH)
+				<< 32;
+		break;
+	}
+
+	return tx_tstamp_cycles;
+}
+
+static void
+igb_start_timecounters(struct rte_eth_dev *dev)
+{
+	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct e1000_adapter *adapter =
+		(struct e1000_adapter *)dev->data->dev_private;
+	uint32_t incval = 1;
+	uint32_t shift = 0;
+	uint64_t mask = E1000_CYCLECOUNTER_MASK;
+
+	switch (hw->mac.type) {
+	case e1000_82580:
+	case e1000_i350:
+	case e1000_i354:
+		/* 32 LSB bits + 8 MSB bits = 40 bits */
+		mask = (1ULL << 40) - 1;
+		/* fall-through */
+	case e1000_i210:
+	case e1000_i211:
+		/*
+		 * Start incrementing the register
+		 * used to timestamp PTP packets.
+		 */
+		E1000_WRITE_REG(hw, E1000_TIMINCA, incval);
+		break;
+	case e1000_82576:
+		incval = E1000_INCVALUE_82576;
+		shift = IGB_82576_TSYNC_SHIFT;
+		E1000_WRITE_REG(hw, E1000_TIMINCA,
+				E1000_INCPERIOD_82576 | incval);
+		break;
+	default:
+		/* Not supported */
+		return;
+	}
+
+	memset(&adapter->systime_tc, 0, sizeof(struct rte_timecounter));
+	memset(&adapter->rx_tstamp_tc, 0, sizeof(struct rte_timecounter));
+	memset(&adapter->tx_tstamp_tc, 0, sizeof(struct rte_timecounter));
+
+	adapter->systime_tc.cc_mask = mask;
+	adapter->systime_tc.cc_shift = shift;
+	adapter->systime_tc.nsec_mask = (1ULL << shift) - 1;
+
+	adapter->rx_tstamp_tc.cc_mask = mask;
+	adapter->rx_tstamp_tc.cc_shift = shift;
+	adapter->rx_tstamp_tc.nsec_mask = (1ULL << shift) - 1;
+
+	adapter->tx_tstamp_tc.cc_mask = mask;
+	adapter->tx_tstamp_tc.cc_shift = shift;
+	adapter->tx_tstamp_tc.nsec_mask = (1ULL << shift) - 1;
+}
+
+static int
+igb_timesync_adjust_time(struct rte_eth_dev *dev, int64_t delta)
+{
+	struct e1000_adapter *adapter =
+			(struct e1000_adapter *)dev->data->dev_private;
+
+	adapter->systime_tc.nsec += delta;
+	adapter->rx_tstamp_tc.nsec += delta;
+	adapter->tx_tstamp_tc.nsec += delta;
+
+	return 0;
+}
+
+static int
+igb_timesync_write_time(struct rte_eth_dev *dev, const struct timespec *ts)
+{
+	uint64_t ns;
+	struct e1000_adapter *adapter =
+			(struct e1000_adapter *)dev->data->dev_private;
+
+	ns = rte_timespec_to_ns(ts);
+
+	/* Set the timecounters to a new value. */
+	adapter->systime_tc.nsec = ns;
+	adapter->rx_tstamp_tc.nsec = ns;
+	adapter->tx_tstamp_tc.nsec = ns;
+
+	return 0;
+}
+
+static int
+igb_timesync_read_time(struct rte_eth_dev *dev, struct timespec *ts)
+{
+	uint64_t ns, systime_cycles;
+	struct e1000_adapter *adapter =
+			(struct e1000_adapter *)dev->data->dev_private;
+
+	systime_cycles = igb_read_systime_cyclecounter(dev);
+	ns = rte_timecounter_update(&adapter->systime_tc, systime_cycles);
+	*ts = rte_ns_to_timespec(ns);
+
+	return 0;
+}
+
 static int
 igb_timesync_enable(struct rte_eth_dev *dev)
 {
@@ -4195,13 +4407,32 @@ igb_timesync_enable(struct rte_eth_dev *dev)
 	uint32_t tsync_ctl;
 	uint32_t tsauxc;
 
+	/* Stop the timesync system time. */
+	E1000_WRITE_REG(hw, E1000_TIMINCA, 0x0);
+	/* Reset the timesync system time value. */
+	switch (hw->mac.type) {
+	case e1000_82580:
+	case e1000_i350:
+	case e1000_i354:
+	case e1000_i210:
+	case e1000_i211:
+		E1000_WRITE_REG(hw, E1000_SYSTIMR, 0x0);
+		/* fall-through */
+	case e1000_82576:
+		E1000_WRITE_REG(hw, E1000_SYSTIML, 0x0);
+		E1000_WRITE_REG(hw, E1000_SYSTIMH, 0x0);
+		break;
+	default:
+		/* Not supported. */
+		return -ENOTSUP;
+	}
+
 	/* Enable system time for it isn't on by default. */
 	tsauxc = E1000_READ_REG(hw, E1000_TSAUXC);
 	tsauxc &= ~E1000_TSAUXC_DISABLE_SYSTIME;
 	E1000_WRITE_REG(hw, E1000_TSAUXC, tsauxc);
 
-	/* Start incrementing the register used to timestamp PTP packets. */
-	E1000_WRITE_REG(hw, E1000_TIMINCA, E1000_TIMINCA_INIT);
+	igb_start_timecounters(dev);
 
 	/* Enable L2 filtering of IEEE1588/802.1AS Ethernet frame types. */
 	E1000_WRITE_REG(hw, E1000_ETQF(E1000_ETQF_FILTER_1588),
@@ -4253,19 +4484,19 @@ igb_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 			       uint32_t flags __rte_unused)
 {
 	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct e1000_adapter *adapter =
+			(struct e1000_adapter *)dev->data->dev_private;
 	uint32_t tsync_rxctl;
-	uint32_t rx_stmpl;
-	uint32_t rx_stmph;
+	uint64_t rx_tstamp_cycles;
+	uint64_t ns;
 
 	tsync_rxctl = E1000_READ_REG(hw, E1000_TSYNCRXCTL);
 	if ((tsync_rxctl & E1000_TSYNCRXCTL_VALID) == 0)
 		return -EINVAL;
 
-	rx_stmpl = E1000_READ_REG(hw, E1000_RXSTMPL);
-	rx_stmph = E1000_READ_REG(hw, E1000_RXSTMPH);
-
-	timestamp->tv_sec = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
-	timestamp->tv_nsec = 0;
+	rx_tstamp_cycles = igb_read_rx_tstamp_cyclecounter(dev);
+	ns = rte_timecounter_update(&adapter->rx_tstamp_tc, rx_tstamp_cycles);
+	*timestamp = rte_ns_to_timespec(ns);
 
 	return  0;
 }
@@ -4275,19 +4506,19 @@ igb_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 			       struct timespec *timestamp)
 {
 	struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct e1000_adapter *adapter =
+			(struct e1000_adapter *)dev->data->dev_private;
 	uint32_t tsync_txctl;
-	uint32_t tx_stmpl;
-	uint32_t tx_stmph;
+	uint64_t tx_tstamp_cycles;
+	uint64_t ns;
 
 	tsync_txctl = E1000_READ_REG(hw, E1000_TSYNCTXCTL);
 	if ((tsync_txctl & E1000_TSYNCTXCTL_VALID) == 0)
 		return -EINVAL;
 
-	tx_stmpl = E1000_READ_REG(hw, E1000_TXSTMPL);
-	tx_stmph = E1000_READ_REG(hw, E1000_TXSTMPH);
-
-	timestamp->tv_sec = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
-	timestamp->tv_nsec = 0;
+	tx_tstamp_cycles = igb_read_tx_tstamp_cyclecounter(dev);
+	ns = rte_timecounter_update(&adapter->tx_tstamp_tc, tx_tstamp_cycles);
+	*timestamp = rte_ns_to_timespec(ns);
 
 	return  0;
 }
-- 
2.5.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v7 5/8] i40e: support ieee1588 functions for device time
  2015-11-13 16:09     ` [dpdk-dev] [PATCH v7 " Pablo de Lara
                         ` (3 preceding siblings ...)
  2015-11-13 16:09       ` [dpdk-dev] [PATCH v7 4/8] igb: " Pablo de Lara
@ 2015-11-13 16:09       ` Pablo de Lara
  2015-11-13 16:09       ` [dpdk-dev] [PATCH v7 6/8] testpmd: add nanosecond output for ieee1588 Pablo de Lara
                         ` (4 subsequent siblings)
  9 siblings, 0 replies; 84+ messages in thread
From: Pablo de Lara @ 2015-11-13 16:09 UTC (permalink / raw)
  To: dev; +Cc: konstatin.ananyev

Add additional functions to support the existing IEEE1588
functionality and to enable getting, setting and adjusting
the device time.

Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
Reviewed-by: John McNamara <john.mcnamara@intel.com>
---
 drivers/net/i40e/i40e_ethdev.c | 210 +++++++++++++++++++++++++++++++++--------
 drivers/net/i40e/i40e_ethdev.h |   8 +-
 2 files changed, 180 insertions(+), 38 deletions(-)

diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 6fc4f88..2c51a0b 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -125,11 +125,13 @@
 	(1UL << RTE_ETH_FLOW_NONFRAG_IPV6_OTHER) | \
 	(1UL << RTE_ETH_FLOW_L2_PAYLOAD))
 
-#define I40E_PTP_40GB_INCVAL  0x0199999999ULL
-#define I40E_PTP_10GB_INCVAL  0x0333333333ULL
-#define I40E_PTP_1GB_INCVAL   0x2000000000ULL
-#define I40E_PRTTSYN_TSYNENA  0x80000000
-#define I40E_PRTTSYN_TSYNTYPE 0x0e000000
+/* Additional timesync values. */
+#define I40E_PTP_40GB_INCVAL     0x0199999999ULL
+#define I40E_PTP_10GB_INCVAL     0x0333333333ULL
+#define I40E_PTP_1GB_INCVAL      0x2000000000ULL
+#define I40E_PRTTSYN_TSYNENA     0x80000000
+#define I40E_PRTTSYN_TSYNTYPE    0x0e000000
+#define I40E_CYCLECOUNTER_MASK   0xffffffffffffffff
 
 #define I40E_MAX_PERCENT            100
 #define I40E_DEFAULT_DCB_APP_NUM    1
@@ -399,11 +401,20 @@ static int i40e_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 static int i40e_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 					   struct timespec *timestamp);
 static void i40e_read_stats_registers(struct i40e_pf *pf, struct i40e_hw *hw);
+
+static int i40e_timesync_adjust_time(struct rte_eth_dev *dev, int64_t delta);
+
+static int i40e_timesync_read_time(struct rte_eth_dev *dev,
+				   struct timespec *timestamp);
+static int i40e_timesync_write_time(struct rte_eth_dev *dev,
+				    const struct timespec *timestamp);
+
 static int i40e_dev_rx_queue_intr_enable(struct rte_eth_dev *dev,
 					 uint16_t queue_id);
 static int i40e_dev_rx_queue_intr_disable(struct rte_eth_dev *dev,
 					  uint16_t queue_id);
 
+
 static const struct rte_pci_id pci_id_i40e_map[] = {
 #define RTE_PCI_DEV_ID_DECL_I40E(vend, dev) {RTE_PCI_DEVICE(vend, dev)},
 #include "rte_pci_dev_ids.h"
@@ -468,6 +479,9 @@ static const struct eth_dev_ops i40e_eth_dev_ops = {
 	.timesync_read_rx_timestamp   = i40e_timesync_read_rx_timestamp,
 	.timesync_read_tx_timestamp   = i40e_timesync_read_tx_timestamp,
 	.get_dcb_info                 = i40e_dev_get_dcb_info,
+	.timesync_adjust_time         = i40e_timesync_adjust_time,
+	.timesync_read_time           = i40e_timesync_read_time,
+	.timesync_write_time          = i40e_timesync_write_time,
 };
 
 /* store statistics names and its offset in stats structure */
@@ -7761,17 +7775,61 @@ i40e_mirror_rule_reset(struct rte_eth_dev *dev, uint8_t sw_id)
 	return 0;
 }
 
-static int
-i40e_timesync_enable(struct rte_eth_dev *dev)
+static uint64_t
+i40e_read_systime_cyclecounter(struct rte_eth_dev *dev)
 {
 	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
-	struct rte_eth_link *link = &dev->data->dev_link;
-	uint32_t tsync_ctl_l;
-	uint32_t tsync_ctl_h;
+	uint64_t systim_cycles;
+
+	systim_cycles = (uint64_t)I40E_READ_REG(hw, I40E_PRTTSYN_TIME_L);
+	systim_cycles |= (uint64_t)I40E_READ_REG(hw, I40E_PRTTSYN_TIME_H)
+			<< 32;
+
+	return systim_cycles;
+}
+
+static uint64_t
+i40e_read_rx_tstamp_cyclecounter(struct rte_eth_dev *dev, uint8_t index)
+{
+	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	uint64_t rx_tstamp;
+
+	rx_tstamp = (uint64_t)I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_L(index));
+	rx_tstamp |= (uint64_t)I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_H(index))
+			<< 32;
+
+	return rx_tstamp;
+}
+
+static uint64_t
+i40e_read_tx_tstamp_cyclecounter(struct rte_eth_dev *dev)
+{
+	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	uint64_t tx_tstamp;
+
+	tx_tstamp = (uint64_t)I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_L);
+	tx_tstamp |= (uint64_t)I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_H)
+			<< 32;
+
+	return tx_tstamp;
+}
+
+static void
+i40e_start_timecounters(struct rte_eth_dev *dev)
+{
+	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct i40e_adapter *adapter =
+			(struct i40e_adapter *)dev->data->dev_private;
+	struct rte_eth_link link;
 	uint32_t tsync_inc_l;
 	uint32_t tsync_inc_h;
 
-	switch (link->link_speed) {
+	/* Get current link speed. */
+	memset(&link, 0, sizeof(link));
+	i40e_dev_link_update(dev, 1);
+	rte_i40e_dev_atomic_read_link_status(dev, &link);
+
+	switch (link.link_speed) {
 	case ETH_LINK_SPEED_40G:
 		tsync_inc_l = I40E_PTP_40GB_INCVAL & 0xFFFFFFFF;
 		tsync_inc_h = I40E_PTP_40GB_INCVAL >> 32;
@@ -7789,19 +7847,95 @@ i40e_timesync_enable(struct rte_eth_dev *dev)
 		tsync_inc_h = 0x0;
 	}
 
-	/* Clear timesync registers. */
-	I40E_READ_REG(hw, I40E_PRTTSYN_STAT_0);
-	I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_H);
-	I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_L(0));
-	I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_L(1));
-	I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_L(2));
-	I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_L(3));
-	I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_H);
-
 	/* Set the timesync increment value. */
 	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_L, tsync_inc_l);
 	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_H, tsync_inc_h);
 
+	memset(&adapter->systime_tc, 0, sizeof(struct rte_timecounter));
+	memset(&adapter->rx_tstamp_tc, 0, sizeof(struct rte_timecounter));
+	memset(&adapter->tx_tstamp_tc, 0, sizeof(struct rte_timecounter));
+
+	adapter->systime_tc.cc_mask = I40E_CYCLECOUNTER_MASK;
+	adapter->systime_tc.cc_shift = 0;
+	adapter->systime_tc.nsec_mask = 0;
+
+	adapter->rx_tstamp_tc.cc_mask = I40E_CYCLECOUNTER_MASK;
+	adapter->rx_tstamp_tc.cc_shift = 0;
+	adapter->rx_tstamp_tc.nsec_mask = 0;
+
+	adapter->tx_tstamp_tc.cc_mask = I40E_CYCLECOUNTER_MASK;
+	adapter->tx_tstamp_tc.cc_shift = 0;
+	adapter->tx_tstamp_tc.nsec_mask = 0;
+}
+
+static int
+i40e_timesync_adjust_time(struct rte_eth_dev *dev, int64_t delta)
+{
+	struct i40e_adapter *adapter =
+			(struct i40e_adapter *)dev->data->dev_private;
+
+	adapter->systime_tc.nsec += delta;
+	adapter->rx_tstamp_tc.nsec += delta;
+	adapter->tx_tstamp_tc.nsec += delta;
+
+	return 0;
+}
+
+static int
+i40e_timesync_write_time(struct rte_eth_dev *dev, const struct timespec *ts)
+{
+	uint64_t ns;
+	struct i40e_adapter *adapter =
+			(struct i40e_adapter *)dev->data->dev_private;
+
+	ns = rte_timespec_to_ns(ts);
+
+	/* Set the timecounters to a new value. */
+	adapter->systime_tc.nsec = ns;
+	adapter->rx_tstamp_tc.nsec = ns;
+	adapter->tx_tstamp_tc.nsec = ns;
+
+	return 0;
+}
+
+static int
+i40e_timesync_read_time(struct rte_eth_dev *dev, struct timespec *ts)
+{
+	uint64_t ns, systime_cycles;
+	struct i40e_adapter *adapter =
+			(struct i40e_adapter *)dev->data->dev_private;
+
+	systime_cycles = i40e_read_systime_cyclecounter(dev);
+	ns = rte_timecounter_update(&adapter->systime_tc, systime_cycles);
+	*ts = rte_ns_to_timespec(ns);
+
+	return 0;
+}
+
+static int
+i40e_timesync_enable(struct rte_eth_dev *dev)
+{
+	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	uint32_t tsync_ctl_l;
+	uint32_t tsync_ctl_h;
+
+	/* Stop the timesync system time. */
+	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_L, 0x0);
+	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_H, 0x0);
+	/* Reset the timesync system time value. */
+	I40E_WRITE_REG(hw, I40E_PRTTSYN_TIME_L, 0x0);
+	I40E_WRITE_REG(hw, I40E_PRTTSYN_TIME_H, 0x0);
+
+	i40e_start_timecounters(dev);
+
+	/* Clear timesync registers. */
+	I40E_READ_REG(hw, I40E_PRTTSYN_STAT_0);
+	I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_H);
+	I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_H(0));
+	I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_H(1));
+	I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_H(2));
+	I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_H(3));
+
 	/* Enable timestamping of PTP packets. */
 	tsync_ctl_l = I40E_READ_REG(hw, I40E_PRTTSYN_CTL0);
 	tsync_ctl_l |= I40E_PRTTSYN_TSYNENA;
@@ -7833,7 +7967,7 @@ i40e_timesync_disable(struct rte_eth_dev *dev)
 	I40E_WRITE_REG(hw, I40E_PRTTSYN_CTL0, tsync_ctl_l);
 	I40E_WRITE_REG(hw, I40E_PRTTSYN_CTL1, tsync_ctl_h);
 
-	/* Set the timesync increment value. */
+	/* Reset the timesync increment value. */
 	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_L, 0x0);
 	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_H, 0x0);
 
@@ -7845,22 +7979,23 @@ i40e_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 				struct timespec *timestamp, uint32_t flags)
 {
 	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct i40e_adapter *adapter =
+		(struct i40e_adapter *)dev->data->dev_private;
+
 	uint32_t sync_status;
-	uint32_t rx_stmpl;
-	uint32_t rx_stmph;
 	uint32_t index = flags & 0x03;
+	uint64_t rx_tstamp_cycles;
+	uint64_t ns;
 
 	sync_status = I40E_READ_REG(hw, I40E_PRTTSYN_STAT_1);
 	if ((sync_status & (1 << index)) == 0)
 		return -EINVAL;
 
-	rx_stmpl = I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_L(index));
-	rx_stmph = I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_H(index));
+	rx_tstamp_cycles = i40e_read_rx_tstamp_cyclecounter(dev, index);
+	ns = rte_timecounter_update(&adapter->rx_tstamp_tc, rx_tstamp_cycles);
+	*timestamp = rte_ns_to_timespec(ns);
 
-	timestamp->tv_sec = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
-	timestamp->tv_nsec = 0;
-
-	return  0;
+	return 0;
 }
 
 static int
@@ -7868,21 +8003,22 @@ i40e_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 				struct timespec *timestamp)
 {
 	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct i40e_adapter *adapter =
+		(struct i40e_adapter *)dev->data->dev_private;
+
 	uint32_t sync_status;
-	uint32_t tx_stmpl;
-	uint32_t tx_stmph;
+	uint64_t tx_tstamp_cycles;
+	uint64_t ns;
 
 	sync_status = I40E_READ_REG(hw, I40E_PRTTSYN_STAT_0);
 	if ((sync_status & I40E_PRTTSYN_STAT_0_TXTIME_MASK) == 0)
 		return -EINVAL;
 
-	tx_stmpl = I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_L);
-	tx_stmph = I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_H);
+	tx_tstamp_cycles = i40e_read_tx_tstamp_cyclecounter(dev);
+	ns = rte_timecounter_update(&adapter->tx_tstamp_tc, tx_tstamp_cycles);
+	*timestamp = rte_ns_to_timespec(ns);
 
-	timestamp->tv_sec = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
-	timestamp->tv_nsec = 0;
-
-	return  0;
+	return 0;
 }
 
 /*
diff --git a/drivers/net/i40e/i40e_ethdev.h b/drivers/net/i40e/i40e_ethdev.h
index d281935..be705f7 100644
--- a/drivers/net/i40e/i40e_ethdev.h
+++ b/drivers/net/i40e/i40e_ethdev.h
@@ -35,6 +35,7 @@
 #define _I40E_ETHDEV_H_
 
 #include <rte_eth_ctrl.h>
+#include <rte_time.h>
 
 #define I40E_VLAN_TAG_SIZE        4
 
@@ -516,11 +517,16 @@ struct i40e_adapter {
 		struct i40e_vf vf;
 	};
 
-	/* for vector PMD */
+	/* For vector PMD */
 	bool rx_bulk_alloc_allowed;
 	bool rx_vec_allowed;
 	bool tx_simple_allowed;
 	bool tx_vec_allowed;
+
+	/* For PTP */
+	struct rte_timecounter systime_tc;
+	struct rte_timecounter rx_tstamp_tc;
+	struct rte_timecounter tx_tstamp_tc;
 };
 
 int i40e_dev_switch_queues(struct i40e_pf *pf, bool on);
-- 
2.5.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v7 6/8] testpmd: add nanosecond output for ieee1588
  2015-11-13 16:09     ` [dpdk-dev] [PATCH v7 " Pablo de Lara
                         ` (4 preceding siblings ...)
  2015-11-13 16:09       ` [dpdk-dev] [PATCH v7 5/8] i40e: " Pablo de Lara
@ 2015-11-13 16:09       ` Pablo de Lara
  2015-11-13 16:09       ` [dpdk-dev] [PATCH v7 7/8] example: add minimal PTP client Pablo de Lara
                         ` (3 subsequent siblings)
  9 siblings, 0 replies; 84+ messages in thread
From: Pablo de Lara @ 2015-11-13 16:09 UTC (permalink / raw)
  To: dev; +Cc: konstatin.ananyev

Testpmd was only printing out second values when printing
RX/TX timestamp value, instead of both second and nanoseconds.
Since resolution of time counters is in nanoseconds,
testpmd should print out both.

Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
Reviewed-by: John McNamara <john.mcnamara@intel.com>
---
 app/test-pmd/ieee1588fwd.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/app/test-pmd/ieee1588fwd.c b/app/test-pmd/ieee1588fwd.c
index b1a301b..c69023a 100644
--- a/app/test-pmd/ieee1588fwd.c
+++ b/app/test-pmd/ieee1588fwd.c
@@ -89,8 +89,8 @@ port_ieee1588_rx_timestamp_check(portid_t pi, uint32_t index)
 		       (unsigned) pi);
 		return;
 	}
-	printf("Port %u RX timestamp value %lu\n",
-	       (unsigned) pi, timestamp.tv_sec);
+	printf("Port %u RX timestamp value %lu s %lu ns\n",
+	       (unsigned) pi, timestamp.tv_sec, timestamp.tv_nsec);
 }
 
 #define MAX_TX_TMST_WAIT_MICROSECS 1000 /**< 1 milli-second */
@@ -112,9 +112,9 @@ port_ieee1588_tx_timestamp_check(portid_t pi)
 		       (unsigned) pi, (unsigned) MAX_TX_TMST_WAIT_MICROSECS);
 		return;
 	}
-	printf("Port %u TX timestamp value %lu validated after "
+	printf("Port %u TX timestamp value %lu s %lu ns validated after "
 	       "%u micro-second%s\n",
-	       (unsigned) pi, timestamp.tv_sec, wait_us,
+	       (unsigned) pi, timestamp.tv_sec, timestamp.tv_nsec, wait_us,
 	       (wait_us == 1) ? "" : "s");
 }
 
-- 
2.5.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v7 7/8] example: add minimal PTP client
  2015-11-13 16:09     ` [dpdk-dev] [PATCH v7 " Pablo de Lara
                         ` (5 preceding siblings ...)
  2015-11-13 16:09       ` [dpdk-dev] [PATCH v7 6/8] testpmd: add nanosecond output for ieee1588 Pablo de Lara
@ 2015-11-13 16:09       ` Pablo de Lara
  2015-11-13 16:09       ` [dpdk-dev] [PATCH v7 8/8] doc: add a ptpclient sample guide Pablo de Lara
                         ` (2 subsequent siblings)
  9 siblings, 0 replies; 84+ messages in thread
From: Pablo de Lara @ 2015-11-13 16:09 UTC (permalink / raw)
  To: dev; +Cc: konstatin.ananyev

From: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>

Add a sample application that acts as a PTP slave using the
DPDK ieee1588 functions.

Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
Reviewed-by: John McNamara <john.mcnamara@intel.com>
---
 MAINTAINERS                    |   4 +
 examples/Makefile              |   1 +
 examples/ptpclient/Makefile    |  56 +++
 examples/ptpclient/ptpclient.c | 780 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 841 insertions(+)
 create mode 100644 examples/ptpclient/Makefile
 create mode 100644 examples/ptpclient/ptpclient.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 0aa9504..d6feada 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -522,3 +522,7 @@ F: examples/tep_termination/
 F: examples/vmdq/
 F: examples/vmdq_dcb/
 F: doc/guides/sample_app_ug/vmdq_dcb_forwarding.rst
+
+M: Pablo de Lara <pablo.de.lara.guarch@intel.com>
+M: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
+F: examples/ptpclient/
diff --git a/examples/Makefile b/examples/Makefile
index b4eddbd..830e31a 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -62,6 +62,7 @@ DIRS-y += load_balancer
 DIRS-y += multi_process
 DIRS-y += netmap_compat/bridge
 DIRS-$(CONFIG_RTE_LIBRTE_REORDER) += packet_ordering
+DIRS-$(CONFIG_RTE_LIBRTE_IEEE1588) += ptpclient
 DIRS-$(CONFIG_RTE_LIBRTE_METER) += qos_meter
 DIRS-$(CONFIG_RTE_LIBRTE_SCHED) += qos_sched
 DIRS-y += quota_watermark
diff --git a/examples/ptpclient/Makefile b/examples/ptpclient/Makefile
new file mode 100644
index 0000000..b77cf71
--- /dev/null
+++ b/examples/ptpclient/Makefile
@@ -0,0 +1,56 @@
+#   BSD LICENSE
+#
+#   Copyright(c) 2015 Intel Corporation. All rights reserved.
+#   All rights reserved.
+#
+#   Redistribution and use in source and binary forms, with or without
+#   modification, are permitted provided that the following conditions
+#   are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in
+#       the documentation and/or other materials provided with the
+#       distribution.
+#     * Neither the name of Intel Corporation nor the names of its
+#       contributors may be used to endorse or promote products derived
+#       from this software without specific prior written permission.
+#
+#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+
+# Default target, can be overriddegitn by command line or environment
+RTE_TARGET ?= x86_64-native-linuxapp-gcc
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# binary name
+APP = ptpclient
+
+# all source are stored in SRCS-y
+SRCS-y := ptpclient.c
+
+CFLAGS += -O3
+CFLAGS += $(WERROR_FLAGS)
+
+# workaround for a gcc bug with noreturn attribute
+# http://gcc.gnu.org/bugzilla/show_bug.cgi?id=12603
+ifeq ($(CONFIG_RTE_TOOLCHAIN_GCC),y)
+CFLAGS_main.o += -Wno-return-type
+endif
+
+include $(RTE_SDK)/mk/rte.extapp.mk
diff --git a/examples/ptpclient/ptpclient.c b/examples/ptpclient/ptpclient.c
new file mode 100644
index 0000000..0af4f3b
--- /dev/null
+++ b/examples/ptpclient/ptpclient.c
@@ -0,0 +1,780 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This application is a simple Layer 2 PTP v2 client. It shows delta values
+ * which are used to synchronize the PHC clock. if the "-T 1" parameter is
+ * passed to the application the Linux kernel clock is also synchronized.
+ */
+
+#include <stdint.h>
+#include <inttypes.h>
+#include <rte_eal.h>
+#include <rte_ethdev.h>
+#include <rte_cycles.h>
+#include <rte_lcore.h>
+#include <rte_mbuf.h>
+#include <rte_ip.h>
+#include <limits.h>
+#include <sys/time.h>
+#include <getopt.h>
+
+#define RX_RING_SIZE 128
+#define TX_RING_SIZE 512
+
+#define NUM_MBUFS            8191
+#define MBUF_CACHE_SIZE       250
+
+/* Values for the PTP messageType field. */
+#define SYNC                  0x0
+#define DELAY_REQ             0x1
+#define PDELAY_REQ            0x2
+#define PDELAY_RESP           0x3
+#define FOLLOW_UP             0x8
+#define DELAY_RESP            0x9
+#define PDELAY_RESP_FOLLOW_UP 0xA
+#define ANNOUNCE              0xB
+#define SIGNALING             0xC
+#define MANAGEMENT            0xD
+
+#define NSEC_PER_SEC        1000000000L
+#define KERNEL_TIME_ADJUST_LIMIT  20000
+#define PTP_PROTOCOL             0x88F7
+
+struct rte_mempool *mbuf_pool;
+uint32_t ptp_enabled_port_mask;
+uint8_t ptp_enabled_port_nb;
+static uint8_t ptp_enabled_ports[RTE_MAX_ETHPORTS];
+
+static const struct rte_eth_conf port_conf_default = {
+	.rxmode = { .max_rx_pkt_len = ETHER_MAX_LEN }
+};
+
+static const struct ether_addr ether_multicast = {
+	.addr_bytes = {0x01, 0x1b, 0x19, 0x0, 0x0, 0x0}
+};
+
+/* Structs used for PTP handling. */
+struct tstamp {
+	uint16_t   sec_msb;
+	uint32_t   sec_lsb;
+	uint32_t   ns;
+}  __attribute__((packed));
+
+struct clock_id {
+	uint8_t id[8];
+};
+
+struct port_id {
+	struct clock_id        clock_id;
+	uint16_t               port_number;
+}  __attribute__((packed));
+
+struct ptp_header {
+	uint8_t              msg_type;
+	uint8_t              ver;
+	uint16_t             message_length;
+	uint8_t              domain_number;
+	uint8_t              reserved1;
+	uint8_t              flag_field[2];
+	int64_t              correction;
+	uint32_t             reserved2;
+	struct port_id       source_port_id;
+	uint16_t             seq_id;
+	uint8_t              control;
+	int8_t               log_message_interval;
+} __attribute__((packed));
+
+struct sync_msg {
+	struct ptp_header   hdr;
+	struct tstamp       origin_tstamp;
+} __attribute__((packed));
+
+struct follow_up_msg {
+	struct ptp_header   hdr;
+	struct tstamp       precise_origin_tstamp;
+	uint8_t             suffix[0];
+} __attribute__((packed));
+
+struct delay_req_msg {
+	struct ptp_header   hdr;
+	struct tstamp       origin_tstamp;
+} __attribute__((packed));
+
+struct delay_resp_msg {
+	struct ptp_header    hdr;
+	struct tstamp        rx_tstamp;
+	struct port_id       req_port_id;
+	uint8_t              suffix[0];
+} __attribute__((packed));
+
+struct ptp_message {
+	union {
+		struct ptp_header          header;
+		struct sync_msg            sync;
+		struct delay_req_msg       delay_req;
+		struct follow_up_msg       follow_up;
+		struct delay_resp_msg      delay_resp;
+	} __attribute__((packed));
+};
+
+struct ptpv2_data_slave_ordinary {
+	struct rte_mbuf *m;
+	struct timespec tstamp1;
+	struct timespec tstamp2;
+	struct timespec tstamp3;
+	struct timespec tstamp4;
+	struct clock_id client_clock_id;
+	struct clock_id master_clock_id;
+	struct timeval new_adj;
+	int64_t delta;
+	uint8_t portid;
+	uint16_t seqID_SYNC;
+	uint16_t seqID_FOLLOWUP;
+	uint8_t ptpset;
+	uint8_t kernel_time_set;
+	uint8_t current_ptp_port;
+};
+
+static struct ptpv2_data_slave_ordinary ptp_data;
+
+static inline uint64_t timespec64_to_ns(const struct timespec *ts)
+{
+	return ((uint64_t) ts->tv_sec * NSEC_PER_SEC) + ts->tv_nsec;
+}
+
+static struct timeval
+ns_to_timeval(int64_t nsec)
+{
+	struct timespec t_spec = {0, 0};
+	struct timeval t_eval = {0, 0};
+	int32_t rem;
+
+	if (nsec == 0)
+		return t_eval;
+	rem = nsec % NSEC_PER_SEC;
+	t_spec.tv_sec = nsec / NSEC_PER_SEC;
+
+	if (rem < 0) {
+		t_spec.tv_sec--;
+		rem += NSEC_PER_SEC;
+	}
+
+	t_spec.tv_nsec = rem;
+	t_eval.tv_sec = t_spec.tv_sec;
+	t_eval.tv_usec = t_spec.tv_nsec / 1000;
+
+	return t_eval;
+}
+
+/*
+ * Initializes a given port using global settings and with the RX buffers
+ * coming from the mbuf_pool passed as a parameter.
+ */
+static inline int
+port_init(uint8_t port, struct rte_mempool *mbuf_pool)
+{
+	struct rte_eth_dev_info dev_info;
+	struct rte_eth_conf port_conf = port_conf_default;
+	const uint16_t rx_rings = 1;
+	const uint16_t tx_rings = 1;
+	int retval;
+	uint16_t q;
+
+	if (port >= rte_eth_dev_count())
+		return -1;
+
+	/* Configure the Ethernet device. */
+	retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf);
+	if (retval != 0)
+		return retval;
+
+	/* Allocate and set up 1 RX queue per Ethernet port. */
+	for (q = 0; q < rx_rings; q++) {
+		retval = rte_eth_rx_queue_setup(port, q, RX_RING_SIZE,
+				rte_eth_dev_socket_id(port), NULL, mbuf_pool);
+
+		if (retval < 0)
+			return retval;
+	}
+
+	/* Allocate and set up 1 TX queue per Ethernet port. */
+	for (q = 0; q < tx_rings; q++) {
+		/* Setup txq_flags */
+		struct rte_eth_txconf *txconf;
+
+		rte_eth_dev_info_get(q, &dev_info);
+		txconf = &dev_info.default_txconf;
+		txconf->txq_flags = 0;
+
+		retval = rte_eth_tx_queue_setup(port, q, TX_RING_SIZE,
+				rte_eth_dev_socket_id(port), txconf);
+		if (retval < 0)
+			return retval;
+	}
+
+	/* Start the Ethernet port. */
+	retval = rte_eth_dev_start(port);
+	if (retval < 0)
+		return retval;
+
+	/* Enable timesync timestamping for the Ethernet device */
+	rte_eth_timesync_enable(port);
+
+	/* Enable RX in promiscuous mode for the Ethernet device. */
+	rte_eth_promiscuous_enable(port);
+
+	return 0;
+}
+
+static void
+print_clock_info(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	int64_t nsec;
+	struct timespec net_time, sys_time;
+
+	printf("Master Clock id: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
+		ptp_data->master_clock_id.id[0],
+		ptp_data->master_clock_id.id[1],
+		ptp_data->master_clock_id.id[2],
+		ptp_data->master_clock_id.id[3],
+		ptp_data->master_clock_id.id[4],
+		ptp_data->master_clock_id.id[5],
+		ptp_data->master_clock_id.id[6],
+		ptp_data->master_clock_id.id[7]);
+
+	printf("\nT2 - Slave  Clock.  %lds %ldns",
+			(ptp_data->tstamp2.tv_sec),
+			(ptp_data->tstamp2.tv_nsec));
+
+	printf("\nT1 - Master Clock.  %lds %ldns ",
+			ptp_data->tstamp1.tv_sec,
+			(ptp_data->tstamp1.tv_nsec));
+
+	printf("\nT3 - Slave  Clock.  %lds %ldns",
+			ptp_data->tstamp3.tv_sec,
+			(ptp_data->tstamp3.tv_nsec));
+
+	printf("\nT4 - Master Clock.  %lds %ldns ",
+			ptp_data->tstamp4.tv_sec,
+			(ptp_data->tstamp4.tv_nsec));
+
+	printf("\nDelta between master and slave clocks:%"PRId64"ns\n",
+			ptp_data->delta);
+
+	clock_gettime(CLOCK_REALTIME, &sys_time);
+	rte_eth_timesync_read_time(ptp_data->current_ptp_port, &net_time);
+
+	time_t ts = net_time.tv_sec;
+
+	printf("\n\nComparison between Linux kernel Time and PTP:");
+
+	printf("\nCurrent PTP Time: %.24s %.9ld ns",
+			ctime(&ts), net_time.tv_nsec);
+
+	nsec = (int64_t)timespec64_to_ns(&net_time) -
+			(int64_t)timespec64_to_ns(&sys_time);
+	ptp_data->new_adj = ns_to_timeval(nsec);
+
+	gettimeofday(&ptp_data->new_adj, NULL);
+
+	time_t tp = ptp_data->new_adj.tv_sec;
+
+	printf("\nCurrent SYS Time: %.24s %.6ld ns",
+				ctime(&tp), ptp_data->new_adj.tv_usec);
+
+	printf("\nDelta between PTP and Linux Kernel time:%"PRId64"ns\n",
+				nsec);
+
+	printf("[Ctrl+C to quit]\n");
+
+	/* Clear screen and put cursor in column 1, row 1 */
+	printf("\033[2J\033[1;1H");
+}
+
+static int64_t
+delta_eval(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	int64_t delta;
+	uint64_t t1 = 0;
+	uint64_t t2 = 0;
+	uint64_t t3 = 0;
+	uint64_t t4 = 0;
+
+	t1 = timespec64_to_ns(&ptp_data->tstamp1);
+	t2 = timespec64_to_ns(&ptp_data->tstamp2);
+	t3 = timespec64_to_ns(&ptp_data->tstamp3);
+	t4 = timespec64_to_ns(&ptp_data->tstamp4);
+
+	delta = -((int64_t)((t2 - t1) - (t4 - t3))) / 2;
+
+	return delta;
+}
+
+/*
+ * Parse the PTP SYNC message.
+ */
+static void
+parse_sync(struct ptpv2_data_slave_ordinary *ptp_data, uint16_t rx_tstamp_idx)
+{
+	struct ptp_header *ptp_hdr;
+
+	ptp_hdr = (struct ptp_header *)(rte_pktmbuf_mtod(ptp_data->m, char *)
+			+ sizeof(struct ether_hdr));
+	ptp_data->seqID_SYNC = rte_be_to_cpu_16(ptp_hdr->seq_id);
+
+	if (ptp_data->ptpset == 0) {
+		rte_memcpy(&ptp_data->master_clock_id,
+				&ptp_hdr->source_port_id.clock_id,
+				sizeof(struct clock_id));
+		ptp_data->ptpset = 1;
+	}
+
+	if (memcmp(&ptp_hdr->source_port_id.clock_id,
+			&ptp_hdr->source_port_id.clock_id,
+			sizeof(struct clock_id)) == 0) {
+
+		if (ptp_data->ptpset == 1)
+			rte_eth_timesync_read_rx_timestamp(ptp_data->portid,
+					&ptp_data->tstamp2, rx_tstamp_idx);
+	}
+
+}
+
+/*
+ * Parse the PTP FOLLOWUP message and send DELAY_REQ to the master clock.
+ */
+static void
+parse_fup(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	struct ether_hdr *eth_hdr;
+	struct ptp_header *ptp_hdr;
+	struct clock_id *client_clkid;
+	struct ptp_message *ptp_msg;
+	struct rte_mbuf *created_pkt;
+	struct tstamp *origin_tstamp;
+	struct ether_addr eth_multicast = ether_multicast;
+	size_t pkt_size;
+	int wait_us;
+	struct rte_mbuf *m = ptp_data->m;
+
+	eth_hdr = rte_pktmbuf_mtod(m, struct ether_hdr *);
+	ptp_hdr = (struct ptp_header *)(rte_pktmbuf_mtod(m, char *)
+			+ sizeof(struct ether_hdr));
+	if (memcmp(&ptp_data->master_clock_id,
+			&ptp_hdr->source_port_id.clock_id,
+			sizeof(struct clock_id)) != 0)
+		return;
+
+	ptp_data->seqID_FOLLOWUP = rte_be_to_cpu_16(ptp_hdr->seq_id);
+	ptp_msg = (struct ptp_message *) (rte_pktmbuf_mtod(m, char *) +
+					  sizeof(struct ether_hdr));
+
+	origin_tstamp = &ptp_msg->follow_up.precise_origin_tstamp;
+	ptp_data->tstamp1.tv_nsec = ntohl(origin_tstamp->ns);
+	ptp_data->tstamp1.tv_sec =
+		((uint64_t)ntohl(origin_tstamp->sec_lsb)) |
+		(((uint64_t)ntohs(origin_tstamp->sec_msb)) << 32);
+
+	if (ptp_data->seqID_FOLLOWUP == ptp_data->seqID_SYNC) {
+
+		created_pkt = rte_pktmbuf_alloc(mbuf_pool);
+		pkt_size = sizeof(struct ether_hdr) +
+			sizeof(struct ptp_message);
+		created_pkt->data_len = pkt_size;
+		created_pkt->pkt_len = pkt_size;
+		eth_hdr = rte_pktmbuf_mtod(created_pkt, struct ether_hdr *);
+		rte_eth_macaddr_get(ptp_data->portid, &eth_hdr->s_addr);
+
+		/* Set multicast address 01-1B-19-00-00-00. */
+		ether_addr_copy(&eth_multicast, &eth_hdr->d_addr);
+
+		eth_hdr->ether_type = htons(PTP_PROTOCOL);
+		ptp_msg = (struct ptp_message *)
+			(rte_pktmbuf_mtod(created_pkt, char *) +
+			sizeof(struct ether_hdr));
+
+		ptp_msg->delay_req.hdr.seq_id = htons(ptp_data->seqID_SYNC);
+		ptp_msg->delay_req.hdr.msg_type = DELAY_REQ;
+		ptp_msg->delay_req.hdr.ver = 2;
+		ptp_msg->delay_req.hdr.control = 1;
+		ptp_msg->delay_req.hdr.log_message_interval = 127;
+
+		/* Set up clock id. */
+		client_clkid =
+			&ptp_msg->delay_req.hdr.source_port_id.clock_id;
+
+		client_clkid->id[0] = eth_hdr->s_addr.addr_bytes[0];
+		client_clkid->id[1] = eth_hdr->s_addr.addr_bytes[1];
+		client_clkid->id[2] = eth_hdr->s_addr.addr_bytes[2];
+		client_clkid->id[3] = 0xFF;
+		client_clkid->id[4] = 0xFE;
+		client_clkid->id[5] = eth_hdr->s_addr.addr_bytes[3];
+		client_clkid->id[6] = eth_hdr->s_addr.addr_bytes[4];
+		client_clkid->id[7] = eth_hdr->s_addr.addr_bytes[5];
+
+		rte_memcpy(&ptp_data->client_clock_id,
+			   client_clkid,
+			   sizeof(struct clock_id));
+
+		/* Enable flag for hardware timestamping. */
+		created_pkt->ol_flags |= PKT_TX_IEEE1588_TMST;
+
+		/*Read value from NIC to prevent latching with old value. */
+		rte_eth_timesync_read_tx_timestamp(ptp_data->portid,
+				&ptp_data->tstamp3);
+
+		/* Transmit the packet. */
+		rte_eth_tx_burst(ptp_data->portid, 0, &created_pkt, 1);
+
+		wait_us = 0;
+		ptp_data->tstamp3.tv_nsec = 0;
+		ptp_data->tstamp3.tv_sec = 0;
+
+		/* Wait at least 1 us to read TX timestamp. */
+		while ((rte_eth_timesync_read_tx_timestamp(ptp_data->portid,
+				&ptp_data->tstamp3) < 0) && (wait_us < 1000)) {
+			rte_delay_us(1);
+			wait_us++;
+		}
+	}
+}
+
+/*
+ * Update the kernel time with the difference between it and the current NIC
+ * time.
+ */
+static inline void
+update_kernel_time(void)
+{
+	int64_t nsec;
+	struct timespec net_time, sys_time;
+
+	clock_gettime(CLOCK_REALTIME, &sys_time);
+	rte_eth_timesync_read_time(ptp_data.current_ptp_port, &net_time);
+
+	nsec = (int64_t)timespec64_to_ns(&net_time) -
+	       (int64_t)timespec64_to_ns(&sys_time);
+
+	ptp_data.new_adj = ns_to_timeval(nsec);
+
+	/*
+	 * If difference between kernel time and system time in NIC is too big
+	 * (more than +/- 20 microseconds), use clock_settime to set directly
+	 * the kernel time, as adjtime is better for small adjustments (takes
+	 * longer to adjust the time).
+	 */
+
+	if (nsec > KERNEL_TIME_ADJUST_LIMIT || nsec < -KERNEL_TIME_ADJUST_LIMIT)
+		clock_settime(CLOCK_REALTIME, &net_time);
+	else
+		adjtime(&ptp_data.new_adj, 0);
+
+
+}
+
+/*
+ * Parse the DELAY_RESP message.
+ */
+static void
+parse_drsp(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	struct rte_mbuf *m = ptp_data->m;
+	struct ptp_message *ptp_msg;
+	struct tstamp *rx_tstamp;
+	uint16_t seq_id;
+
+	ptp_msg = (struct ptp_message *) (rte_pktmbuf_mtod(m, char *) +
+					sizeof(struct ether_hdr));
+	seq_id = rte_be_to_cpu_16(ptp_msg->delay_resp.hdr.seq_id);
+	if (memcmp(&ptp_data->client_clock_id,
+		   &ptp_msg->delay_resp.req_port_id.clock_id,
+		   sizeof(struct clock_id)) == 0) {
+		if (seq_id == ptp_data->seqID_FOLLOWUP) {
+			rx_tstamp = &ptp_msg->delay_resp.rx_tstamp;
+			ptp_data->tstamp4.tv_nsec = ntohl(rx_tstamp->ns);
+			ptp_data->tstamp4.tv_sec =
+				((uint64_t)ntohl(rx_tstamp->sec_lsb)) |
+				(((uint64_t)ntohs(rx_tstamp->sec_msb)) << 32);
+
+			/* Evaluate the delta for adjustment. */
+			ptp_data->delta = delta_eval(ptp_data);
+
+			rte_eth_timesync_adjust_time(ptp_data->portid,
+						     ptp_data->delta);
+
+			ptp_data->current_ptp_port = ptp_data->portid;
+
+			/* Update kernel time if enabled in app parameters. */
+			if (ptp_data->kernel_time_set == 1)
+				update_kernel_time();
+
+
+
+		}
+	}
+}
+
+/* This function processes PTP packets, implementing slave PTP IEEE1588 L2
+ * functionality.
+ */
+static void
+parse_ptp_frames(uint8_t portid, struct rte_mbuf *m) {
+	struct ptp_header *ptp_hdr;
+	struct ether_hdr *eth_hdr;
+	uint16_t eth_type;
+
+	eth_hdr = rte_pktmbuf_mtod(m, struct ether_hdr *);
+	eth_type = rte_be_to_cpu_16(eth_hdr->ether_type);
+
+	if (eth_type == PTP_PROTOCOL) {
+		ptp_data.m = m;
+		ptp_data.portid = portid;
+		ptp_hdr = (struct ptp_header *)(rte_pktmbuf_mtod(m, char *)
+					+ sizeof(struct ether_hdr));
+
+		switch (ptp_hdr->msg_type) {
+		case SYNC:
+			parse_sync(&ptp_data, m->timesync);
+			break;
+		case FOLLOW_UP:
+			parse_fup(&ptp_data);
+			break;
+		case DELAY_RESP:
+			parse_drsp(&ptp_data);
+			print_clock_info(&ptp_data);
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+/*
+ * The lcore main. This is the main thread that does the work, reading from an
+ * input port and writing to an output port.
+ */
+static __attribute__((noreturn)) void
+lcore_main(void)
+{
+	uint8_t portid;
+	unsigned nb_rx;
+	struct rte_mbuf *m;
+
+	/*
+	 * Check that the port is on the same NUMA node as the polling thread
+	 * for best performance.
+	 */
+	printf("\nCore %u Waiting for SYNC packets. [Ctrl+C to quit]\n",
+			rte_lcore_id());
+
+	/* Run until the application is quit or killed. */
+
+	while (1) {
+		/* Read packet from RX queues. */
+		for (portid = 0; portid < ptp_enabled_port_nb; portid++) {
+
+			portid = ptp_enabled_ports[portid];
+			nb_rx = rte_eth_rx_burst(portid, 0, &m, 1);
+
+			if (likely(nb_rx == 0))
+				continue;
+
+			if (m->ol_flags & PKT_RX_IEEE1588_PTP)
+				parse_ptp_frames(portid, m);
+
+			rte_pktmbuf_free(m);
+		}
+	}
+}
+
+static void
+print_usage(const char *prgname)
+{
+	printf("%s [EAL options] -- -p PORTMASK -T VALUE\n"
+		" -T VALUE: 0 - Disable, 1 - Enable Linux Clock"
+		" Synchronization (0 default)\n"
+		" -p PORTMASK: hexadecimal bitmask of ports to configure\n",
+		prgname);
+}
+
+static int
+ptp_parse_portmask(const char *portmask)
+{
+	char *end = NULL;
+	unsigned long pm;
+
+	/* Parse the hexadecimal string. */
+	pm = strtoul(portmask, &end, 16);
+
+	if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
+		return -1;
+
+	if (pm == 0)
+		return -1;
+
+	return pm;
+}
+
+static int
+parse_ptp_kernel(const char *param)
+{
+	char *end = NULL;
+	unsigned long pm;
+
+	/* Parse the hexadecimal string. */
+	pm = strtoul(param, &end, 16);
+
+	if ((param[0] == '\0') || (end == NULL) || (*end != '\0'))
+		return -1;
+	if (pm == 0)
+		return 0;
+
+	return 1;
+}
+
+/* Parse the commandline arguments. */
+static int
+ptp_parse_args(int argc, char **argv)
+{
+	int opt, ret;
+	char **argvopt;
+	int option_index;
+	char *prgname = argv[0];
+	static struct option lgopts[] = { {NULL, 0, 0, 0} };
+
+	argvopt = argv;
+
+	while ((opt = getopt_long(argc, argvopt, "p:T:",
+				  lgopts, &option_index)) != EOF) {
+
+		switch (opt) {
+
+		/* Portmask. */
+		case 'p':
+			ptp_enabled_port_mask = ptp_parse_portmask(optarg);
+			if (ptp_enabled_port_mask == 0) {
+				printf("invalid portmask\n");
+				print_usage(prgname);
+				return -1;
+			}
+			break;
+		/* Time synchronization. */
+		case 'T':
+			ret = parse_ptp_kernel(optarg);
+			if (ret < 0) {
+				print_usage(prgname);
+				return -1;
+			}
+
+			ptp_data.kernel_time_set = ret;
+			break;
+
+		default:
+			print_usage(prgname);
+			return -1;
+		}
+	}
+
+	argv[optind-1] = prgname;
+
+	optind = 0; /* Reset getopt lib. */
+
+	return 0;
+}
+
+/*
+ * The main function, which does initialization and calls the per-lcore
+ * functions.
+ */
+int
+main(int argc, char *argv[])
+{
+	unsigned nb_ports;
+
+	uint8_t portid;
+
+	/* Initialize the Environment Abstraction Layer (EAL). */
+	int ret = rte_eal_init(argc, argv);
+
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
+
+	memset(&ptp_data, '\0', sizeof(struct ptpv2_data_slave_ordinary));
+
+	argc -= ret;
+	argv += ret;
+
+	ret = ptp_parse_args(argc, argv);
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "Error with PTP initialization\n");
+
+	/* Check that there is an even number of ports to send/receive on. */
+	nb_ports = rte_eth_dev_count();
+
+	/* Creates a new mempool in memory to hold the mbufs. */
+	mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", NUM_MBUFS * nb_ports,
+		MBUF_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
+
+	if (mbuf_pool == NULL)
+		rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
+
+	/* Initialize all ports. */
+	for (portid = 0; portid < nb_ports; portid++) {
+		if ((ptp_enabled_port_mask & (1 << portid)) != 0) {
+			if (port_init(portid, mbuf_pool) == 0) {
+				ptp_enabled_ports[ptp_enabled_port_nb] = portid;
+				ptp_enabled_port_nb++;
+			} else {
+				rte_exit(EXIT_FAILURE,
+					 "Cannot init port %"PRIu8 "\n",
+					 portid);
+			}
+		} else
+			printf("Skipping disabled port %u\n", portid);
+	}
+
+	if (ptp_enabled_port_nb == 0) {
+		rte_exit(EXIT_FAILURE,
+			"All available ports are disabled."
+			" Please set portmask.\n");
+	}
+
+	if (rte_lcore_count() > 1)
+		printf("\nWARNING: Too many lcores enabled. Only 1 used.\n");
+
+	/* Call lcore_main on the master core only. */
+	lcore_main();
+
+	return 0;
+}
-- 
2.5.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [dpdk-dev] [PATCH v7 8/8] doc: add a ptpclient sample guide
  2015-11-13 16:09     ` [dpdk-dev] [PATCH v7 " Pablo de Lara
                         ` (6 preceding siblings ...)
  2015-11-13 16:09       ` [dpdk-dev] [PATCH v7 7/8] example: add minimal PTP client Pablo de Lara
@ 2015-11-13 16:09       ` Pablo de Lara
  2015-11-13 16:28       ` [dpdk-dev] [PATCH v7 0/8] add sample ptp slave application Thomas Monjalon
  2015-11-13 16:49       ` Thomas Monjalon
  9 siblings, 0 replies; 84+ messages in thread
From: Pablo de Lara @ 2015-11-13 16:09 UTC (permalink / raw)
  To: dev; +Cc: konstatin.ananyev

From: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>

Add a sample app guide for the ptpclient application.

Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
Reviewed-by: John McNamara <john.mcnamara@intel.com>
---
 doc/guides/sample_app_ug/img/ptpclient.svg | 528 +++++++++++++++++++++++++++++
 doc/guides/sample_app_ug/index.rst         |   3 +
 doc/guides/sample_app_ug/ptpclient.rst     | 306 +++++++++++++++++
 3 files changed, 837 insertions(+)
 create mode 100644 doc/guides/sample_app_ug/img/ptpclient.svg
 create mode 100644 doc/guides/sample_app_ug/ptpclient.rst

diff --git a/doc/guides/sample_app_ug/img/ptpclient.svg b/doc/guides/sample_app_ug/img/ptpclient.svg
new file mode 100644
index 0000000..fd78ef8
--- /dev/null
+++ b/doc/guides/sample_app_ug/img/ptpclient.svg
@@ -0,0 +1,528 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="88.134132mm"
+   height="96.380394mm"
+   viewBox="0 0 312.28629 341.50533"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="ptpclient.svg">
+  <defs
+     id="defs4">
+    <marker
+       inkscape:stockid="Arrow2Mend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow2Mend"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4256"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="scale(-0.6,-0.6)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Lstart"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow2Lstart"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4247"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="matrix(1.1,0,0,1.1,1.1,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible"
+       id="marker6746"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="Arrow2Lend">
+      <path
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         id="path6748"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker6526"
+       style="overflow:visible"
+       inkscape:isstock="true"
+       inkscape:collect="always">
+      <path
+         id="path6528"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker6276"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path6278"
+         d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(-0.8,0,0,-0.8,-10,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible"
+       id="marker6128"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="DotM">
+      <path
+         transform="matrix(0.4,0,0,0.4,2.96,0.4)"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
+         id="path6130"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible"
+       id="marker6022"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="Arrow2Lend"
+       inkscape:collect="always">
+      <path
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         id="path6024"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible"
+       id="marker5922"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="DotM">
+      <path
+         transform="matrix(0.4,0,0,0.4,2.96,0.4)"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
+         id="path5924"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow2Lend"
+       style="overflow:visible"
+       inkscape:isstock="true"
+       inkscape:collect="always">
+      <path
+         id="path4250"
+         style="fill:#0000ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="DotM"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker5676"
+       style="overflow:visible"
+       inkscape:isstock="true"
+       inkscape:collect="always">
+      <path
+         id="path5678"
+         d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
+         style="fill:#0000ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(0.4,0,0,0.4,2.96,0.4)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lstart"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker5600"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path5602"
+         d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(0.8,0,0,0.8,10,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible"
+       id="marker5510"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="DotM"
+       inkscape:collect="always">
+      <path
+         transform="matrix(0.4,0,0,0.4,2.96,0.4)"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
+         id="path5512"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker4651"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4653"
+         d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(-0.8,0,0,-0.8,-10,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="marker4605"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4607"
+         d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(-0.8,0,0,-0.8,-10,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow1Lend"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4232"
+         d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(-0.8,0,0,-0.8,-10,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="DotM"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="DotM"
+       style="overflow:visible"
+       inkscape:isstock="true"
+       inkscape:collect="always">
+      <path
+         id="path4293"
+         d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(0.4,0,0,0.4,2.96,0.4)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lstart"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow1Lstart"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4229"
+         d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         transform="matrix(0.8,0,0,0.8,10,0)"
+         inkscape:connector-curvature="0" />
+    </marker>
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3402"
+       is_visible="true" />
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3398"
+       is_visible="true" />
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3394"
+       is_visible="true" />
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3378"
+       is_visible="true" />
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3374"
+       is_visible="true" />
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3370"
+       is_visible="true" />
+    <inkscape:path-effect
+       effect="spiro"
+       id="path-effect3366"
+       is_visible="true" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1.06"
+     inkscape:cx="246.81474"
+     inkscape:cy="136.37428"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     showguides="false"
+     inkscape:window-width="1920"
+     inkscape:window-height="1053"
+     inkscape:window-x="94"
+     inkscape:window-y="69"
+     inkscape:window-maximized="1"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-33.920555,-581.05015)">
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.30149066;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="m 64.094156,600.92012 c 0,49.42495 0,321.26225 0,321.26225"
+       id="path3388"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.47424495;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="m 291.64401,600.59617 0,321.95932"
+       id="path3390"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.28789771;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#DotM);marker-end:url(#marker6746)"
+       d="M 64.094156,623.68474 289.7829,665.38811"
+       id="path4223"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.28789771;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#marker5510);marker-end:url(#marker6526)"
+       d="M 63.199944,683.08006 290.44233,725.29448"
+       id="path5274"
+       inkscape:connector-curvature="0" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="295.22058"
+       y="665.10974"
+       id="text5424"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan5426"
+         x="295.22058"
+         y="665.10974">T2</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#800080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="242.44649"
+       y="657.08087"
+       id="text5428"
+       sodipodi:linespacing="100%"
+       transform="matrix(0.98289795,0.18415108,-0.18415108,0.98289795,0,0)"><tspan
+         sodipodi:role="line"
+         id="tspan5430"
+         x="242.44649"
+         y="657.08087"
+         style="font-size:17.5px">FOLLOW UP:T1</tspan></text>
+    <path
+       style="fill:#0000ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.28789771;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#marker5676);marker-end:url(#Arrow2Lend)"
+       d="M 291.5001,774.36878 64.400801,816.4401"
+       id="path5586"
+       inkscape:connector-curvature="0" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="-44.967201"
+       y="809.64362"
+       id="text5898"
+       sodipodi:linespacing="100%"
+       transform="matrix(0.98291896,-0.18403889,0.18403889,0.98291896,0,0)"><tspan
+         sodipodi:role="line"
+         id="tspan5900"
+         x="-44.967201"
+         y="809.64362"
+         style="font-size:17.5px">DELAY REQUEST</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="297.08099"
+       y="774.7981"
+       id="text5902"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan5904"
+         x="297.08099"
+         y="774.7981">T3</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#800080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="35.20845"
+       y="817.29871"
+       id="text5906"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan5908"
+         x="35.20845"
+         y="817.29871">T4</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#800080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="33.920555"
+       y="623.68475"
+       id="text5910"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan5912"
+         x="33.920555"
+         y="623.68475">T1</tspan></text>
+    <path
+       style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.28789771;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#marker6128);marker-end:url(#marker6022)"
+       d="M 63.971502,838.62056 290.6415,881.55049"
+       id="path5914"
+       inkscape:connector-curvature="0" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#800080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="223.59686"
+       y="811.73932"
+       id="text6858"
+       sodipodi:linespacing="100%"
+       transform="matrix(0.98367106,0.17997568,-0.17997568,0.98367106,0,0)"><tspan
+         sodipodi:role="line"
+         id="tspan6860"
+         x="223.59686"
+         y="811.73932"
+         style="font-size:17.5px">DELAY RESPONSE:T4</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.39868927px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="698.62549"
+       y="-320.85892"
+       id="text6862"
+       sodipodi:linespacing="100%"
+       transform="matrix(0.0147127,0.99989176,-0.99989176,0.0147127,0,0)"><tspan
+         sodipodi:role="line"
+         id="tspan6864"
+         x="698.62549"
+         y="-320.85892">time</tspan></text>
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2.1464963;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow2Mend)"
+       d="m 337.72657,748.13938 0,66.11208"
+       id="path6866"
+       inkscape:connector-curvature="0" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.42257881px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#800080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="38.764343"
+       y="590.47479"
+       id="text7094"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan7096"
+         x="38.764343"
+         y="590.47479">master</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:47.51625061px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="571.69812"
+       y="61.796165"
+       id="text7098"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan7100"
+         x="571.69812"
+         y="61.796165" /></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.42257881px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="271.23392"
+       y="593.71478"
+       id="text7102"
+       sodipodi:linespacing="100%"><tspan
+         sodipodi:role="line"
+         id="tspan7104"
+         x="271.23392"
+         y="593.71478">slave</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.3917141px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#800080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="244.40062"
+       y="602.10443"
+       id="text4317"
+       sodipodi:linespacing="125%"
+       transform="matrix(0.98605316,0.16643065,-0.16643065,0.98605316,0,0)"><tspan
+         sodipodi:role="line"
+         id="tspan4319"
+         x="244.40062"
+         y="602.10443"
+         style="font-size:17.5px">SYNC</tspan></text>
+  </g>
+</svg>
diff --git a/doc/guides/sample_app_ug/index.rst b/doc/guides/sample_app_ug/index.rst
index 9beedd9..8ae86c0 100644
--- a/doc/guides/sample_app_ug/index.rst
+++ b/doc/guides/sample_app_ug/index.rst
@@ -73,6 +73,7 @@ Sample Applications User Guide
     vm_power_management
     tep_termination
     proc_info
+    ptpclient
 
 **Figures**
 
@@ -136,6 +137,8 @@ Sample Applications User Guide
 :numref:`figure_overlay_networking` :ref:`figure_overlay_networking`
 :numref:`figure_tep_termination_arch` :ref:`figure_tep_termination_arch`
 
+:numref:`figure_ptpclient_highlevel` :ref:`figure_ptpclient_highlevel`
+
 **Tables**
 
 :numref:`table_qos_metering_1` :ref:`table_qos_metering_1`
diff --git a/doc/guides/sample_app_ug/ptpclient.rst b/doc/guides/sample_app_ug/ptpclient.rst
new file mode 100644
index 0000000..6e425b7
--- /dev/null
+++ b/doc/guides/sample_app_ug/ptpclient.rst
@@ -0,0 +1,306 @@
+..  BSD LICENSE
+    Copyright(c) 2015 Intel Corporation. All rights reserved.
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions
+    are met:
+
+    * Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.
+    * Neither the name of Intel Corporation nor the names of its
+    contributors may be used to endorse or promote products derived
+    from this software without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+    OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+PTP Client Sample Application
+=============================
+
+The PTP (Precision Time Protocol) client sample application is a simple
+example of using the DPDK IEEE1588 API to communicate with a PTP master clock
+to synchronize the time on the NIC and, optionally, on the Linux system.
+
+Note, PTP is a time syncing protocol and cannot be used within DPDK as a
+time-stamping mechanism. See the following for an explanation of the protocol:
+`Precision Time Protocol
+<https://en.wikipedia.org/wiki/Precision_Time_Protocol>`_.
+
+
+Limitations
+-----------
+
+The PTP sample application is intended as a simple reference implementation of
+a PTP client using the DPDK IEEE1588 API.
+In order to keep the application simple the following assumptions are made:
+
+* The first discovered master is the master for the session.
+* Only L2 PTP packets are supported.
+* Only the PTP v2 protocol is supported.
+* Only the slave clock is implemented.
+
+
+How the Application Works
+-------------------------
+
+.. _figure_ptpclient_highlevel:
+
+.. figure:: img/ptpclient.*
+
+   PTP Synchronization Protocol
+
+The PTP synchronization in the sample application works as follows:
+
+* Master sends *Sync* message - the slave saves it as T2.
+* Master sends *Follow Up* message and sends time of T1.
+* Slave sends *Delay Request* frame to PTP Master and stores T3.
+* Master sends *Delay Response* T4 time which is time of received T3.
+
+The adjustment for slave can be represented as:
+
+   adj = -[(T2-T1)-(T4 - T3)]/2
+
+If the command line parameter ``-T 1`` is used the application also
+synchronizes the PTP PHC clock with the Linux kernel clock.
+
+
+Compiling the Application
+-------------------------
+
+To compile the application, export the path to the DPDK source tree and edit
+the ``config/common_linuxapp`` configuration file to enable IEEE1588:
+
+.. code-block:: console
+
+    export RTE_SDK=/path/to/rte_sdk
+
+    # Edit  common_linuxapp and set the following options:
+    CONFIG_RTE_LIBRTE_IEEE1588=y
+
+Set the target, for example:
+
+.. code-block:: console
+
+    export RTE_TARGET=x86_64-native-linuxapp-gcc
+
+See the *DPDK Getting Started* Guide for possible ``RTE_TARGET`` values.
+
+Build the application as follows:
+
+.. code-block:: console
+
+    # Recompile DPDK.
+    make install T=$RTE_TARGET
+
+    # Compile the application.
+    cd ${RTE_SDK}/examples/ptpclient
+    make
+
+
+Running the Application
+-----------------------
+
+To run the example in a ``linuxapp`` environment:
+
+.. code-block:: console
+
+    ./build/ptpclient -c 2 -n 4 -- -p 0x1 -T 0
+
+Refer to *DPDK Getting Started Guide* for general information on running
+applications and the Environment Abstraction Layer (EAL) options.
+
+* ``-p portmask``: Hexadecimal portmask.
+* ``-T 0``: Update only the PTP slave clock.
+* ``-T 1``: Update the PTP slave clock and synchronize the Linux Kernel to the PTP clock.
+
+
+Code Explanation
+----------------
+
+The following sections provide an explanation of the main components of the
+code.
+
+All DPDK library functions used in the sample code are prefixed with ``rte_``
+and are explained in detail in the *DPDK API Documentation*.
+
+
+The Main Function
+~~~~~~~~~~~~~~~~~
+
+The ``main()`` function performs the initialization and calls the execution
+threads for each lcore.
+
+The first task is to initialize the Environment Abstraction Layer (EAL).  The
+``argc`` and ``argv`` arguments are provided to the ``rte_eal_init()``
+function. The value returned is the number of parsed arguments:
+
+.. code-block:: c
+
+    int ret = rte_eal_init(argc, argv);
+    if (ret < 0)
+        rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
+
+And than we parse application specific arguments
+
+.. code-block:: c
+
+    argc -= ret;
+    argv += ret;
+
+    ret = ptp_parse_args(argc, argv);
+    if (ret < 0)
+        rte_exit(EXIT_FAILURE, "Error with PTP initialization\n");
+
+The ``main()`` also allocates a mempool to hold the mbufs (Message Buffers)
+used by the application:
+
+.. code-block:: c
+
+    mbuf_pool = rte_mempool_create("MBUF_POOL",
+                                   NUM_MBUFS * nb_ports,
+                                   MBUF_SIZE,
+                                   MBUF_CACHE_SIZE,
+                                   sizeof(struct rte_pktmbuf_pool_private),
+                                   rte_pktmbuf_pool_init, NULL,
+                                   rte_pktmbuf_init,      NULL,
+                                   rte_socket_id(),
+                                   0);
+
+Mbufs are the packet buffer structure used by DPDK. They are explained in
+detail in the "Mbuf Library" section of the *DPDK Programmer's Guide*.
+
+The ``main()`` function also initializes all the ports using the user defined
+``port_init()`` function with portmask provided by user:
+
+.. code-block:: c
+
+    for (portid = 0; portid < nb_ports; portid++)
+        if ((ptp_enabled_port_mask & (1 << portid)) != 0) {
+
+            if (port_init(portid, mbuf_pool) == 0) {
+                ptp_enabled_ports[ptp_enabled_port_nb] = portid;
+                ptp_enabled_port_nb++;
+            } else {
+                rte_exit(EXIT_FAILURE, "Cannot init port %"PRIu8 "\n",
+                        portid);
+            }
+        }
+
+
+Once the initialization is complete, the application is ready to launch a
+function on an lcore. In this example ``lcore_main()`` is called on a single
+lcore.
+
+.. code-block:: c
+
+	lcore_main();
+
+The ``lcore_main()`` function is explained below.
+
+
+The Lcores Main
+~~~~~~~~~~~~~~~
+
+As we saw above the ``main()`` function calls an application function on the
+available lcores.
+
+The main work of the application is done within the loop:
+
+.. code-block:: c
+
+        for (portid = 0; portid < ptp_enabled_port_nb; portid++) {
+
+            portid = ptp_enabled_ports[portid];
+            nb_rx = rte_eth_rx_burst(portid, 0, &m, 1);
+
+            if (likely(nb_rx == 0))
+                continue;
+
+            if (m->ol_flags & PKT_RX_IEEE1588_PTP)
+                parse_ptp_frames(portid, m);
+
+            rte_pktmbuf_free(m);
+        }
+
+Packets are received one by one on the RX ports and, if required, PTP response
+packets are transmitted on the TX ports.
+
+If the offload flags in the mbuf indicate that the packet is a PTP packet then
+the packet is parsed to determine which type:
+
+.. code-block:: c
+
+            if (m->ol_flags & PKT_RX_IEEE1588_PTP)
+                 parse_ptp_frames(portid, m);
+
+
+All packets are freed explicitly using ``rte_pktmbuf_free()``.
+
+The forwarding loop can be interrupted and the application closed using
+``Ctrl-C``.
+
+
+PTP parsing
+~~~~~~~~~~~
+
+The ``parse_ptp_frames()`` function processes PTP packets, implementing slave
+PTP IEEE1588 L2 functionality.
+
+.. code-block:: c
+
+    void
+    parse_ptp_frames(uint8_t portid, struct rte_mbuf *m) {
+        struct ptp_header *ptp_hdr;
+        struct ether_hdr *eth_hdr;
+        uint16_t eth_type;
+
+        eth_hdr = rte_pktmbuf_mtod(m, struct ether_hdr *);
+        eth_type = rte_be_to_cpu_16(eth_hdr->ether_type);
+
+        if (eth_type == PTP_PROTOCOL) {
+            ptp_data.m = m;
+            ptp_data.portid = portid;
+            ptp_hdr = (struct ptp_header *)(rte_pktmbuf_mtod(m, char *)
+                        + sizeof(struct ether_hdr));
+
+            switch (ptp_hdr->msgtype) {
+            case SYNC:
+                parse_sync(&ptp_data);
+                break;
+            case FOLLOW_UP:
+                parse_fup(&ptp_data);
+                break;
+            case DELAY_RESP:
+                parse_drsp(&ptp_data);
+                print_clock_info(&ptp_data);
+                break;
+            default:
+                break;
+            }
+        }
+    }
+
+There are 3 types of packets on the RX path which we must parse to create a minimal
+implementation of the PTP slave client:
+
+* SYNC packet.
+* FOLLOW UP packet
+* DELAY RESPONSE packet.
+
+When we parse the *FOLLOW UP* packet we also create and send a *DELAY_REQUEST* packet.
+Also when we parse the *DELAY RESPONSE* packet, and all conditions are met we adjust the PTP slave clock.
-- 
2.5.0

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [dpdk-dev] [PATCH v7 0/8] add sample ptp slave application
  2015-11-13 16:09     ` [dpdk-dev] [PATCH v7 " Pablo de Lara
                         ` (7 preceding siblings ...)
  2015-11-13 16:09       ` [dpdk-dev] [PATCH v7 8/8] doc: add a ptpclient sample guide Pablo de Lara
@ 2015-11-13 16:28       ` Thomas Monjalon
  2015-11-13 16:38         ` De Lara Guarch, Pablo
  2015-11-13 16:49       ` Thomas Monjalon
  9 siblings, 1 reply; 84+ messages in thread
From: Thomas Monjalon @ 2015-11-13 16:28 UTC (permalink / raw)
  To: Pablo de Lara; +Cc: dev, konstatin.ananyev

2015-11-13 16:09, Pablo de Lara:
> V6->v7:
>  - Simplified common functionality for timecounters and make it more generic.

So you chose to drop the read() callback?
I think it's better. What was the benefit of having it?

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [dpdk-dev] [PATCH v7 0/8] add sample ptp slave application
  2015-11-13 16:28       ` [dpdk-dev] [PATCH v7 0/8] add sample ptp slave application Thomas Monjalon
@ 2015-11-13 16:38         ` De Lara Guarch, Pablo
  0 siblings, 0 replies; 84+ messages in thread
From: De Lara Guarch, Pablo @ 2015-11-13 16:38 UTC (permalink / raw)
  To: Thomas Monjalon; +Cc: dev



> -----Original Message-----
> From: Thomas Monjalon [mailto:thomas.monjalon@6wind.com]
> Sent: Friday, November 13, 2015 4:29 PM
> To: De Lara Guarch, Pablo
> Cc: dev@dpdk.org; konstatin.ananyev@intel.com
> Subject: Re: [PATCH v7 0/8] add sample ptp slave application
> 
> 2015-11-13 16:09, Pablo de Lara:
> > V6->v7:
> >  - Simplified common functionality for timecounters and make it more
> generic.
> 
> So you chose to drop the read() callback?
> I think it's better. What was the benefit of having it?

Yes, I looked at the structure and functions differently,
with the intention of making them more independent of the IEEE1588 code,
so I removed all references to it (the read() function needed an argument with the device information).

It was just another approach, but if I wanted to take that out,
I had to change slightly the input parameters of the functions,
so now they accept an external read (cleaner indeed).

Thanks for the comments.
Pablo

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [dpdk-dev] [PATCH v7 0/8] add sample ptp slave application
  2015-11-13 16:09     ` [dpdk-dev] [PATCH v7 " Pablo de Lara
                         ` (8 preceding siblings ...)
  2015-11-13 16:28       ` [dpdk-dev] [PATCH v7 0/8] add sample ptp slave application Thomas Monjalon
@ 2015-11-13 16:49       ` Thomas Monjalon
  9 siblings, 0 replies; 84+ messages in thread
From: Thomas Monjalon @ 2015-11-13 16:49 UTC (permalink / raw)
  To: Pablo de Lara; +Cc: dev

2015-11-13 16:09, Pablo de Lara:
> Daniel Mrzyglod (5):
>   ethdev: add additional ieee1588 support functions
>   eal: add common time structures and functions
>   ixgbe: add additional ieee1588 support functions
>   example: minimal ptp client implementation
>   doc: add a ptpclient sample guide
> 
> Pablo de Lara (3):
>   igb: add additional ieee1588 support functions
>   i40e: add additional ieee1588 support functions
>   testpmd: add nanosecond output for ieee1588 fwd

Applied, thanks

^ permalink raw reply	[flat|nested] 84+ messages in thread

end of thread, other threads:[~2015-11-13 16:51 UTC | newest]

Thread overview: 84+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-10-02 15:20 [dpdk-dev] [PATCH 0/3] add sample ptp slave application Daniel Mrzyglod
2015-10-02 15:20 ` [dpdk-dev] [PATCH 1/3] ethdev: add additional ieee1588 support functions Daniel Mrzyglod
2015-10-02 15:20 ` [dpdk-dev] [PATCH 2/3] ixgbe: " Daniel Mrzyglod
2015-10-02 15:20 ` [dpdk-dev] [PATCH 3/3] example: PTP client slave minimal implementation Daniel Mrzyglod
2015-10-30  9:43 ` [dpdk-dev] [PATCH v2 0/6] add sample ptp slave application Daniel Mrzyglod
2015-10-30  9:43   ` [dpdk-dev] [PATCH v2 1/6] ethdev: add additional ieee1588 support functions Daniel Mrzyglod
2015-10-30  9:43   ` [dpdk-dev] [PATCH v2 2/6] ixgbe: " Daniel Mrzyglod
2015-10-30  9:43   ` [dpdk-dev] [PATCH v2 3/6] igb: " Daniel Mrzyglod
2015-10-30  9:43   ` [dpdk-dev] [PATCH v2 4/6] i40e: " Daniel Mrzyglod
2015-10-30 11:19     ` Ananyev, Konstantin
2015-10-30 11:33       ` De Lara Guarch, Pablo
2015-10-30 11:36         ` Thomas Monjalon
2015-10-30 11:38         ` Ananyev, Konstantin
2015-10-30  9:43   ` [dpdk-dev] [PATCH v2 5/6] example: PTP client slave minimal implementation Daniel Mrzyglod
2015-10-30  9:43   ` [dpdk-dev] [PATCH v2 6/6] doc: add a PTPCLIENT sample guide Daniel Mrzyglod
2015-10-30 11:23   ` [dpdk-dev] [PATCH v2 0/6] add sample ptp slave application Mcnamara, John
2015-11-03 16:38 ` [dpdk-dev] [PATCH v3 0/7] " Daniel Mrzyglod
2015-11-03 16:38   ` [dpdk-dev] [PATCH v3 1/7] ethdev: add additional ieee1588 support functions Daniel Mrzyglod
2015-11-03 16:38   ` [dpdk-dev] [PATCH v3 2/7] net: Add common PTP structures and functions Daniel Mrzyglod
2015-11-03 16:38   ` [dpdk-dev] [PATCH v3 3/7] ixgbe: add additional ieee1588 support functions Daniel Mrzyglod
2015-11-03 16:38   ` [dpdk-dev] [PATCH v3 4/7] igb: " Daniel Mrzyglod
2015-11-03 16:38   ` [dpdk-dev] [PATCH v3 5/7] i40e: " Daniel Mrzyglod
2015-11-03 16:38   ` [dpdk-dev] [PATCH v3 6/7] example: PTP client slave minimal implementation Daniel Mrzyglod
2015-11-03 20:06     ` De Lara Guarch, Pablo
2015-11-03 16:38   ` [dpdk-dev] [PATCH v3 7/7] doc: add a PTPCLIENT sample guide Daniel Mrzyglod
2015-11-04 10:06 ` [dpdk-dev] [PATCH v4 0/7] add sample ptp slave application Daniel Mrzyglod
2015-11-04 10:06   ` [dpdk-dev] [PATCH v4 1/7] ethdev: add additional ieee1588 support functions Daniel Mrzyglod
2015-11-04 10:06   ` [dpdk-dev] [PATCH v4 2/7] net: Add common PTP structures and functions Daniel Mrzyglod
2015-11-04 10:06   ` [dpdk-dev] [PATCH v4 3/7] ixgbe: add additional ieee1588 support functions Daniel Mrzyglod
2015-11-04 10:06   ` [dpdk-dev] [PATCH v4 4/7] igb: " Daniel Mrzyglod
2015-11-04 10:06   ` [dpdk-dev] [PATCH v4 5/7] i40e: " Daniel Mrzyglod
2015-11-04 10:06   ` [dpdk-dev] [PATCH v4 6/7] example: PTP client slave minimal implementation Daniel Mrzyglod
2015-11-04 10:06   ` [dpdk-dev] [PATCH v4 7/7] doc: add a PTPCLIENT sample guide Daniel Mrzyglod
2015-11-05 12:46   ` [dpdk-dev] [PATCH v4 0/7] add sample ptp slave application Mcnamara, John
2015-11-05 15:17     ` Thomas Monjalon
2015-11-05 16:08       ` Mrzyglod, DanielX T
2015-11-05 13:37   ` Mcnamara, John
2015-11-05 14:05 ` [dpdk-dev] [PATCH v5 " Daniel Mrzyglod
2015-11-05 14:06   ` [dpdk-dev] [PATCH v5 1/7] ethdev: add additional ieee1588 support functions Daniel Mrzyglod
2015-11-10 11:03     ` Thomas Monjalon
2015-11-10 11:36       ` Mcnamara, John
2015-11-10 11:58         ` Thomas Monjalon
2015-11-10 14:12           ` Mcnamara, John
2015-11-10 14:16             ` Thomas Monjalon
2015-11-10 15:18             ` Liu, Yong
2015-11-11  1:40               ` Cao, Waterman
2015-11-05 14:06   ` [dpdk-dev] [PATCH v5 2/7] net: Add common PTP structures and functions Daniel Mrzyglod
2015-11-10 11:25     ` Thomas Monjalon
2015-11-11 10:45       ` Mcnamara, John
2015-11-11 11:24         ` Thomas Monjalon
2015-11-05 14:06   ` [dpdk-dev] [PATCH v5 3/7] ixgbe: add additional ieee1588 support functions Daniel Mrzyglod
2015-11-05 14:06   ` [dpdk-dev] [PATCH v5 4/7] igb: " Daniel Mrzyglod
2015-11-05 14:06   ` [dpdk-dev] [PATCH v5 5/7] i40e: " Daniel Mrzyglod
2015-11-05 14:06   ` [dpdk-dev] [PATCH v5 6/7] example: PTP client slave minimal implementation Daniel Mrzyglod
2015-11-05 14:06   ` [dpdk-dev] [PATCH v5 7/7] doc: add a PTPCLIENT sample guide Daniel Mrzyglod
2015-11-05 14:10   ` [dpdk-dev] [PATCH v5 0/7] add sample ptp slave application Mrzyglod, DanielX T
2015-11-05 14:30     ` Mcnamara, John
2015-11-12 12:55   ` [dpdk-dev] [PATCH v6 0/8] " Pablo de Lara
2015-11-12 12:55     ` [dpdk-dev] [PATCH v6 1/8] ethdev: add additional ieee1588 support functions Pablo de Lara
2015-11-12 12:55     ` [dpdk-dev] [PATCH v6 2/8] eal: add common time structures and functions Pablo de Lara
2015-11-12 12:55     ` [dpdk-dev] [PATCH v6 3/8] ixgbe: add additional ieee1588 support functions Pablo de Lara
2015-11-12 12:55     ` [dpdk-dev] [PATCH v6 4/8] igb: " Pablo de Lara
2015-11-12 12:55     ` [dpdk-dev] [PATCH v6 5/8] i40e: " Pablo de Lara
2015-11-12 12:55     ` [dpdk-dev] [PATCH v6 6/8] testpmd: add nanosecond output for ieee1588 fwd Pablo de Lara
2015-11-12 12:55     ` [dpdk-dev] [PATCH v6 7/8] example: minimal ptp client implementation Pablo de Lara
2015-11-12 12:55     ` [dpdk-dev] [PATCH v6 8/8] doc: add a ptpclient sample guide Pablo de Lara
2015-11-13 14:38       ` Thomas Monjalon
2015-11-13 14:58         ` De Lara Guarch, Pablo
2015-11-13 15:10           ` Thomas Monjalon
2015-11-13 15:15             ` De Lara Guarch, Pablo
2015-11-13 15:19               ` Thomas Monjalon
2015-11-12 13:20     ` [dpdk-dev] [PATCH v6 0/8] add sample ptp slave application Mcnamara, John
2015-11-13 16:09     ` [dpdk-dev] [PATCH v7 " Pablo de Lara
2015-11-13 16:09       ` [dpdk-dev] [PATCH v7 1/8] ethdev: add ieee1588 functions for device clock time Pablo de Lara
2015-11-13 16:09       ` [dpdk-dev] [PATCH v7 2/8] eal: add helpers for time conversions Pablo de Lara
2015-11-13 16:09       ` [dpdk-dev] [PATCH v7 3/8] ixgbe: support ieee1588 functions for device time Pablo de Lara
2015-11-13 16:09       ` [dpdk-dev] [PATCH v7 4/8] igb: " Pablo de Lara
2015-11-13 16:09       ` [dpdk-dev] [PATCH v7 5/8] i40e: " Pablo de Lara
2015-11-13 16:09       ` [dpdk-dev] [PATCH v7 6/8] testpmd: add nanosecond output for ieee1588 Pablo de Lara
2015-11-13 16:09       ` [dpdk-dev] [PATCH v7 7/8] example: add minimal PTP client Pablo de Lara
2015-11-13 16:09       ` [dpdk-dev] [PATCH v7 8/8] doc: add a ptpclient sample guide Pablo de Lara
2015-11-13 16:28       ` [dpdk-dev] [PATCH v7 0/8] add sample ptp slave application Thomas Monjalon
2015-11-13 16:38         ` De Lara Guarch, Pablo
2015-11-13 16:49       ` Thomas Monjalon

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).