Skip to content

Commit 2354bf2

Browse files
committed
iavf: add support for indirect access to PHC time
JIRA: https://issues.redhat.com/browse/RHEL-83568 commit 52e3bea Author: Jacob Keller <jacob.e.keller@intel.com> Date: Wed Nov 6 12:37:24 2024 -0500 iavf: add support for indirect access to PHC time Implement support for reading the PHC time indirectly via the VIRTCHNL_OP_1588_PTP_GET_TIME operation. Based on some simple tests with ftrace, the latency of the indirect clock access appears to be about ~110 microseconds. This is due to the cost of preparing a message to send over the virtchnl queue. This is expected, due to the increased jitter caused by sending messages over virtchnl. It is not easy to control the precise time that the message is sent by the VF, or the time that the message is responded to by the PF, or the time that the message sent from the PF is received by the VF. For sending the request, note that many PTP related operations will require sending of VIRTCHNL messages. Instead of adding a separate AQ flag and storage for each operation, setup a simple queue mechanism for queuing up virtchnl messages. Each message will be converted to a iavf_ptp_aq_cmd structure which ends with a flexible array member. A single AQ flag is added for processing messages from this queue. In principle this could be extended to handle arbitrary virtchnl messages. For now it is kept to PTP-specific as the need is primarily for handling PTP-related commands. Use this to implement .gettimex64 using the indirect method via the virtchnl command. The response from the PF is processed and stored into the cached_phc_time. A wait queue is used to allow the PTP clock gettime request to sleep until the message is sent from the PF. Signed-off-by: Jacob Keller <jacob.e.keller@intel.com> Reviewed-by: Rahul Rameshbabu <rrameshbabu@nvidia.com> Reviewed-by: Simon Horman <horms@kernel.org> Tested-by: Rafal Romanowski <rafal.romanowski@intel.com> Signed-off-by: Mateusz Polchlopek <mateusz.polchlopek@intel.com> Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com> Signed-off-by: Michal Schmidt <mschmidt@redhat.com>
1 parent 2b314aa commit 2354bf2

File tree

4 files changed

+257
-2
lines changed

4 files changed

+257
-2
lines changed

drivers/net/ethernet/intel/iavf/iavf_main.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2269,7 +2269,10 @@ static int iavf_process_aq_command(struct iavf_adapter *adapter)
22692269
iavf_enable_vlan_insertion_v2(adapter, ETH_P_8021AD);
22702270
return 0;
22712271
}
2272-
2272+
if (adapter->aq_required & IAVF_FLAG_AQ_SEND_PTP_CMD) {
2273+
iavf_virtchnl_send_ptp_cmd(adapter);
2274+
return IAVF_SUCCESS;
2275+
}
22732276
if (adapter->aq_required & IAVF_FLAG_AQ_REQUEST_STATS) {
22742277
iavf_request_stats(adapter);
22752278
return 0;
@@ -5496,6 +5499,10 @@ static int iavf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
54965499
/* Setup the wait queue for indicating virtchannel events */
54975500
init_waitqueue_head(&adapter->vc_waitqueue);
54985501

5502+
INIT_LIST_HEAD(&adapter->ptp.aq_cmds);
5503+
init_waitqueue_head(&adapter->ptp.phc_time_waitqueue);
5504+
mutex_init(&adapter->ptp.aq_cmd_lock);
5505+
54995506
queue_delayed_work(adapter->wq, &adapter->watchdog_task,
55005507
msecs_to_jiffies(5 * (pdev->devfn & 0x07)));
55015508
/* Initialization goes on in the work. Do not add more of it below. */

drivers/net/ethernet/intel/iavf/iavf_ptp.c

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
#include "iavf.h"
55
#include "iavf_ptp.h"
66

7+
#define iavf_clock_to_adapter(info) \
8+
container_of_const(info, struct iavf_adapter, ptp.info)
9+
710
/**
811
* iavf_ptp_cap_supported - Check if a PTP capability is supported
912
* @adapter: private adapter structure
@@ -21,6 +24,140 @@ bool iavf_ptp_cap_supported(const struct iavf_adapter *adapter, u32 cap)
2124
return (adapter->ptp.hw_caps.caps & cap) == cap;
2225
}
2326

27+
/**
28+
* iavf_allocate_ptp_cmd - Allocate a PTP command message structure
29+
* @v_opcode: the virtchnl opcode
30+
* @msglen: length in bytes of the associated virtchnl structure
31+
*
32+
* Allocates a PTP command message and pre-fills it with the provided message
33+
* length and opcode.
34+
*
35+
* Return: allocated PTP command.
36+
*/
37+
static struct iavf_ptp_aq_cmd *iavf_allocate_ptp_cmd(enum virtchnl_ops v_opcode,
38+
u16 msglen)
39+
{
40+
struct iavf_ptp_aq_cmd *cmd;
41+
42+
cmd = kzalloc(struct_size(cmd, msg, msglen), GFP_KERNEL);
43+
if (!cmd)
44+
return NULL;
45+
46+
cmd->v_opcode = v_opcode;
47+
cmd->msglen = msglen;
48+
49+
return cmd;
50+
}
51+
52+
/**
53+
* iavf_queue_ptp_cmd - Queue PTP command for sending over virtchnl
54+
* @adapter: private adapter structure
55+
* @cmd: the command structure to send
56+
*
57+
* Queue the given command structure into the PTP virtchnl command queue tos
58+
* end to the PF.
59+
*/
60+
static void iavf_queue_ptp_cmd(struct iavf_adapter *adapter,
61+
struct iavf_ptp_aq_cmd *cmd)
62+
{
63+
mutex_lock(&adapter->ptp.aq_cmd_lock);
64+
list_add_tail(&cmd->list, &adapter->ptp.aq_cmds);
65+
mutex_unlock(&adapter->ptp.aq_cmd_lock);
66+
67+
adapter->aq_required |= IAVF_FLAG_AQ_SEND_PTP_CMD;
68+
mod_delayed_work(adapter->wq, &adapter->watchdog_task, 0);
69+
}
70+
71+
/**
72+
* iavf_send_phc_read - Send request to read PHC time
73+
* @adapter: private adapter structure
74+
*
75+
* Send a request to obtain the PTP hardware clock time. This allocates the
76+
* VIRTCHNL_OP_1588_PTP_GET_TIME message and queues it up to send to
77+
* indirectly read the PHC time.
78+
*
79+
* This function does not wait for the reply from the PF.
80+
*
81+
* Return: 0 if success, error code otherwise.
82+
*/
83+
static int iavf_send_phc_read(struct iavf_adapter *adapter)
84+
{
85+
struct iavf_ptp_aq_cmd *cmd;
86+
87+
if (!adapter->ptp.clock)
88+
return -EOPNOTSUPP;
89+
90+
cmd = iavf_allocate_ptp_cmd(VIRTCHNL_OP_1588_PTP_GET_TIME,
91+
sizeof(struct virtchnl_phc_time));
92+
if (!cmd)
93+
return -ENOMEM;
94+
95+
iavf_queue_ptp_cmd(adapter, cmd);
96+
97+
return 0;
98+
}
99+
100+
/**
101+
* iavf_read_phc_indirect - Indirectly read the PHC time via virtchnl
102+
* @adapter: private adapter structure
103+
* @ts: storage for the timestamp value
104+
* @sts: system timestamp values before and after the read
105+
*
106+
* Used when the device does not have direct register access to the PHC time.
107+
* Indirectly reads the time via the VIRTCHNL_OP_1588_PTP_GET_TIME, and waits
108+
* for the reply from the PF.
109+
*
110+
* Based on some simple measurements using ftrace and phc2sys, this clock
111+
* access method has about a ~110 usec latency even when the system is not
112+
* under load. In order to achieve acceptable results when using phc2sys with
113+
* the indirect clock access method, it is recommended to use more
114+
* conservative proportional and integration constants with the P/I servo.
115+
*
116+
* Return: 0 if success, error code otherwise.
117+
*/
118+
static int iavf_read_phc_indirect(struct iavf_adapter *adapter,
119+
struct timespec64 *ts,
120+
struct ptp_system_timestamp *sts)
121+
{
122+
long ret;
123+
int err;
124+
125+
adapter->ptp.phc_time_ready = false;
126+
127+
ptp_read_system_prets(sts);
128+
129+
err = iavf_send_phc_read(adapter);
130+
if (err)
131+
return err;
132+
133+
ret = wait_event_interruptible_timeout(adapter->ptp.phc_time_waitqueue,
134+
adapter->ptp.phc_time_ready,
135+
HZ);
136+
137+
ptp_read_system_postts(sts);
138+
139+
if (ret < 0)
140+
return ret;
141+
else if (!ret)
142+
return -EBUSY;
143+
144+
*ts = ns_to_timespec64(adapter->ptp.cached_phc_time);
145+
146+
return 0;
147+
}
148+
149+
static int iavf_ptp_gettimex64(struct ptp_clock_info *info,
150+
struct timespec64 *ts,
151+
struct ptp_system_timestamp *sts)
152+
{
153+
struct iavf_adapter *adapter = iavf_clock_to_adapter(info);
154+
155+
if (!adapter->ptp.clock)
156+
return -EOPNOTSUPP;
157+
158+
return iavf_read_phc_indirect(adapter, ts, sts);
159+
}
160+
24161
/**
25162
* iavf_ptp_register_clock - Register a new PTP for userspace
26163
* @adapter: private adapter structure
@@ -38,6 +175,7 @@ static int iavf_ptp_register_clock(struct iavf_adapter *adapter)
38175
snprintf(ptp_info->name, sizeof(ptp_info->name), "%s-%s-clk",
39176
KBUILD_MODNAME, dev_name(dev));
40177
ptp_info->owner = THIS_MODULE;
178+
ptp_info->gettimex64 = iavf_ptp_gettimex64;
41179

42180
clock = ptp_clock_register(ptp_info, dev);
43181
if (IS_ERR(clock))
@@ -91,13 +229,24 @@ void iavf_ptp_init(struct iavf_adapter *adapter)
91229
*/
92230
void iavf_ptp_release(struct iavf_adapter *adapter)
93231
{
232+
struct iavf_ptp_aq_cmd *cmd, *tmp;
233+
94234
if (!adapter->ptp.clock)
95235
return;
96236

97237
pci_dbg(adapter->pdev, "removing PTP clock %s\n",
98238
adapter->ptp.info.name);
99239
ptp_clock_unregister(adapter->ptp.clock);
100240
adapter->ptp.clock = NULL;
241+
242+
/* Cancel any remaining uncompleted PTP clock commands */
243+
mutex_lock(&adapter->ptp.aq_cmd_lock);
244+
list_for_each_entry_safe(cmd, tmp, &adapter->ptp.aq_cmds, list) {
245+
list_del(&cmd->list);
246+
kfree(cmd);
247+
}
248+
adapter->aq_required &= ~IAVF_FLAG_AQ_SEND_PTP_CMD;
249+
mutex_unlock(&adapter->ptp.aq_cmd_lock);
101250
}
102251

103252
/**

drivers/net/ethernet/intel/iavf/iavf_ptp.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,17 @@ void iavf_ptp_init(struct iavf_adapter *adapter);
1111
void iavf_ptp_release(struct iavf_adapter *adapter);
1212
void iavf_ptp_process_caps(struct iavf_adapter *adapter);
1313
bool iavf_ptp_cap_supported(const struct iavf_adapter *adapter, u32 cap);
14+
void iavf_virtchnl_send_ptp_cmd(struct iavf_adapter *adapter);
1415
#else /* IS_ENABLED(CONFIG_PTP_1588_CLOCK) */
1516
static inline void iavf_ptp_init(struct iavf_adapter *adapter) { }
1617
static inline void iavf_ptp_release(struct iavf_adapter *adapter) { }
1718
static inline void iavf_ptp_process_caps(struct iavf_adapter *adapter) { }
18-
static inline bool iavf_ptp_cap_supported(struct iavf_adapter *adapter, u32 cap)
19+
static inline bool iavf_ptp_cap_supported(const struct iavf_adapter *adapter,
20+
u32 cap)
1921
{
2022
return false;
2123
}
24+
25+
static inline void iavf_virtchnl_send_ptp_cmd(struct iavf_adapter *adapter) { }
2226
#endif /* IS_ENABLED(CONFIG_PTP_1588_CLOCK) */
2327
#endif /* _IAVF_PTP_H_ */

drivers/net/ethernet/intel/iavf/iavf_virtchnl.c

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1494,6 +1494,67 @@ void iavf_disable_vlan_insertion_v2(struct iavf_adapter *adapter, u16 tpid)
14941494
VIRTCHNL_OP_DISABLE_VLAN_INSERTION_V2);
14951495
}
14961496

1497+
#if IS_ENABLED(CONFIG_PTP_1588_CLOCK)
1498+
/**
1499+
* iavf_virtchnl_send_ptp_cmd - Send one queued PTP command
1500+
* @adapter: adapter private structure
1501+
*
1502+
* De-queue one PTP command request and send the command message to the PF.
1503+
* Clear IAVF_FLAG_AQ_SEND_PTP_CMD if no more messages are left to send.
1504+
*/
1505+
void iavf_virtchnl_send_ptp_cmd(struct iavf_adapter *adapter)
1506+
{
1507+
struct iavf_ptp_aq_cmd *cmd;
1508+
int err;
1509+
1510+
if (!adapter->ptp.clock) {
1511+
/* This shouldn't be possible to hit, since no messages should
1512+
* be queued if PTP is not initialized.
1513+
*/
1514+
pci_err(adapter->pdev, "PTP is not initialized\n");
1515+
adapter->aq_required &= ~IAVF_FLAG_AQ_SEND_PTP_CMD;
1516+
return;
1517+
}
1518+
1519+
mutex_lock(&adapter->ptp.aq_cmd_lock);
1520+
cmd = list_first_entry_or_null(&adapter->ptp.aq_cmds,
1521+
struct iavf_ptp_aq_cmd, list);
1522+
if (!cmd) {
1523+
/* no further PTP messages to send */
1524+
adapter->aq_required &= ~IAVF_FLAG_AQ_SEND_PTP_CMD;
1525+
goto out_unlock;
1526+
}
1527+
1528+
if (adapter->current_op != VIRTCHNL_OP_UNKNOWN) {
1529+
/* bail because we already have a command pending */
1530+
pci_err(adapter->pdev,
1531+
"Cannot send PTP command %d, command %d pending\n",
1532+
cmd->v_opcode, adapter->current_op);
1533+
goto out_unlock;
1534+
}
1535+
1536+
err = iavf_send_pf_msg(adapter, cmd->v_opcode, cmd->msg, cmd->msglen);
1537+
if (!err) {
1538+
/* Command was sent without errors, so we can remove it from
1539+
* the list and discard it.
1540+
*/
1541+
list_del(&cmd->list);
1542+
kfree(cmd);
1543+
} else {
1544+
/* We failed to send the command, try again next cycle */
1545+
pci_err(adapter->pdev, "Failed to send PTP command %d\n",
1546+
cmd->v_opcode);
1547+
}
1548+
1549+
if (list_empty(&adapter->ptp.aq_cmds))
1550+
/* no further PTP messages to send */
1551+
adapter->aq_required &= ~IAVF_FLAG_AQ_SEND_PTP_CMD;
1552+
1553+
out_unlock:
1554+
mutex_unlock(&adapter->ptp.aq_cmd_lock);
1555+
}
1556+
#endif /* IS_ENABLED(CONFIG_PTP_1588_CLOCK) */
1557+
14971558
/**
14981559
* iavf_print_link_message - print link up or down
14991560
* @adapter: adapter structure
@@ -2189,6 +2250,37 @@ static void iavf_activate_fdir_filters(struct iavf_adapter *adapter)
21892250
adapter->aq_required |= IAVF_FLAG_AQ_ADD_FDIR_FILTER;
21902251
}
21912252

2253+
/**
2254+
* iavf_virtchnl_ptp_get_time - Respond to VIRTCHNL_OP_1588_PTP_GET_TIME
2255+
* @adapter: private adapter structure
2256+
* @data: the message from the PF
2257+
* @len: length of the message from the PF
2258+
*
2259+
* Handle the VIRTCHNL_OP_1588_PTP_GET_TIME message from the PF. This message
2260+
* is sent by the PF in response to the same op as a request from the VF.
2261+
* Extract the 64bit nanoseconds time from the message and store it in
2262+
* cached_phc_time. Then, notify any thread that is waiting for the update via
2263+
* the wait queue.
2264+
*/
2265+
static void iavf_virtchnl_ptp_get_time(struct iavf_adapter *adapter,
2266+
void *data, u16 len)
2267+
{
2268+
struct virtchnl_phc_time *msg = data;
2269+
2270+
if (len != sizeof(*msg)) {
2271+
dev_err_once(&adapter->pdev->dev,
2272+
"Invalid VIRTCHNL_OP_1588_PTP_GET_TIME from PF. Got size %u, expected %zu\n",
2273+
len, sizeof(*msg));
2274+
return;
2275+
}
2276+
2277+
adapter->ptp.cached_phc_time = msg->time;
2278+
adapter->ptp.cached_phc_updated = jiffies;
2279+
adapter->ptp.phc_time_ready = true;
2280+
2281+
wake_up(&adapter->ptp.phc_time_waitqueue);
2282+
}
2283+
21922284
/**
21932285
* iavf_virtchnl_completion
21942286
* @adapter: adapter structure
@@ -2617,6 +2709,9 @@ void iavf_virtchnl_completion(struct iavf_adapter *adapter,
26172709
/* process any state change needed due to new capabilities */
26182710
iavf_ptp_process_caps(adapter);
26192711
break;
2712+
case VIRTCHNL_OP_1588_PTP_GET_TIME:
2713+
iavf_virtchnl_ptp_get_time(adapter, msg, msglen);
2714+
break;
26202715
case VIRTCHNL_OP_ENABLE_QUEUES:
26212716
/* enable transmits */
26222717
iavf_irq_enable(adapter, true);

0 commit comments

Comments
 (0)