Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,27 @@
# SPDX-License-Identifier: GPL-2.0-only
# Copyright (C) 2024 Intel Corporation
config IDPF
tristate "Intel(R) Infrastructure Data Path Function Support"
depends on PCI_MSI
depends on PTP_1588_CLOCK_OPTIONAL
select DIMLIB
select LIBETH_XDP
help
This driver supports Intel(R) Infrastructure Data Path Function
devices.
To compile this driver as a module, choose M here. The module
will be called idpf.
if IDPF
config IDPF_SINGLEQ
bool "idpf singleq support"
help
This option enables support for legacy single Rx/Tx queues w/no
completion and fill queues. Only enable if you have hardware which
wants to work in this mode as it increases the driver size and adds
runtme checks on hotpath.
endif # IDPF
@@ -0,0 +1,26 @@
# SPDX-License-Identifier: GPL-2.0-only
# Copyright (C) 2023 Intel Corporation
# Makefile for Intel(R) Infrastructure Data Path Function Linux Driver
obj-$(CONFIG_IDPF) += idpf.o
idpf-y := \
idpf_controlq.o \
idpf_controlq_setup.o \
idpf_dev.o \
idpf_ethtool.o \
idpf_idc.o \
idpf_lib.o \
idpf_main.o \
idpf_txrx.o \
idpf_virtchnl.o \
idpf_vf_dev.o
idpf-$(CONFIG_IDPF_SINGLEQ) += idpf_singleq_txrx.o
idpf-$(CONFIG_PTP_1588_CLOCK) += idpf_ptp.o
idpf-$(CONFIG_PTP_1588_CLOCK) += idpf_virtchnl_ptp.o
idpf-y += xdp.o
idpf-y += xsk.o
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,621 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2023 Intel Corporation */
#include "idpf_controlq.h"
/**
* idpf_ctlq_setup_regs - initialize control queue registers
* @cq: pointer to the specific control queue
* @q_create_info: structs containing info for each queue to be initialized
*/
static void idpf_ctlq_setup_regs(struct idpf_ctlq_info *cq,
struct idpf_ctlq_create_info *q_create_info)
{
/* set control queue registers in our local struct */
cq->reg.head = q_create_info->reg.head;
cq->reg.tail = q_create_info->reg.tail;
cq->reg.len = q_create_info->reg.len;
cq->reg.bah = q_create_info->reg.bah;
cq->reg.bal = q_create_info->reg.bal;
cq->reg.len_mask = q_create_info->reg.len_mask;
cq->reg.len_ena_mask = q_create_info->reg.len_ena_mask;
cq->reg.head_mask = q_create_info->reg.head_mask;
}
/**
* idpf_ctlq_init_regs - Initialize control queue registers
* @hw: pointer to hw struct
* @cq: pointer to the specific Control queue
* @is_rxq: true if receive control queue, false otherwise
*
* Initialize registers. The caller is expected to have already initialized the
* descriptor ring memory and buffer memory
*/
static void idpf_ctlq_init_regs(struct idpf_hw *hw, struct idpf_ctlq_info *cq,
bool is_rxq)
{
/* Update tail to post pre-allocated buffers for rx queues */
if (is_rxq)
idpf_mbx_wr32(hw, cq->reg.tail, (u32)(cq->ring_size - 1));
/* For non-Mailbox control queues only TAIL need to be set */
if (cq->q_id != -1)
return;
/* Clear Head for both send or receive */
idpf_mbx_wr32(hw, cq->reg.head, 0);
/* set starting point */
idpf_mbx_wr32(hw, cq->reg.bal, lower_32_bits(cq->desc_ring.pa));
idpf_mbx_wr32(hw, cq->reg.bah, upper_32_bits(cq->desc_ring.pa));
idpf_mbx_wr32(hw, cq->reg.len, (cq->ring_size | cq->reg.len_ena_mask));
}
/**
* idpf_ctlq_init_rxq_bufs - populate receive queue descriptors with buf
* @cq: pointer to the specific Control queue
*
* Record the address of the receive queue DMA buffers in the descriptors.
* The buffers must have been previously allocated.
*/
static void idpf_ctlq_init_rxq_bufs(struct idpf_ctlq_info *cq)
{
int i;
for (i = 0; i < cq->ring_size; i++) {
struct idpf_ctlq_desc *desc = IDPF_CTLQ_DESC(cq, i);
struct idpf_dma_mem *bi = cq->bi.rx_buff[i];
/* No buffer to post to descriptor, continue */
if (!bi)
continue;
desc->flags =
cpu_to_le16(IDPF_CTLQ_FLAG_BUF | IDPF_CTLQ_FLAG_RD);
desc->opcode = 0;
desc->datalen = cpu_to_le16(bi->size);
desc->ret_val = 0;
desc->v_opcode_dtype = 0;
desc->v_retval = 0;
desc->params.indirect.addr_high =
cpu_to_le32(upper_32_bits(bi->pa));
desc->params.indirect.addr_low =
cpu_to_le32(lower_32_bits(bi->pa));
desc->params.indirect.param0 = 0;
desc->params.indirect.sw_cookie = 0;
desc->params.indirect.v_flags = 0;
}
}
/**
* idpf_ctlq_shutdown - shutdown the CQ
* @hw: pointer to hw struct
* @cq: pointer to the specific Control queue
*
* The main shutdown routine for any controq queue
*/
static void idpf_ctlq_shutdown(struct idpf_hw *hw, struct idpf_ctlq_info *cq)
{
spin_lock(&cq->cq_lock);
/* free ring buffers and the ring itself */
idpf_ctlq_dealloc_ring_res(hw, cq);
/* Set ring_size to 0 to indicate uninitialized queue */
cq->ring_size = 0;
spin_unlock(&cq->cq_lock);
}
/**
* idpf_ctlq_add - add one control queue
* @hw: pointer to hardware struct
* @qinfo: info for queue to be created
* @cq_out: (output) double pointer to control queue to be created
*
* Allocate and initialize a control queue and add it to the control queue list.
* The cq parameter will be allocated/initialized and passed back to the caller
* if no errors occur.
*
* Note: idpf_ctlq_init must be called prior to any calls to idpf_ctlq_add
*/
int idpf_ctlq_add(struct idpf_hw *hw,
struct idpf_ctlq_create_info *qinfo,
struct idpf_ctlq_info **cq_out)
{
struct idpf_ctlq_info *cq;
bool is_rxq = false;
int err;
cq = kzalloc_obj(*cq);
if (!cq)
return -ENOMEM;
cq->cq_type = qinfo->type;
cq->q_id = qinfo->id;
cq->buf_size = qinfo->buf_size;
cq->ring_size = qinfo->len;
cq->next_to_use = 0;
cq->next_to_clean = 0;
cq->next_to_post = cq->ring_size - 1;
switch (qinfo->type) {
case IDPF_CTLQ_TYPE_MAILBOX_RX:
is_rxq = true;
fallthrough;
case IDPF_CTLQ_TYPE_MAILBOX_TX:
err = idpf_ctlq_alloc_ring_res(hw, cq);
break;
default:
err = -EBADR;
break;
}
if (err)
goto init_free_q;
if (is_rxq) {
idpf_ctlq_init_rxq_bufs(cq);
} else {
/* Allocate the array of msg pointers for TX queues */
cq->bi.tx_msg = kzalloc_objs(struct idpf_ctlq_msg *, qinfo->len);
if (!cq->bi.tx_msg) {
err = -ENOMEM;
goto init_dealloc_q_mem;
}
}
idpf_ctlq_setup_regs(cq, qinfo);
idpf_ctlq_init_regs(hw, cq, is_rxq);
spin_lock_init(&cq->cq_lock);
list_add(&cq->cq_list, &hw->cq_list_head);
*cq_out = cq;
return 0;
init_dealloc_q_mem:
/* free ring buffers and the ring itself */
idpf_ctlq_dealloc_ring_res(hw, cq);
init_free_q:
kfree(cq);
return err;
}
/**
* idpf_ctlq_remove - deallocate and remove specified control queue
* @hw: pointer to hardware struct
* @cq: pointer to control queue to be removed
*/
void idpf_ctlq_remove(struct idpf_hw *hw,
struct idpf_ctlq_info *cq)
{
list_del(&cq->cq_list);
idpf_ctlq_shutdown(hw, cq);
kfree(cq);
}
/**
* idpf_ctlq_init - main initialization routine for all control queues
* @hw: pointer to hardware struct
* @num_q: number of queues to initialize
* @q_info: array of structs containing info for each queue to be initialized
*
* This initializes any number and any type of control queues. This is an all
* or nothing routine; if one fails, all previously allocated queues will be
* destroyed. This must be called prior to using the individual add/remove
* APIs.
*/
int idpf_ctlq_init(struct idpf_hw *hw, u8 num_q,
struct idpf_ctlq_create_info *q_info)
{
struct idpf_ctlq_info *cq, *tmp;
int err;
int i;
INIT_LIST_HEAD(&hw->cq_list_head);
for (i = 0; i < num_q; i++) {
struct idpf_ctlq_create_info *qinfo = q_info + i;
err = idpf_ctlq_add(hw, qinfo, &cq);
if (err)
goto init_destroy_qs;
}
return 0;
init_destroy_qs:
list_for_each_entry_safe(cq, tmp, &hw->cq_list_head, cq_list)
idpf_ctlq_remove(hw, cq);
return err;
}
/**
* idpf_ctlq_deinit - destroy all control queues
* @hw: pointer to hw struct
*/
void idpf_ctlq_deinit(struct idpf_hw *hw)
{
struct idpf_ctlq_info *cq, *tmp;
list_for_each_entry_safe(cq, tmp, &hw->cq_list_head, cq_list)
idpf_ctlq_remove(hw, cq);
}
/**
* idpf_ctlq_send - send command to Control Queue (CTQ)
* @hw: pointer to hw struct
* @cq: handle to control queue struct to send on
* @num_q_msg: number of messages to send on control queue
* @q_msg: pointer to array of queue messages to be sent
*
* The caller is expected to allocate DMAable buffers and pass them to the
* send routine via the q_msg struct / control queue specific data struct.
* The control queue will hold a reference to each send message until
* the completion for that message has been cleaned.
*/
int idpf_ctlq_send(struct idpf_hw *hw, struct idpf_ctlq_info *cq,
u16 num_q_msg, struct idpf_ctlq_msg q_msg[])
{
struct idpf_ctlq_desc *desc;
int num_desc_avail;
int err = 0;
int i;
spin_lock(&cq->cq_lock);
/* Ensure there are enough descriptors to send all messages */
num_desc_avail = IDPF_CTLQ_DESC_UNUSED(cq);
if (num_desc_avail == 0 || num_desc_avail < num_q_msg) {
err = -ENOSPC;
goto err_unlock;
}
for (i = 0; i < num_q_msg; i++) {
struct idpf_ctlq_msg *msg = &q_msg[i];
desc = IDPF_CTLQ_DESC(cq, cq->next_to_use);
desc->opcode = cpu_to_le16(msg->opcode);
desc->pfid_vfid = cpu_to_le16(msg->func_id);
desc->v_opcode_dtype = cpu_to_le32(msg->cookie.mbx.chnl_opcode);
desc->v_retval = cpu_to_le32(msg->cookie.mbx.chnl_retval);
desc->flags = cpu_to_le16((msg->host_id & IDPF_HOST_ID_MASK) <<
IDPF_CTLQ_FLAG_HOST_ID_S);
if (msg->data_len) {
struct idpf_dma_mem *buff = msg->ctx.indirect.payload;
desc->datalen |= cpu_to_le16(msg->data_len);
desc->flags |= cpu_to_le16(IDPF_CTLQ_FLAG_BUF);
desc->flags |= cpu_to_le16(IDPF_CTLQ_FLAG_RD);
/* Update the address values in the desc with the pa
* value for respective buffer
*/
desc->params.indirect.addr_high =
cpu_to_le32(upper_32_bits(buff->pa));
desc->params.indirect.addr_low =
cpu_to_le32(lower_32_bits(buff->pa));
memcpy(&desc->params, msg->ctx.indirect.context,
IDPF_INDIRECT_CTX_SIZE);
} else {
memcpy(&desc->params, msg->ctx.direct,
IDPF_DIRECT_CTX_SIZE);
}
/* Store buffer info */
cq->bi.tx_msg[cq->next_to_use] = msg;
(cq->next_to_use)++;
if (cq->next_to_use == cq->ring_size)
cq->next_to_use = 0;
}
/* Force memory write to complete before letting hardware
* know that there are new descriptors to fetch.
*/
dma_wmb();
idpf_mbx_wr32(hw, cq->reg.tail, cq->next_to_use);
err_unlock:
spin_unlock(&cq->cq_lock);
return err;
}
/**
* idpf_ctlq_clean_sq - reclaim send descriptors on HW write back for the
* requested queue
* @cq: pointer to the specific Control queue
* @clean_count: (input|output) number of descriptors to clean as input, and
* number of descriptors actually cleaned as output
* @msg_status: (output) pointer to msg pointer array to be populated; needs
* to be allocated by caller
*
* Returns an array of message pointers associated with the cleaned
* descriptors. The pointers are to the original ctlq_msgs sent on the cleaned
* descriptors. The status will be returned for each; any messages that failed
* to send will have a non-zero status. The caller is expected to free original
* ctlq_msgs and free or reuse the DMA buffers.
*/
int idpf_ctlq_clean_sq(struct idpf_ctlq_info *cq, u16 *clean_count,
struct idpf_ctlq_msg *msg_status[])
{
struct idpf_ctlq_desc *desc;
u16 i, num_to_clean;
u16 ntc, desc_err;
if (*clean_count == 0)
return 0;
if (*clean_count > cq->ring_size)
return -EBADR;
spin_lock(&cq->cq_lock);
ntc = cq->next_to_clean;
num_to_clean = *clean_count;
for (i = 0; i < num_to_clean; i++) {
/* Fetch next descriptor and check if marked as done */
desc = IDPF_CTLQ_DESC(cq, ntc);
if (!(le16_to_cpu(desc->flags) & IDPF_CTLQ_FLAG_DD))
break;
/* Ensure no other fields are read until DD flag is checked */
dma_rmb();
/* strip off FW internal code */
desc_err = le16_to_cpu(desc->ret_val) & 0xff;
msg_status[i] = cq->bi.tx_msg[ntc];
msg_status[i]->status = desc_err;
cq->bi.tx_msg[ntc] = NULL;
/* Zero out any stale data */
memset(desc, 0, sizeof(*desc));
ntc++;
if (ntc == cq->ring_size)
ntc = 0;
}
cq->next_to_clean = ntc;
spin_unlock(&cq->cq_lock);
/* Return number of descriptors actually cleaned */
*clean_count = i;
return 0;
}
/**
* idpf_ctlq_post_rx_buffs - post buffers to descriptor ring
* @hw: pointer to hw struct
* @cq: pointer to control queue handle
* @buff_count: (input|output) input is number of buffers caller is trying to
* return; output is number of buffers that were not posted
* @buffs: array of pointers to dma mem structs to be given to hardware
*
* Caller uses this function to return DMA buffers to the descriptor ring after
* consuming them; buff_count will be the number of buffers.
*
* Note: this function needs to be called after a receive call even
* if there are no DMA buffers to be returned, i.e. buff_count = 0,
* buffs = NULL to support direct commands
*/
int idpf_ctlq_post_rx_buffs(struct idpf_hw *hw, struct idpf_ctlq_info *cq,
u16 *buff_count, struct idpf_dma_mem **buffs)
{
struct idpf_ctlq_desc *desc;
u16 ntp = cq->next_to_post;
bool buffs_avail = false;
u16 tbp = ntp + 1;
int i = 0;
if (*buff_count > cq->ring_size)
return -EBADR;
if (*buff_count > 0)
buffs_avail = true;
spin_lock(&cq->cq_lock);
if (tbp >= cq->ring_size)
tbp = 0;
if (tbp == cq->next_to_clean)
/* Nothing to do */
goto post_buffs_out;
/* Post buffers for as many as provided or up until the last one used */
while (ntp != cq->next_to_clean) {
desc = IDPF_CTLQ_DESC(cq, ntp);
if (cq->bi.rx_buff[ntp])
goto fill_desc;
if (!buffs_avail) {
/* If the caller hasn't given us any buffers or
* there are none left, search the ring itself
* for an available buffer to move to this
* entry starting at the next entry in the ring
*/
tbp = ntp + 1;
/* Wrap ring if necessary */
if (tbp >= cq->ring_size)
tbp = 0;
while (tbp != cq->next_to_clean) {
if (cq->bi.rx_buff[tbp]) {
cq->bi.rx_buff[ntp] =
cq->bi.rx_buff[tbp];
cq->bi.rx_buff[tbp] = NULL;
/* Found a buffer, no need to
* search anymore
*/
break;
}
/* Wrap ring if necessary */
tbp++;
if (tbp >= cq->ring_size)
tbp = 0;
}
if (tbp == cq->next_to_clean)
goto post_buffs_out;
} else {
/* Give back pointer to DMA buffer */
cq->bi.rx_buff[ntp] = buffs[i];
i++;
if (i >= *buff_count)
buffs_avail = false;
}
fill_desc:
desc->flags =
cpu_to_le16(IDPF_CTLQ_FLAG_BUF | IDPF_CTLQ_FLAG_RD);
/* Post buffers to descriptor */
desc->datalen = cpu_to_le16(cq->bi.rx_buff[ntp]->size);
desc->params.indirect.addr_high =
cpu_to_le32(upper_32_bits(cq->bi.rx_buff[ntp]->pa));
desc->params.indirect.addr_low =
cpu_to_le32(lower_32_bits(cq->bi.rx_buff[ntp]->pa));
ntp++;
if (ntp == cq->ring_size)
ntp = 0;
}
post_buffs_out:
/* Only update tail if buffers were actually posted */
if (cq->next_to_post != ntp) {
if (ntp)
/* Update next_to_post to ntp - 1 since current ntp
* will not have a buffer
*/
cq->next_to_post = ntp - 1;
else
/* Wrap to end of end ring since current ntp is 0 */
cq->next_to_post = cq->ring_size - 1;
dma_wmb();
idpf_mbx_wr32(hw, cq->reg.tail, cq->next_to_post);
}
spin_unlock(&cq->cq_lock);
/* return the number of buffers that were not posted */
*buff_count = *buff_count - i;
return 0;
}
/**
* idpf_ctlq_recv - receive control queue message call back
* @cq: pointer to control queue handle to receive on
* @num_q_msg: (input|output) input number of messages that should be received;
* output number of messages actually received
* @q_msg: (output) array of received control queue messages on this q;
* needs to be pre-allocated by caller for as many messages as requested
*
* Called by interrupt handler or polling mechanism. Caller is expected
* to free buffers
*/
int idpf_ctlq_recv(struct idpf_ctlq_info *cq, u16 *num_q_msg,
struct idpf_ctlq_msg *q_msg)
{
u16 num_to_clean, ntc, flags;
struct idpf_ctlq_desc *desc;
int err = 0;
u16 i;
/* take the lock before we start messing with the ring */
spin_lock(&cq->cq_lock);
ntc = cq->next_to_clean;
num_to_clean = *num_q_msg;
for (i = 0; i < num_to_clean; i++) {
/* Fetch next descriptor and check if marked as done */
desc = IDPF_CTLQ_DESC(cq, ntc);
flags = le16_to_cpu(desc->flags);
if (!(flags & IDPF_CTLQ_FLAG_DD))
break;
/* Ensure no other fields are read until DD flag is checked */
dma_rmb();
q_msg[i].vmvf_type = (flags &
(IDPF_CTLQ_FLAG_FTYPE_VM |
IDPF_CTLQ_FLAG_FTYPE_PF)) >>
IDPF_CTLQ_FLAG_FTYPE_S;
if (flags & IDPF_CTLQ_FLAG_ERR)
err = -EBADMSG;
q_msg[i].cookie.mbx.chnl_opcode =
le32_to_cpu(desc->v_opcode_dtype);
q_msg[i].cookie.mbx.chnl_retval =
le32_to_cpu(desc->v_retval);
q_msg[i].opcode = le16_to_cpu(desc->opcode);
q_msg[i].data_len = le16_to_cpu(desc->datalen);
q_msg[i].status = le16_to_cpu(desc->ret_val);
if (desc->datalen) {
memcpy(q_msg[i].ctx.indirect.context,
&desc->params.indirect, IDPF_INDIRECT_CTX_SIZE);
/* Assign pointer to dma buffer to ctlq_msg array
* to be given to upper layer
*/
q_msg[i].ctx.indirect.payload = cq->bi.rx_buff[ntc];
/* Zero out pointer to DMA buffer info;
* will be repopulated by post buffers API
*/
cq->bi.rx_buff[ntc] = NULL;
} else {
memcpy(q_msg[i].ctx.direct, desc->params.raw,
IDPF_DIRECT_CTX_SIZE);
}
/* Zero out stale data in descriptor */
memset(desc, 0, sizeof(struct idpf_ctlq_desc));
ntc++;
if (ntc == cq->ring_size)
ntc = 0;
}
cq->next_to_clean = ntc;
spin_unlock(&cq->cq_lock);
*num_q_msg = i;
if (*num_q_msg == 0)
err = -ENOMSG;
return err;
}
@@ -0,0 +1,144 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (C) 2023 Intel Corporation */
#ifndef _IDPF_CONTROLQ_H_
#define _IDPF_CONTROLQ_H_
#include <linux/slab.h>
#include "idpf_controlq_api.h"
/* Maximum buffer length for all control queue types */
#define IDPF_CTLQ_MAX_BUF_LEN 4096
#define IDPF_CTLQ_DESC(R, i) \
(&(((struct idpf_ctlq_desc *)((R)->desc_ring.va))[i]))
#define IDPF_CTLQ_DESC_UNUSED(R) \
((u16)((((R)->next_to_clean > (R)->next_to_use) ? 0 : (R)->ring_size) + \
(R)->next_to_clean - (R)->next_to_use - 1))
/* Control Queue default settings */
#define IDPF_CTRL_SQ_CMD_TIMEOUT 250 /* msecs */
struct idpf_ctlq_desc {
/* Control queue descriptor flags */
__le16 flags;
/* Control queue message opcode */
__le16 opcode;
__le16 datalen; /* 0 for direct commands */
union {
__le16 ret_val;
__le16 pfid_vfid;
#define IDPF_CTLQ_DESC_VF_ID_S 0
#define IDPF_CTLQ_DESC_VF_ID_M (0x7FF << IDPF_CTLQ_DESC_VF_ID_S)
#define IDPF_CTLQ_DESC_PF_ID_S 11
#define IDPF_CTLQ_DESC_PF_ID_M (0x1F << IDPF_CTLQ_DESC_PF_ID_S)
};
/* Virtchnl message opcode and virtchnl descriptor type
* v_opcode=[27:0], v_dtype=[31:28]
*/
__le32 v_opcode_dtype;
/* Virtchnl return value */
__le32 v_retval;
union {
struct {
__le32 param0;
__le32 param1;
__le32 param2;
__le32 param3;
} direct;
struct {
__le32 param0;
__le16 sw_cookie;
/* Virtchnl flags */
__le16 v_flags;
__le32 addr_high;
__le32 addr_low;
} indirect;
u8 raw[16];
} params;
};
/* Flags sub-structure
* |0 |1 |2 |3 |4 |5 |6 |7 |8 |9 |10 |11 |12 |13 |14 |15 |
* |DD |CMP|ERR| * RSV * |FTYPE | *RSV* |RD |VFC|BUF| HOST_ID |
*/
/* command flags and offsets */
#define IDPF_CTLQ_FLAG_DD_S 0
#define IDPF_CTLQ_FLAG_CMP_S 1
#define IDPF_CTLQ_FLAG_ERR_S 2
#define IDPF_CTLQ_FLAG_FTYPE_S 6
#define IDPF_CTLQ_FLAG_RD_S 10
#define IDPF_CTLQ_FLAG_VFC_S 11
#define IDPF_CTLQ_FLAG_BUF_S 12
#define IDPF_CTLQ_FLAG_HOST_ID_S 13
#define IDPF_CTLQ_FLAG_DD BIT(IDPF_CTLQ_FLAG_DD_S) /* 0x1 */
#define IDPF_CTLQ_FLAG_CMP BIT(IDPF_CTLQ_FLAG_CMP_S) /* 0x2 */
#define IDPF_CTLQ_FLAG_ERR BIT(IDPF_CTLQ_FLAG_ERR_S) /* 0x4 */
#define IDPF_CTLQ_FLAG_FTYPE_VM BIT(IDPF_CTLQ_FLAG_FTYPE_S) /* 0x40 */
#define IDPF_CTLQ_FLAG_FTYPE_PF BIT(IDPF_CTLQ_FLAG_FTYPE_S + 1) /* 0x80 */
#define IDPF_CTLQ_FLAG_RD BIT(IDPF_CTLQ_FLAG_RD_S) /* 0x400 */
#define IDPF_CTLQ_FLAG_VFC BIT(IDPF_CTLQ_FLAG_VFC_S) /* 0x800 */
#define IDPF_CTLQ_FLAG_BUF BIT(IDPF_CTLQ_FLAG_BUF_S) /* 0x1000 */
/* Host ID is a special field that has 3b and not a 1b flag */
#define IDPF_CTLQ_FLAG_HOST_ID_M MAKE_MASK(0x7000UL, IDPF_CTLQ_FLAG_HOST_ID_S)
struct idpf_mbxq_desc {
u8 pad[8]; /* CTLQ flags/opcode/len/retval fields */
u32 chnl_opcode; /* avoid confusion with desc->opcode */
u32 chnl_retval; /* ditto for desc->retval */
u32 pf_vf_id; /* used by CP when sending to PF */
};
/* Max number of MMIO regions not including the mailbox and rstat regions in
* the fallback case when the whole bar is mapped.
*/
#define IDPF_MMIO_MAP_FALLBACK_MAX_REMAINING 3
struct idpf_mmio_reg {
void __iomem *vaddr;
resource_size_t addr_start;
resource_size_t addr_len;
};
/* Define the driver hardware struct to replace other control structs as needed
* Align to ctlq_hw_info
*/
struct idpf_hw {
struct idpf_mmio_reg mbx;
struct idpf_mmio_reg rstat;
/* Array of remaining LAN BAR regions */
int num_lan_regs;
struct idpf_mmio_reg *lan_regs;
struct idpf_adapter *back;
/* control queue - send and receive */
struct idpf_ctlq_info *asq;
struct idpf_ctlq_info *arq;
/* pci info */
u16 device_id;
u16 vendor_id;
u16 subsystem_device_id;
u16 subsystem_vendor_id;
u8 revision_id;
bool adapter_stopped;
struct list_head cq_list_head;
};
int idpf_ctlq_alloc_ring_res(struct idpf_hw *hw,
struct idpf_ctlq_info *cq);
void idpf_ctlq_dealloc_ring_res(struct idpf_hw *hw, struct idpf_ctlq_info *cq);
/* prototype for functions used for dynamic memory allocation */
void *idpf_alloc_dma_mem(struct idpf_hw *hw, struct idpf_dma_mem *mem,
u64 size);
void idpf_free_dma_mem(struct idpf_hw *hw, struct idpf_dma_mem *mem);
#endif /* _IDPF_CONTROLQ_H_ */
@@ -0,0 +1,177 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (C) 2023 Intel Corporation */
#ifndef _IDPF_CONTROLQ_API_H_
#define _IDPF_CONTROLQ_API_H_
#include "idpf_mem.h"
struct idpf_hw;
/* Used for queue init, response and events */
enum idpf_ctlq_type {
IDPF_CTLQ_TYPE_MAILBOX_TX = 0,
IDPF_CTLQ_TYPE_MAILBOX_RX = 1,
IDPF_CTLQ_TYPE_CONFIG_TX = 2,
IDPF_CTLQ_TYPE_CONFIG_RX = 3,
IDPF_CTLQ_TYPE_EVENT_RX = 4,
IDPF_CTLQ_TYPE_RDMA_TX = 5,
IDPF_CTLQ_TYPE_RDMA_RX = 6,
IDPF_CTLQ_TYPE_RDMA_COMPL = 7
};
/* Generic Control Queue Structures */
struct idpf_ctlq_reg {
/* used for queue tracking */
u32 head;
u32 tail;
/* Below applies only to default mb (if present) */
u32 len;
u32 bah;
u32 bal;
u32 len_mask;
u32 len_ena_mask;
u32 head_mask;
};
/* Generic queue msg structure */
struct idpf_ctlq_msg {
u8 vmvf_type; /* represents the source of the message on recv */
#define IDPF_VMVF_TYPE_VF 0
#define IDPF_VMVF_TYPE_VM 1
#define IDPF_VMVF_TYPE_PF 2
u8 host_id;
/* 3b field used only when sending a message to CP - to be used in
* combination with target func_id to route the message
*/
#define IDPF_HOST_ID_MASK 0x7
u16 opcode;
u16 data_len; /* data_len = 0 when no payload is attached */
union {
u16 func_id; /* when sending a message */
u16 status; /* when receiving a message */
};
union {
struct {
u32 chnl_opcode;
u32 chnl_retval;
} mbx;
} cookie;
union {
#define IDPF_DIRECT_CTX_SIZE 16
#define IDPF_INDIRECT_CTX_SIZE 8
/* 16 bytes of context can be provided or 8 bytes of context
* plus the address of a DMA buffer
*/
u8 direct[IDPF_DIRECT_CTX_SIZE];
struct {
u8 context[IDPF_INDIRECT_CTX_SIZE];
struct idpf_dma_mem *payload;
} indirect;
struct {
u32 rsvd;
u16 data;
u16 flags;
} sw_cookie;
} ctx;
};
/* Generic queue info structures */
/* MB, CONFIG and EVENT q do not have extended info */
struct idpf_ctlq_create_info {
enum idpf_ctlq_type type;
int id; /* absolute queue offset passed as input
* -1 for default mailbox if present
*/
u16 len; /* Queue length passed as input */
u16 buf_size; /* buffer size passed as input */
u64 base_address; /* output, HPA of the Queue start */
struct idpf_ctlq_reg reg; /* registers accessed by ctlqs */
int ext_info_size;
void *ext_info; /* Specific to q type */
};
/* Control Queue information */
struct idpf_ctlq_info {
struct list_head cq_list;
enum idpf_ctlq_type cq_type;
int q_id;
spinlock_t cq_lock; /* control queue lock */
/* used for interrupt processing */
u16 next_to_use;
u16 next_to_clean;
u16 next_to_post; /* starting descriptor to post buffers
* to after recev
*/
struct idpf_dma_mem desc_ring; /* descriptor ring memory
* idpf_dma_mem is defined in OSdep.h
*/
union {
struct idpf_dma_mem **rx_buff;
struct idpf_ctlq_msg **tx_msg;
} bi;
u16 buf_size; /* queue buffer size */
u16 ring_size; /* Number of descriptors */
struct idpf_ctlq_reg reg; /* registers accessed by ctlqs */
};
/**
* enum idpf_mbx_opc - PF/VF mailbox commands
* @idpf_mbq_opc_send_msg_to_cp: used by PF or VF to send a message to its CP
* @idpf_mbq_opc_send_msg_to_peer_drv: used by PF or VF to send a message to
* any peer driver
*/
enum idpf_mbx_opc {
idpf_mbq_opc_send_msg_to_cp = 0x0801,
idpf_mbq_opc_send_msg_to_peer_drv = 0x0804,
};
/* API supported for control queue management */
/* Will init all required q including default mb. "q_info" is an array of
* create_info structs equal to the number of control queues to be created.
*/
int idpf_ctlq_init(struct idpf_hw *hw, u8 num_q,
struct idpf_ctlq_create_info *q_info);
/* Allocate and initialize a single control queue, which will be added to the
* control queue list; returns a handle to the created control queue
*/
int idpf_ctlq_add(struct idpf_hw *hw,
struct idpf_ctlq_create_info *qinfo,
struct idpf_ctlq_info **cq);
/* Deinitialize and deallocate a single control queue */
void idpf_ctlq_remove(struct idpf_hw *hw,
struct idpf_ctlq_info *cq);
/* Sends messages to HW and will also free the buffer*/
int idpf_ctlq_send(struct idpf_hw *hw,
struct idpf_ctlq_info *cq,
u16 num_q_msg,
struct idpf_ctlq_msg q_msg[]);
/* Receives messages and called by interrupt handler/polling
* initiated by app/process. Also caller is supposed to free the buffers
*/
int idpf_ctlq_recv(struct idpf_ctlq_info *cq, u16 *num_q_msg,
struct idpf_ctlq_msg *q_msg);
/* Reclaims send descriptors on HW write back */
int idpf_ctlq_clean_sq(struct idpf_ctlq_info *cq, u16 *clean_count,
struct idpf_ctlq_msg *msg_status[]);
/* Indicate RX buffers are done being processed */
int idpf_ctlq_post_rx_buffs(struct idpf_hw *hw,
struct idpf_ctlq_info *cq,
u16 *buff_count,
struct idpf_dma_mem **buffs);
/* Will destroy all q including the default mb */
void idpf_ctlq_deinit(struct idpf_hw *hw);
#endif /* _IDPF_CONTROLQ_API_H_ */
@@ -0,0 +1,169 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2023 Intel Corporation */
#include "idpf_controlq.h"
/**
* idpf_ctlq_alloc_desc_ring - Allocate Control Queue (CQ) rings
* @hw: pointer to hw struct
* @cq: pointer to the specific Control queue
*/
static int idpf_ctlq_alloc_desc_ring(struct idpf_hw *hw,
struct idpf_ctlq_info *cq)
{
size_t size = cq->ring_size * sizeof(struct idpf_ctlq_desc);
cq->desc_ring.va = idpf_alloc_dma_mem(hw, &cq->desc_ring, size);
if (!cq->desc_ring.va)
return -ENOMEM;
return 0;
}
/**
* idpf_ctlq_alloc_bufs - Allocate Control Queue (CQ) buffers
* @hw: pointer to hw struct
* @cq: pointer to the specific Control queue
*
* Allocate the buffer head for all control queues, and if it's a receive
* queue, allocate DMA buffers
*/
static int idpf_ctlq_alloc_bufs(struct idpf_hw *hw,
struct idpf_ctlq_info *cq)
{
int i;
/* Do not allocate DMA buffers for transmit queues */
if (cq->cq_type == IDPF_CTLQ_TYPE_MAILBOX_TX)
return 0;
/* We'll be allocating the buffer info memory first, then we can
* allocate the mapped buffers for the event processing
*/
cq->bi.rx_buff = kzalloc_objs(struct idpf_dma_mem *, cq->ring_size);
if (!cq->bi.rx_buff)
return -ENOMEM;
/* allocate the mapped buffers (except for the last one) */
for (i = 0; i < cq->ring_size - 1; i++) {
struct idpf_dma_mem *bi;
int num = 1; /* number of idpf_dma_mem to be allocated */
cq->bi.rx_buff[i] = kzalloc_objs(struct idpf_dma_mem, num);
if (!cq->bi.rx_buff[i])
goto unwind_alloc_cq_bufs;
bi = cq->bi.rx_buff[i];
bi->va = idpf_alloc_dma_mem(hw, bi, cq->buf_size);
if (!bi->va) {
/* unwind will not free the failed entry */
kfree(cq->bi.rx_buff[i]);
goto unwind_alloc_cq_bufs;
}
}
return 0;
unwind_alloc_cq_bufs:
/* don't try to free the one that failed... */
i--;
for (; i >= 0; i--) {
idpf_free_dma_mem(hw, cq->bi.rx_buff[i]);
kfree(cq->bi.rx_buff[i]);
}
kfree(cq->bi.rx_buff);
return -ENOMEM;
}
/**
* idpf_ctlq_free_desc_ring - Free Control Queue (CQ) rings
* @hw: pointer to hw struct
* @cq: pointer to the specific Control queue
*
* This assumes the posted send buffers have already been cleaned
* and de-allocated
*/
static void idpf_ctlq_free_desc_ring(struct idpf_hw *hw,
struct idpf_ctlq_info *cq)
{
idpf_free_dma_mem(hw, &cq->desc_ring);
}
/**
* idpf_ctlq_free_bufs - Free CQ buffer info elements
* @hw: pointer to hw struct
* @cq: pointer to the specific Control queue
*
* Free the DMA buffers for RX queues, and DMA buffer header for both RX and TX
* queues. The upper layers are expected to manage freeing of TX DMA buffers
*/
static void idpf_ctlq_free_bufs(struct idpf_hw *hw, struct idpf_ctlq_info *cq)
{
void *bi;
if (cq->cq_type == IDPF_CTLQ_TYPE_MAILBOX_RX) {
int i;
/* free DMA buffers for rx queues*/
for (i = 0; i < cq->ring_size; i++) {
if (cq->bi.rx_buff[i]) {
idpf_free_dma_mem(hw, cq->bi.rx_buff[i]);
kfree(cq->bi.rx_buff[i]);
}
}
bi = (void *)cq->bi.rx_buff;
} else {
bi = (void *)cq->bi.tx_msg;
}
/* free the buffer header */
kfree(bi);
}
/**
* idpf_ctlq_dealloc_ring_res - Free memory allocated for control queue
* @hw: pointer to hw struct
* @cq: pointer to the specific Control queue
*
* Free the memory used by the ring, buffers and other related structures
*/
void idpf_ctlq_dealloc_ring_res(struct idpf_hw *hw, struct idpf_ctlq_info *cq)
{
/* free ring buffers and the ring itself */
idpf_ctlq_free_bufs(hw, cq);
idpf_ctlq_free_desc_ring(hw, cq);
}
/**
* idpf_ctlq_alloc_ring_res - allocate memory for descriptor ring and bufs
* @hw: pointer to hw struct
* @cq: pointer to control queue struct
*
* Do *NOT* hold cq_lock when calling this as the memory allocation routines
* called are not going to be atomic context safe
*/
int idpf_ctlq_alloc_ring_res(struct idpf_hw *hw, struct idpf_ctlq_info *cq)
{
int err;
/* allocate the ring memory */
err = idpf_ctlq_alloc_desc_ring(hw, cq);
if (err)
return err;
/* allocate buffers in the rings */
err = idpf_ctlq_alloc_bufs(hw, cq);
if (err)
goto idpf_init_cq_free_ring;
/* success! */
return 0;
idpf_init_cq_free_ring:
idpf_free_dma_mem(hw, &cq->desc_ring);
return err;
}
@@ -0,0 +1,216 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2023 Intel Corporation */
#include "idpf.h"
#include "idpf_lan_pf_regs.h"
#include "idpf_virtchnl.h"
#include "idpf_ptp.h"
#define IDPF_PF_ITR_IDX_SPACING 0x4
/**
* idpf_ctlq_reg_init - initialize default mailbox registers
* @adapter: adapter structure
* @cq: pointer to the array of create control queues
*/
static void idpf_ctlq_reg_init(struct idpf_adapter *adapter,
struct idpf_ctlq_create_info *cq)
{
resource_size_t mbx_start = adapter->dev_ops.static_reg_info[0].start;
int i;
for (i = 0; i < IDPF_NUM_DFLT_MBX_Q; i++) {
struct idpf_ctlq_create_info *ccq = cq + i;
switch (ccq->type) {
case IDPF_CTLQ_TYPE_MAILBOX_TX:
/* set head and tail registers in our local struct */
ccq->reg.head = PF_FW_ATQH - mbx_start;
ccq->reg.tail = PF_FW_ATQT - mbx_start;
ccq->reg.len = PF_FW_ATQLEN - mbx_start;
ccq->reg.bah = PF_FW_ATQBAH - mbx_start;
ccq->reg.bal = PF_FW_ATQBAL - mbx_start;
ccq->reg.len_mask = PF_FW_ATQLEN_ATQLEN_M;
ccq->reg.len_ena_mask = PF_FW_ATQLEN_ATQENABLE_M;
ccq->reg.head_mask = PF_FW_ATQH_ATQH_M;
break;
case IDPF_CTLQ_TYPE_MAILBOX_RX:
/* set head and tail registers in our local struct */
ccq->reg.head = PF_FW_ARQH - mbx_start;
ccq->reg.tail = PF_FW_ARQT - mbx_start;
ccq->reg.len = PF_FW_ARQLEN - mbx_start;
ccq->reg.bah = PF_FW_ARQBAH - mbx_start;
ccq->reg.bal = PF_FW_ARQBAL - mbx_start;
ccq->reg.len_mask = PF_FW_ARQLEN_ARQLEN_M;
ccq->reg.len_ena_mask = PF_FW_ARQLEN_ARQENABLE_M;
ccq->reg.head_mask = PF_FW_ARQH_ARQH_M;
break;
default:
break;
}
}
}
/**
* idpf_mb_intr_reg_init - Initialize mailbox interrupt register
* @adapter: adapter structure
*/
static void idpf_mb_intr_reg_init(struct idpf_adapter *adapter)
{
struct idpf_intr_reg *intr = &adapter->mb_vector.intr_reg;
u32 dyn_ctl = le32_to_cpu(adapter->caps.mailbox_dyn_ctl);
intr->dyn_ctl = idpf_get_reg_addr(adapter, dyn_ctl);
intr->dyn_ctl_intena_m = PF_GLINT_DYN_CTL_INTENA_M;
intr->dyn_ctl_itridx_m = PF_GLINT_DYN_CTL_ITR_INDX_M;
intr->icr_ena = idpf_get_reg_addr(adapter, PF_INT_DIR_OICR_ENA);
intr->icr_ena_ctlq_m = PF_INT_DIR_OICR_ENA_M;
}
/**
* idpf_intr_reg_init - Initialize interrupt registers
* @vport: virtual port structure
* @rsrc: pointer to queue and vector resources
*/
static int idpf_intr_reg_init(struct idpf_vport *vport,
struct idpf_q_vec_rsrc *rsrc)
{
struct idpf_adapter *adapter = vport->adapter;
u16 num_vecs = rsrc->num_q_vectors;
struct idpf_vec_regs *reg_vals;
int num_regs, i, err = 0;
u32 rx_itr, tx_itr, val;
u16 total_vecs;
total_vecs = idpf_get_reserved_vecs(vport->adapter);
reg_vals = kzalloc_objs(struct idpf_vec_regs, total_vecs);
if (!reg_vals)
return -ENOMEM;
num_regs = idpf_get_reg_intr_vecs(adapter, reg_vals, total_vecs);
if (num_regs < num_vecs) {
err = -EINVAL;
goto free_reg_vals;
}
for (i = 0; i < num_vecs; i++) {
struct idpf_q_vector *q_vector = &rsrc->q_vectors[i];
u16 vec_id = rsrc->q_vector_idxs[i] - IDPF_MBX_Q_VEC;
struct idpf_intr_reg *intr = &q_vector->intr_reg;
u32 spacing;
intr->dyn_ctl = idpf_get_reg_addr(adapter,
reg_vals[vec_id].dyn_ctl_reg);
intr->dyn_ctl_intena_m = PF_GLINT_DYN_CTL_INTENA_M;
intr->dyn_ctl_intena_msk_m = PF_GLINT_DYN_CTL_INTENA_MSK_M;
intr->dyn_ctl_itridx_s = PF_GLINT_DYN_CTL_ITR_INDX_S;
intr->dyn_ctl_intrvl_s = PF_GLINT_DYN_CTL_INTERVAL_S;
intr->dyn_ctl_wb_on_itr_m = PF_GLINT_DYN_CTL_WB_ON_ITR_M;
intr->dyn_ctl_swint_trig_m = PF_GLINT_DYN_CTL_SWINT_TRIG_M;
intr->dyn_ctl_sw_itridx_ena_m =
PF_GLINT_DYN_CTL_SW_ITR_INDX_ENA_M;
spacing = IDPF_ITR_IDX_SPACING(reg_vals[vec_id].itrn_index_spacing,
IDPF_PF_ITR_IDX_SPACING);
rx_itr = PF_GLINT_ITR_ADDR(VIRTCHNL2_ITR_IDX_0,
reg_vals[vec_id].itrn_reg,
spacing);
tx_itr = PF_GLINT_ITR_ADDR(VIRTCHNL2_ITR_IDX_1,
reg_vals[vec_id].itrn_reg,
spacing);
intr->rx_itr = idpf_get_reg_addr(adapter, rx_itr);
intr->tx_itr = idpf_get_reg_addr(adapter, tx_itr);
}
/* Data vector for NOIRQ queues */
val = reg_vals[rsrc->q_vector_idxs[i] - IDPF_MBX_Q_VEC].dyn_ctl_reg;
rsrc->noirq_dyn_ctl = idpf_get_reg_addr(adapter, val);
val = PF_GLINT_DYN_CTL_WB_ON_ITR_M | PF_GLINT_DYN_CTL_INTENA_MSK_M |
FIELD_PREP(PF_GLINT_DYN_CTL_ITR_INDX_M, IDPF_NO_ITR_UPDATE_IDX);
rsrc->noirq_dyn_ctl_ena = val;
free_reg_vals:
kfree(reg_vals);
return err;
}
/**
* idpf_reset_reg_init - Initialize reset registers
* @adapter: Driver specific private structure
*/
static void idpf_reset_reg_init(struct idpf_adapter *adapter)
{
adapter->reset_reg.rstat = idpf_get_rstat_reg_addr(adapter, PFGEN_RSTAT);
adapter->reset_reg.rstat_m = PFGEN_RSTAT_PFR_STATE_M;
}
/**
* idpf_trigger_reset - trigger reset
* @adapter: Driver specific private structure
* @trig_cause: Reason to trigger a reset
*/
static void idpf_trigger_reset(struct idpf_adapter *adapter,
enum idpf_flags __always_unused trig_cause)
{
u32 reset_reg;
reset_reg = readl(idpf_get_rstat_reg_addr(adapter, PFGEN_CTRL));
writel(reset_reg | PFGEN_CTRL_PFSWR,
idpf_get_rstat_reg_addr(adapter, PFGEN_CTRL));
}
/**
* idpf_ptp_reg_init - Initialize required registers
* @adapter: Driver specific private structure
*
* Set the bits required for enabling shtime and cmd execution
*/
static void idpf_ptp_reg_init(const struct idpf_adapter *adapter)
{
adapter->ptp->cmd.shtime_enable_mask = PF_GLTSYN_CMD_SYNC_SHTIME_EN_M;
adapter->ptp->cmd.exec_cmd_mask = PF_GLTSYN_CMD_SYNC_EXEC_CMD_M;
}
/**
* idpf_idc_register - register for IDC callbacks
* @adapter: Driver specific private structure
*
* Return: 0 on success or error code on failure.
*/
static int idpf_idc_register(struct idpf_adapter *adapter)
{
return idpf_idc_init_aux_core_dev(adapter, IIDC_FUNCTION_TYPE_PF);
}
/**
* idpf_reg_ops_init - Initialize register API function pointers
* @adapter: Driver specific private structure
*/
static void idpf_reg_ops_init(struct idpf_adapter *adapter)
{
adapter->dev_ops.reg_ops.ctlq_reg_init = idpf_ctlq_reg_init;
adapter->dev_ops.reg_ops.intr_reg_init = idpf_intr_reg_init;
adapter->dev_ops.reg_ops.mb_intr_reg_init = idpf_mb_intr_reg_init;
adapter->dev_ops.reg_ops.reset_reg_init = idpf_reset_reg_init;
adapter->dev_ops.reg_ops.trigger_reset = idpf_trigger_reset;
adapter->dev_ops.reg_ops.ptp_reg_init = idpf_ptp_reg_init;
}
/**
* idpf_dev_ops_init - Initialize device API function pointers
* @adapter: Driver specific private structure
*/
void idpf_dev_ops_init(struct idpf_adapter *adapter)
{
idpf_reg_ops_init(adapter);
adapter->dev_ops.idc_init = idpf_idc_register;
resource_set_range(&adapter->dev_ops.static_reg_info[0],
PF_FW_BASE, IDPF_PF_MBX_REGION_SZ);
resource_set_range(&adapter->dev_ops.static_reg_info[1],
PFGEN_RTRIG, IDPF_PF_RSTAT_REGION_SZ);
}
@@ -0,0 +1,10 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (C) 2023 Intel Corporation */
#ifndef _IDPF_DEVIDS_H_
#define _IDPF_DEVIDS_H_
#define IDPF_DEV_ID_PF 0x1452
#define IDPF_DEV_ID_VF 0x145C
#endif /* _IDPF_DEVIDS_H_ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,510 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2025 Intel Corporation */
#include <linux/export.h>
#include "idpf.h"
#include "idpf_virtchnl.h"
static DEFINE_IDA(idpf_idc_ida);
#define IDPF_IDC_MAX_ADEV_NAME_LEN 15
/**
* idpf_idc_init - Called to initialize IDC
* @adapter: driver private data structure
*
* Return: 0 on success or cap not enabled, error code on failure.
*/
int idpf_idc_init(struct idpf_adapter *adapter)
{
int err;
if (!idpf_is_rdma_cap_ena(adapter) ||
!adapter->dev_ops.idc_init)
return 0;
err = adapter->dev_ops.idc_init(adapter);
if (err)
dev_err(&adapter->pdev->dev, "failed to initialize idc: %d\n",
err);
return err;
}
/**
* idpf_vport_adev_release - function to be mapped to aux dev's release op
* @dev: pointer to device to free
*/
static void idpf_vport_adev_release(struct device *dev)
{
struct iidc_rdma_vport_auxiliary_dev *iadev;
iadev = container_of(dev, struct iidc_rdma_vport_auxiliary_dev, adev.dev);
kfree(iadev);
iadev = NULL;
}
/**
* idpf_plug_vport_aux_dev - allocate and register a vport Auxiliary device
* @cdev_info: IDC core device info pointer
* @vdev_info: IDC vport device info pointer
*
* Return: 0 on success or error code on failure.
*/
static int idpf_plug_vport_aux_dev(struct iidc_rdma_core_dev_info *cdev_info,
struct iidc_rdma_vport_dev_info *vdev_info)
{
struct iidc_rdma_vport_auxiliary_dev *iadev;
char name[IDPF_IDC_MAX_ADEV_NAME_LEN];
struct auxiliary_device *adev;
int ret;
iadev = kzalloc_obj(*iadev);
if (!iadev)
return -ENOMEM;
adev = &iadev->adev;
vdev_info->adev = &iadev->adev;
iadev->vdev_info = vdev_info;
ret = ida_alloc(&idpf_idc_ida, GFP_KERNEL);
if (ret < 0) {
pr_err("failed to allocate unique device ID for Auxiliary driver\n");
goto err_ida_alloc;
}
adev->id = ret;
adev->dev.release = idpf_vport_adev_release;
adev->dev.parent = &cdev_info->pdev->dev;
sprintf(name, "%04x.rdma.vdev", cdev_info->pdev->vendor);
adev->name = name;
ret = auxiliary_device_init(adev);
if (ret)
goto err_aux_dev_init;
ret = auxiliary_device_add(adev);
if (ret)
goto err_aux_dev_add;
return 0;
err_aux_dev_add:
ida_free(&idpf_idc_ida, adev->id);
vdev_info->adev = NULL;
auxiliary_device_uninit(adev);
return ret;
err_aux_dev_init:
ida_free(&idpf_idc_ida, adev->id);
err_ida_alloc:
vdev_info->adev = NULL;
kfree(iadev);
return ret;
}
/**
* idpf_idc_init_aux_vport_dev - initialize vport Auxiliary Device(s)
* @vport: virtual port data struct
*
* Return: 0 on success or error code on failure.
*/
static int idpf_idc_init_aux_vport_dev(struct idpf_vport *vport)
{
struct idpf_adapter *adapter = vport->adapter;
struct iidc_rdma_vport_dev_info *vdev_info;
struct iidc_rdma_core_dev_info *cdev_info;
struct virtchnl2_create_vport *vport_msg;
int err;
vport_msg = (struct virtchnl2_create_vport *)
adapter->vport_params_recvd[vport->idx];
if (!(le16_to_cpu(vport_msg->vport_flags) & VIRTCHNL2_VPORT_ENABLE_RDMA))
return 0;
vport->vdev_info = kzalloc_obj(*vdev_info);
if (!vport->vdev_info)
return -ENOMEM;
cdev_info = vport->adapter->cdev_info;
vdev_info = vport->vdev_info;
vdev_info->vport_id = vport->vport_id;
vdev_info->netdev = vport->netdev;
vdev_info->core_adev = cdev_info->adev;
err = idpf_plug_vport_aux_dev(cdev_info, vdev_info);
if (err) {
vport->vdev_info = NULL;
kfree(vdev_info);
return err;
}
return 0;
}
/**
* idpf_idc_vdev_mtu_event - Function to handle IDC vport mtu change events
* @vdev_info: IDC vport device info pointer
* @event_type: type of event to pass to handler
*/
void idpf_idc_vdev_mtu_event(struct iidc_rdma_vport_dev_info *vdev_info,
enum iidc_rdma_event_type event_type)
{
struct iidc_rdma_vport_auxiliary_drv *iadrv;
struct iidc_rdma_event event = { };
struct auxiliary_device *adev;
if (!vdev_info)
/* RDMA is not enabled */
return;
set_bit(event_type, event.type);
device_lock(&vdev_info->adev->dev);
adev = vdev_info->adev;
if (!adev || !adev->dev.driver)
goto unlock;
iadrv = container_of(adev->dev.driver,
struct iidc_rdma_vport_auxiliary_drv,
adrv.driver);
if (iadrv->event_handler)
iadrv->event_handler(vdev_info, &event);
unlock:
device_unlock(&vdev_info->adev->dev);
}
/**
* idpf_core_adev_release - function to be mapped to aux dev's release op
* @dev: pointer to device to free
*/
static void idpf_core_adev_release(struct device *dev)
{
struct iidc_rdma_core_auxiliary_dev *iadev;
iadev = container_of(dev, struct iidc_rdma_core_auxiliary_dev, adev.dev);
kfree(iadev);
iadev = NULL;
}
/**
* idpf_plug_core_aux_dev - allocate and register an Auxiliary device
* @cdev_info: IDC core device info pointer
*
* Return: 0 on success or error code on failure.
*/
static int idpf_plug_core_aux_dev(struct iidc_rdma_core_dev_info *cdev_info)
{
struct iidc_rdma_core_auxiliary_dev *iadev;
char name[IDPF_IDC_MAX_ADEV_NAME_LEN];
struct auxiliary_device *adev;
int ret;
iadev = kzalloc_obj(*iadev);
if (!iadev)
return -ENOMEM;
adev = &iadev->adev;
cdev_info->adev = adev;
iadev->cdev_info = cdev_info;
ret = ida_alloc(&idpf_idc_ida, GFP_KERNEL);
if (ret < 0) {
pr_err("failed to allocate unique device ID for Auxiliary driver\n");
goto err_ida_alloc;
}
adev->id = ret;
adev->dev.release = idpf_core_adev_release;
adev->dev.parent = &cdev_info->pdev->dev;
sprintf(name, "%04x.rdma.core", cdev_info->pdev->vendor);
adev->name = name;
ret = auxiliary_device_init(adev);
if (ret)
goto err_aux_dev_init;
ret = auxiliary_device_add(adev);
if (ret)
goto err_aux_dev_add;
return 0;
err_aux_dev_add:
ida_free(&idpf_idc_ida, adev->id);
cdev_info->adev = NULL;
auxiliary_device_uninit(adev);
return ret;
err_aux_dev_init:
ida_free(&idpf_idc_ida, adev->id);
err_ida_alloc:
cdev_info->adev = NULL;
kfree(iadev);
return ret;
}
/**
* idpf_unplug_aux_dev - unregister and free an Auxiliary device
* @adev: auxiliary device struct
*/
static void idpf_unplug_aux_dev(struct auxiliary_device *adev)
{
if (!adev)
return;
ida_free(&idpf_idc_ida, adev->id);
auxiliary_device_delete(adev);
auxiliary_device_uninit(adev);
}
/**
* idpf_idc_issue_reset_event - Function to handle reset IDC event
* @cdev_info: IDC core device info pointer
*/
void idpf_idc_issue_reset_event(struct iidc_rdma_core_dev_info *cdev_info)
{
enum iidc_rdma_event_type event_type = IIDC_RDMA_EVENT_WARN_RESET;
struct iidc_rdma_core_auxiliary_drv *iadrv;
struct iidc_rdma_event event = { };
struct auxiliary_device *adev;
if (!cdev_info)
/* RDMA is not enabled */
return;
set_bit(event_type, event.type);
device_lock(&cdev_info->adev->dev);
adev = cdev_info->adev;
if (!adev || !adev->dev.driver)
goto unlock;
iadrv = container_of(adev->dev.driver,
struct iidc_rdma_core_auxiliary_drv,
adrv.driver);
if (iadrv->event_handler)
iadrv->event_handler(cdev_info, &event);
unlock:
device_unlock(&cdev_info->adev->dev);
}
/**
* idpf_idc_vport_dev_up - called when CORE is ready for vport aux devs
* @adapter: private data struct
*
* Return: 0 on success or error code on failure.
*/
static int idpf_idc_vport_dev_up(struct idpf_adapter *adapter)
{
int i, err = 0;
for (i = 0; i < adapter->num_alloc_vports; i++) {
struct idpf_vport *vport = adapter->vports[i];
if (!vport)
continue;
if (!vport->vdev_info)
err = idpf_idc_init_aux_vport_dev(vport);
else
err = idpf_plug_vport_aux_dev(vport->adapter->cdev_info,
vport->vdev_info);
}
return err;
}
/**
* idpf_idc_vport_dev_down - called CORE is leaving vport aux dev support state
* @adapter: private data struct
*/
static void idpf_idc_vport_dev_down(struct idpf_adapter *adapter)
{
int i;
for (i = 0; i < adapter->num_alloc_vports; i++) {
struct idpf_vport *vport = adapter->vports[i];
if (!vport || !vport->vdev_info)
continue;
idpf_unplug_aux_dev(vport->vdev_info->adev);
vport->vdev_info->adev = NULL;
}
}
/**
* idpf_idc_vport_dev_ctrl - Called by an Auxiliary Driver
* @cdev_info: IDC core device info pointer
* @up: RDMA core driver status
*
* This callback function is accessed by an Auxiliary Driver to indicate
* whether core driver is ready to support vport driver load or if vport
* drivers need to be taken down.
*
* Return: 0 on success or error code on failure.
*/
int idpf_idc_vport_dev_ctrl(struct iidc_rdma_core_dev_info *cdev_info, bool up)
{
struct idpf_adapter *adapter = pci_get_drvdata(cdev_info->pdev);
if (up)
return idpf_idc_vport_dev_up(adapter);
idpf_idc_vport_dev_down(adapter);
return 0;
}
EXPORT_SYMBOL_GPL(idpf_idc_vport_dev_ctrl);
/**
* idpf_idc_request_reset - Called by an Auxiliary Driver
* @cdev_info: IDC core device info pointer
* @reset_type: function, core or other
*
* This callback function is accessed by an Auxiliary Driver to request a reset
* on the Auxiliary Device.
*
* Return: 0 on success or error code on failure.
*/
int idpf_idc_request_reset(struct iidc_rdma_core_dev_info *cdev_info,
enum iidc_rdma_reset_type __always_unused reset_type)
{
struct idpf_adapter *adapter = pci_get_drvdata(cdev_info->pdev);
if (!idpf_is_reset_in_prog(adapter)) {
set_bit(IDPF_HR_FUNC_RESET, adapter->flags);
queue_delayed_work(adapter->vc_event_wq,
&adapter->vc_event_task,
msecs_to_jiffies(10));
}
return 0;
}
EXPORT_SYMBOL_GPL(idpf_idc_request_reset);
/**
* idpf_idc_init_msix_data - initialize MSIX data for the cdev_info structure
* @adapter: driver private data structure
*/
static void
idpf_idc_init_msix_data(struct idpf_adapter *adapter)
{
struct iidc_rdma_core_dev_info *cdev_info;
struct iidc_rdma_priv_dev_info *privd;
if (!adapter->rdma_msix_entries)
return;
cdev_info = adapter->cdev_info;
privd = cdev_info->iidc_priv;
privd->msix_entries = adapter->rdma_msix_entries;
privd->msix_count = adapter->num_rdma_msix_entries;
}
/**
* idpf_idc_init_aux_core_dev - initialize Auxiliary Device(s)
* @adapter: driver private data structure
* @ftype: PF or VF
*
* Return: 0 on success or error code on failure.
*/
int idpf_idc_init_aux_core_dev(struct idpf_adapter *adapter,
enum iidc_function_type ftype)
{
struct iidc_rdma_core_dev_info *cdev_info;
struct iidc_rdma_priv_dev_info *privd;
int err, i;
adapter->cdev_info = kzalloc_obj(*cdev_info);
if (!adapter->cdev_info)
return -ENOMEM;
cdev_info = adapter->cdev_info;
privd = kzalloc_obj(*privd);
if (!privd) {
err = -ENOMEM;
goto err_privd_alloc;
}
cdev_info->iidc_priv = privd;
cdev_info->pdev = adapter->pdev;
cdev_info->rdma_protocol = IIDC_RDMA_PROTOCOL_ROCEV2;
privd->ftype = ftype;
privd->mapped_mem_regions =
kzalloc_objs(struct iidc_rdma_lan_mapped_mem_region,
adapter->hw.num_lan_regs);
if (!privd->mapped_mem_regions) {
err = -ENOMEM;
goto err_plug_aux_dev;
}
privd->num_memory_regions = cpu_to_le16(adapter->hw.num_lan_regs);
for (i = 0; i < adapter->hw.num_lan_regs; i++) {
privd->mapped_mem_regions[i].region_addr =
adapter->hw.lan_regs[i].vaddr;
privd->mapped_mem_regions[i].size =
cpu_to_le64(adapter->hw.lan_regs[i].addr_len);
privd->mapped_mem_regions[i].start_offset =
cpu_to_le64(adapter->hw.lan_regs[i].addr_start);
}
idpf_idc_init_msix_data(adapter);
err = idpf_plug_core_aux_dev(cdev_info);
if (err)
goto err_free_mem_regions;
return 0;
err_free_mem_regions:
kfree(privd->mapped_mem_regions);
privd->mapped_mem_regions = NULL;
err_plug_aux_dev:
kfree(privd);
err_privd_alloc:
kfree(cdev_info);
adapter->cdev_info = NULL;
return err;
}
/**
* idpf_idc_deinit_core_aux_device - de-initialize Auxiliary Device(s)
* @adapter: driver private data structure
*/
void idpf_idc_deinit_core_aux_device(struct idpf_adapter *adapter)
{
struct iidc_rdma_core_dev_info *cdev_info = adapter->cdev_info;
struct iidc_rdma_priv_dev_info *privd;
if (!cdev_info)
return;
idpf_unplug_aux_dev(cdev_info->adev);
privd = cdev_info->iidc_priv;
kfree(privd->mapped_mem_regions);
kfree(privd);
kfree(cdev_info);
adapter->cdev_info = NULL;
}
/**
* idpf_idc_deinit_vport_aux_device - de-initialize Auxiliary Device(s)
* @vdev_info: IDC vport device info pointer
*/
void idpf_idc_deinit_vport_aux_device(struct iidc_rdma_vport_dev_info *vdev_info)
{
if (!vdev_info)
return;
idpf_unplug_aux_dev(vdev_info->adev);
kfree(vdev_info);
}
@@ -0,0 +1,128 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (C) 2023 Intel Corporation */
#ifndef _IDPF_LAN_PF_REGS_H_
#define _IDPF_LAN_PF_REGS_H_
/* Receive queues */
#define PF_QRX_BASE 0x00000000
#define PF_QRX_TAIL(_QRX) (PF_QRX_BASE + (((_QRX) * 0x1000)))
#define PF_QRX_BUFFQ_BASE 0x03000000
#define PF_QRX_BUFFQ_TAIL(_QRX) (PF_QRX_BUFFQ_BASE + (((_QRX) * 0x1000)))
/* Transmit queues */
#define PF_QTX_BASE 0x05000000
#define PF_QTX_COMM_DBELL(_DBQM) (PF_QTX_BASE + ((_DBQM) * 0x1000))
/* Control(PF Mailbox) Queue */
#define PF_FW_BASE 0x08400000
#define PF_FW_ARQBAL (PF_FW_BASE)
#define PF_FW_ARQBAH (PF_FW_BASE + 0x4)
#define PF_FW_ARQLEN (PF_FW_BASE + 0x8)
#define PF_FW_ARQLEN_ARQLEN_S 0
#define PF_FW_ARQLEN_ARQLEN_M GENMASK(12, 0)
#define PF_FW_ARQLEN_ARQVFE_S 28
#define PF_FW_ARQLEN_ARQVFE_M BIT(PF_FW_ARQLEN_ARQVFE_S)
#define PF_FW_ARQLEN_ARQOVFL_S 29
#define PF_FW_ARQLEN_ARQOVFL_M BIT(PF_FW_ARQLEN_ARQOVFL_S)
#define PF_FW_ARQLEN_ARQCRIT_S 30
#define PF_FW_ARQLEN_ARQCRIT_M BIT(PF_FW_ARQLEN_ARQCRIT_S)
#define PF_FW_ARQLEN_ARQENABLE_S 31
#define PF_FW_ARQLEN_ARQENABLE_M BIT(PF_FW_ARQLEN_ARQENABLE_S)
#define PF_FW_ARQH (PF_FW_BASE + 0xC)
#define PF_FW_ARQH_ARQH_S 0
#define PF_FW_ARQH_ARQH_M GENMASK(12, 0)
#define PF_FW_ARQT (PF_FW_BASE + 0x10)
#define PF_FW_ATQBAL (PF_FW_BASE + 0x14)
#define PF_FW_ATQBAH (PF_FW_BASE + 0x18)
#define PF_FW_ATQLEN (PF_FW_BASE + 0x1C)
#define PF_FW_ATQLEN_ATQLEN_S 0
#define PF_FW_ATQLEN_ATQLEN_M GENMASK(9, 0)
#define PF_FW_ATQLEN_ATQVFE_S 28
#define PF_FW_ATQLEN_ATQVFE_M BIT(PF_FW_ATQLEN_ATQVFE_S)
#define PF_FW_ATQLEN_ATQOVFL_S 29
#define PF_FW_ATQLEN_ATQOVFL_M BIT(PF_FW_ATQLEN_ATQOVFL_S)
#define PF_FW_ATQLEN_ATQCRIT_S 30
#define PF_FW_ATQLEN_ATQCRIT_M BIT(PF_FW_ATQLEN_ATQCRIT_S)
#define PF_FW_ATQLEN_ATQENABLE_S 31
#define PF_FW_ATQLEN_ATQENABLE_M BIT(PF_FW_ATQLEN_ATQENABLE_S)
#define PF_FW_ATQH (PF_FW_BASE + 0x20)
#define PF_FW_ATQH_ATQH_S 0
#define PF_FW_ATQH_ATQH_M GENMASK(9, 0)
#define PF_FW_ATQT (PF_FW_BASE + 0x24)
/* Timesync registers */
#define PF_GLTSYN_CMD_SYNC_EXEC_CMD_M GENMASK(1, 0)
#define PF_GLTSYN_CMD_SYNC_SHTIME_EN_M BIT(2)
/* Interrupts */
#define PF_GLINT_BASE 0x08900000
#define PF_GLINT_DYN_CTL(_INT) (PF_GLINT_BASE + ((_INT) * 0x1000))
#define PF_GLINT_DYN_CTL_INTENA_S 0
#define PF_GLINT_DYN_CTL_INTENA_M BIT(PF_GLINT_DYN_CTL_INTENA_S)
#define PF_GLINT_DYN_CTL_CLEARPBA_S 1
#define PF_GLINT_DYN_CTL_CLEARPBA_M BIT(PF_GLINT_DYN_CTL_CLEARPBA_S)
#define PF_GLINT_DYN_CTL_SWINT_TRIG_S 2
#define PF_GLINT_DYN_CTL_SWINT_TRIG_M BIT(PF_GLINT_DYN_CTL_SWINT_TRIG_S)
#define PF_GLINT_DYN_CTL_ITR_INDX_S 3
#define PF_GLINT_DYN_CTL_ITR_INDX_M GENMASK(4, 3)
#define PF_GLINT_DYN_CTL_INTERVAL_S 5
#define PF_GLINT_DYN_CTL_INTERVAL_M BIT(PF_GLINT_DYN_CTL_INTERVAL_S)
#define PF_GLINT_DYN_CTL_SW_ITR_INDX_ENA_S 24
#define PF_GLINT_DYN_CTL_SW_ITR_INDX_ENA_M BIT(PF_GLINT_DYN_CTL_SW_ITR_INDX_ENA_S)
#define PF_GLINT_DYN_CTL_SW_ITR_INDX_S 25
#define PF_GLINT_DYN_CTL_SW_ITR_INDX_M BIT(PF_GLINT_DYN_CTL_SW_ITR_INDX_S)
#define PF_GLINT_DYN_CTL_WB_ON_ITR_S 30
#define PF_GLINT_DYN_CTL_WB_ON_ITR_M BIT(PF_GLINT_DYN_CTL_WB_ON_ITR_S)
#define PF_GLINT_DYN_CTL_INTENA_MSK_S 31
#define PF_GLINT_DYN_CTL_INTENA_MSK_M BIT(PF_GLINT_DYN_CTL_INTENA_MSK_S)
/* _ITR is ITR index, _INT is interrupt index, _itrn_indx_spacing is
* spacing b/w itrn registers of the same vector.
*/
#define PF_GLINT_ITR_ADDR(_ITR, _reg_start, _itrn_indx_spacing) \
((_reg_start) + ((_ITR) * (_itrn_indx_spacing)))
/* For PF, itrn_indx_spacing is 4 and itrn_reg_spacing is 0x1000 */
#define PF_GLINT_ITR(_ITR, _INT) \
(PF_GLINT_BASE + (((_ITR) + 1) * 4) + ((_INT) * 0x1000))
#define PF_GLINT_ITR_MAX_INDEX 2
#define PF_GLINT_ITR_INTERVAL_S 0
#define PF_GLINT_ITR_INTERVAL_M GENMASK(11, 0)
/* Generic registers */
#define PF_INT_DIR_OICR_ENA 0x08406000
#define PF_INT_DIR_OICR_ENA_S 0
#define PF_INT_DIR_OICR_ENA_M GENMASK(31, 0)
#define PF_INT_DIR_OICR 0x08406004
#define PF_INT_DIR_OICR_TSYN_EVNT 0
#define PF_INT_DIR_OICR_PHY_TS_0 BIT(1)
#define PF_INT_DIR_OICR_PHY_TS_1 BIT(2)
#define PF_INT_DIR_OICR_CAUSE 0x08406008
#define PF_INT_DIR_OICR_CAUSE_CAUSE_S 0
#define PF_INT_DIR_OICR_CAUSE_CAUSE_M GENMASK(31, 0)
#define PF_INT_PBA_CLEAR 0x0840600C
#define PF_FUNC_RID 0x08406010
#define PF_FUNC_RID_FUNCTION_NUMBER_S 0
#define PF_FUNC_RID_FUNCTION_NUMBER_M GENMASK(2, 0)
#define PF_FUNC_RID_DEVICE_NUMBER_S 3
#define PF_FUNC_RID_DEVICE_NUMBER_M GENMASK(7, 3)
#define PF_FUNC_RID_BUS_NUMBER_S 8
#define PF_FUNC_RID_BUS_NUMBER_M GENMASK(15, 8)
/* Reset registers */
#define PFGEN_RTRIG 0x08407000
#define PFGEN_RTRIG_CORER_S 0
#define PFGEN_RTRIG_CORER_M BIT(0)
#define PFGEN_RTRIG_LINKR_S 1
#define PFGEN_RTRIG_LINKR_M BIT(1)
#define PFGEN_RTRIG_IMCR_S 2
#define PFGEN_RTRIG_IMCR_M BIT(2)
#define PFGEN_RSTAT 0x08407008 /* PFR Status */
#define PFGEN_RSTAT_PFR_STATE_S 0
#define PFGEN_RSTAT_PFR_STATE_M GENMASK(1, 0)
#define PFGEN_CTRL 0x0840700C
#define PFGEN_CTRL_PFSWR BIT(0)
#endif
@@ -0,0 +1,310 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (C) 2023 Intel Corporation */
#ifndef _IDPF_LAN_TXRX_H_
#define _IDPF_LAN_TXRX_H_
#include <linux/bits.h>
enum idpf_rss_hash {
IDPF_HASH_INVALID = 0,
/* Values 1 - 28 are reserved for future use */
IDPF_HASH_NONF_UNICAST_IPV4_UDP = 29,
IDPF_HASH_NONF_MULTICAST_IPV4_UDP,
IDPF_HASH_NONF_IPV4_UDP,
IDPF_HASH_NONF_IPV4_TCP_SYN_NO_ACK,
IDPF_HASH_NONF_IPV4_TCP,
IDPF_HASH_NONF_IPV4_SCTP,
IDPF_HASH_NONF_IPV4_OTHER,
IDPF_HASH_FRAG_IPV4,
/* Values 37-38 are reserved */
IDPF_HASH_NONF_UNICAST_IPV6_UDP = 39,
IDPF_HASH_NONF_MULTICAST_IPV6_UDP,
IDPF_HASH_NONF_IPV6_UDP,
IDPF_HASH_NONF_IPV6_TCP_SYN_NO_ACK,
IDPF_HASH_NONF_IPV6_TCP,
IDPF_HASH_NONF_IPV6_SCTP,
IDPF_HASH_NONF_IPV6_OTHER,
IDPF_HASH_FRAG_IPV6,
IDPF_HASH_NONF_RSVD47,
IDPF_HASH_NONF_FCOE_OX,
IDPF_HASH_NONF_FCOE_RX,
IDPF_HASH_NONF_FCOE_OTHER,
/* Values 51-62 are reserved */
IDPF_HASH_L2_PAYLOAD = 63,
IDPF_HASH_MAX
};
/* Supported RSS offloads */
#define IDPF_DEFAULT_RSS_HASH \
(BIT_ULL(IDPF_HASH_NONF_IPV4_UDP) | \
BIT_ULL(IDPF_HASH_NONF_IPV4_SCTP) | \
BIT_ULL(IDPF_HASH_NONF_IPV4_TCP) | \
BIT_ULL(IDPF_HASH_NONF_IPV4_OTHER) | \
BIT_ULL(IDPF_HASH_FRAG_IPV4) | \
BIT_ULL(IDPF_HASH_NONF_IPV6_UDP) | \
BIT_ULL(IDPF_HASH_NONF_IPV6_TCP) | \
BIT_ULL(IDPF_HASH_NONF_IPV6_SCTP) | \
BIT_ULL(IDPF_HASH_NONF_IPV6_OTHER) | \
BIT_ULL(IDPF_HASH_FRAG_IPV6) | \
BIT_ULL(IDPF_HASH_L2_PAYLOAD))
#define IDPF_DEFAULT_RSS_HASH_EXPANDED (IDPF_DEFAULT_RSS_HASH | \
BIT_ULL(IDPF_HASH_NONF_IPV4_TCP_SYN_NO_ACK) | \
BIT_ULL(IDPF_HASH_NONF_UNICAST_IPV4_UDP) | \
BIT_ULL(IDPF_HASH_NONF_MULTICAST_IPV4_UDP) | \
BIT_ULL(IDPF_HASH_NONF_IPV6_TCP_SYN_NO_ACK) | \
BIT_ULL(IDPF_HASH_NONF_UNICAST_IPV6_UDP) | \
BIT_ULL(IDPF_HASH_NONF_MULTICAST_IPV6_UDP))
/* For idpf_splitq_base_tx_compl_desc */
#define IDPF_TXD_COMPLQ_GEN_S 15
#define IDPF_TXD_COMPLQ_GEN_M BIT_ULL(IDPF_TXD_COMPLQ_GEN_S)
#define IDPF_TXD_COMPLQ_COMPL_TYPE_S 11
#define IDPF_TXD_COMPLQ_COMPL_TYPE_M GENMASK_ULL(13, 11)
#define IDPF_TXD_COMPLQ_QID_S 0
#define IDPF_TXD_COMPLQ_QID_M GENMASK_ULL(9, 0)
/* For base mode TX descriptors */
#define IDPF_TXD_CTX_QW0_TUNN_L4T_CS_S 23
#define IDPF_TXD_CTX_QW0_TUNN_L4T_CS_M BIT_ULL(IDPF_TXD_CTX_QW0_TUNN_L4T_CS_S)
#define IDPF_TXD_CTX_QW0_TUNN_DECTTL_S 19
#define IDPF_TXD_CTX_QW0_TUNN_DECTTL_M \
(0xFULL << IDPF_TXD_CTX_QW0_TUNN_DECTTL_S)
#define IDPF_TXD_CTX_QW0_TUNN_NATLEN_S 12
#define IDPF_TXD_CTX_QW0_TUNN_NATLEN_M \
(0X7FULL << IDPF_TXD_CTX_QW0_TUNN_NATLEN_S)
#define IDPF_TXD_CTX_QW0_TUNN_EIP_NOINC_S 11
#define IDPF_TXD_CTX_QW0_TUNN_EIP_NOINC_M \
BIT_ULL(IDPF_TXD_CTX_QW0_TUNN_EIP_NOINC_S)
#define IDPF_TXD_CTX_EIP_NOINC_IPID_CONST \
IDPF_TXD_CTX_QW0_TUNN_EIP_NOINC_M
#define IDPF_TXD_CTX_QW0_TUNN_NATT_S 9
#define IDPF_TXD_CTX_QW0_TUNN_NATT_M (0x3ULL << IDPF_TXD_CTX_QW0_TUNN_NATT_S)
#define IDPF_TXD_CTX_UDP_TUNNELING BIT_ULL(IDPF_TXD_CTX_QW0_TUNN_NATT_S)
#define IDPF_TXD_CTX_GRE_TUNNELING (0x2ULL << IDPF_TXD_CTX_QW0_TUNN_NATT_S)
#define IDPF_TXD_CTX_QW0_TUNN_EXT_IPLEN_S 2
#define IDPF_TXD_CTX_QW0_TUNN_EXT_IPLEN_M \
(0x3FULL << IDPF_TXD_CTX_QW0_TUNN_EXT_IPLEN_S)
#define IDPF_TXD_CTX_QW0_TUNN_EXT_IP_S 0
#define IDPF_TXD_CTX_QW0_TUNN_EXT_IP_M \
(0x3ULL << IDPF_TXD_CTX_QW0_TUNN_EXT_IP_S)
#define IDPF_TXD_CTX_QW1_MSS_S 50
#define IDPF_TXD_CTX_QW1_MSS_M GENMASK_ULL(63, 50)
#define IDPF_TXD_CTX_QW1_TSO_LEN_S 30
#define IDPF_TXD_CTX_QW1_TSO_LEN_M GENMASK_ULL(47, 30)
#define IDPF_TXD_CTX_QW1_CMD_S 4
#define IDPF_TXD_CTX_QW1_CMD_M GENMASK_ULL(15, 4)
#define IDPF_TXD_CTX_QW1_DTYPE_S 0
#define IDPF_TXD_CTX_QW1_DTYPE_M GENMASK_ULL(3, 0)
#define IDPF_TXD_QW1_L2TAG1_S 48
#define IDPF_TXD_QW1_L2TAG1_M GENMASK_ULL(63, 48)
#define IDPF_TXD_QW1_TX_BUF_SZ_S 34
#define IDPF_TXD_QW1_TX_BUF_SZ_M GENMASK_ULL(47, 34)
#define IDPF_TXD_QW1_OFFSET_S 16
#define IDPF_TXD_QW1_OFFSET_M GENMASK_ULL(33, 16)
#define IDPF_TXD_QW1_CMD_S 4
#define IDPF_TXD_QW1_CMD_M GENMASK_ULL(15, 4)
#define IDPF_TXD_QW1_DTYPE_S 0
#define IDPF_TXD_QW1_DTYPE_M GENMASK_ULL(3, 0)
/* TX Completion Descriptor Completion Types */
#define IDPF_TXD_COMPLT_ITR_FLUSH 0
/* Descriptor completion type 1 is reserved */
#define IDPF_TXD_COMPLT_RS 2
/* Descriptor completion type 3 is reserved */
#define IDPF_TXD_COMPLT_RE 4
#define IDPF_TXD_COMPLT_SW_MARKER 5
enum idpf_tx_desc_dtype_value {
IDPF_TX_DESC_DTYPE_DATA = 0,
IDPF_TX_DESC_DTYPE_CTX = 1,
/* DTYPE 2 is reserved
* DTYPE 3 is free for future use
* DTYPE 4 is reserved
*/
IDPF_TX_DESC_DTYPE_FLEX_TSO_CTX = 5,
/* DTYPE 6 is reserved */
IDPF_TX_DESC_DTYPE_FLEX_L2TAG1_L2TAG2 = 7,
/* DTYPE 8, 9 are free for future use
* DTYPE 10 is reserved
* DTYPE 11 is free for future use
*/
IDPF_TX_DESC_DTYPE_FLEX_FLOW_SCHE = 12,
/* DTYPE 13, 14 are free for future use */
/* DESC_DONE - HW has completed write-back of descriptor */
IDPF_TX_DESC_DTYPE_DESC_DONE = 15,
};
enum idpf_tx_ctx_desc_cmd_bits {
IDPF_TX_CTX_DESC_TSO = 0x01,
IDPF_TX_CTX_DESC_TSYN = 0x02,
IDPF_TX_CTX_DESC_IL2TAG2 = 0x04,
IDPF_TX_CTX_DESC_RSVD = 0x08,
IDPF_TX_CTX_DESC_SWTCH_NOTAG = 0x00,
IDPF_TX_CTX_DESC_SWTCH_UPLINK = 0x10,
IDPF_TX_CTX_DESC_SWTCH_LOCAL = 0x20,
IDPF_TX_CTX_DESC_SWTCH_VSI = 0x30,
IDPF_TX_CTX_DESC_FILT_AU_EN = 0x40,
IDPF_TX_CTX_DESC_FILT_AU_EVICT = 0x80,
IDPF_TX_CTX_DESC_RSVD1 = 0xF00
};
enum idpf_tx_desc_len_fields {
/* Note: These are predefined bit offsets */
IDPF_TX_DESC_LEN_MACLEN_S = 0, /* 7 BITS */
IDPF_TX_DESC_LEN_IPLEN_S = 7, /* 7 BITS */
IDPF_TX_DESC_LEN_L4_LEN_S = 14 /* 4 BITS */
};
enum idpf_tx_base_desc_cmd_bits {
IDPF_TX_DESC_CMD_EOP = BIT(0),
IDPF_TX_DESC_CMD_RS = BIT(1),
/* only on VFs else RSVD */
IDPF_TX_DESC_CMD_ICRC = BIT(2),
IDPF_TX_DESC_CMD_IL2TAG1 = BIT(3),
IDPF_TX_DESC_CMD_RSVD1 = BIT(4),
IDPF_TX_DESC_CMD_IIPT_IPV6 = BIT(5),
IDPF_TX_DESC_CMD_IIPT_IPV4 = BIT(6),
IDPF_TX_DESC_CMD_IIPT_IPV4_CSUM = GENMASK(6, 5),
IDPF_TX_DESC_CMD_RSVD2 = BIT(7),
IDPF_TX_DESC_CMD_L4T_EOFT_TCP = BIT(8),
IDPF_TX_DESC_CMD_L4T_EOFT_SCTP = BIT(9),
IDPF_TX_DESC_CMD_L4T_EOFT_UDP = GENMASK(9, 8),
IDPF_TX_DESC_CMD_RSVD3 = BIT(10),
IDPF_TX_DESC_CMD_RSVD4 = BIT(11),
};
/* Transmit descriptors */
/* splitq tx buf, singleq tx buf and singleq compl desc */
struct idpf_base_tx_desc {
__le64 buf_addr; /* Address of descriptor's data buf */
__le64 qw1; /* type_cmd_offset_bsz_l2tag1 */
}; /* read used with buffer queues */
struct idpf_splitq_4b_tx_compl_desc {
/* qid=[10:0] comptype=[13:11] rsvd=[14] gen=[15] */
__le16 qid_comptype_gen;
union {
__le16 q_head; /* Queue head */
__le16 compl_tag; /* Completion tag */
} q_head_compl_tag;
}; /* writeback used with completion queues */
struct idpf_splitq_tx_compl_desc {
struct idpf_splitq_4b_tx_compl_desc common;
u8 ts[3];
u8 rsvd; /* Reserved */
}; /* writeback used with completion queues */
/* Context descriptors */
struct idpf_base_tx_ctx_desc {
struct {
__le32 tunneling_params;
__le16 l2tag2;
__le16 rsvd1;
} qw0;
__le64 qw1; /* type_cmd_tlen_mss/rt_hint */
};
/* Common cmd field defines for all desc except Flex Flow Scheduler (0x0C) */
enum idpf_tx_flex_desc_cmd_bits {
IDPF_TX_FLEX_DESC_CMD_EOP = BIT(0),
IDPF_TX_FLEX_DESC_CMD_RS = BIT(1),
IDPF_TX_FLEX_DESC_CMD_RE = BIT(2),
IDPF_TX_FLEX_DESC_CMD_IL2TAG1 = BIT(3),
IDPF_TX_FLEX_DESC_CMD_DUMMY = BIT(4),
IDPF_TX_FLEX_DESC_CMD_CS_EN = BIT(5),
IDPF_TX_FLEX_DESC_CMD_FILT_AU_EN = BIT(6),
IDPF_TX_FLEX_DESC_CMD_FILT_AU_EVICT = BIT(7),
};
struct idpf_flex_tx_desc {
__le64 buf_addr; /* Packet buffer address */
struct {
#define IDPF_FLEX_TXD_QW1_DTYPE_S 0
#define IDPF_FLEX_TXD_QW1_DTYPE_M GENMASK(4, 0)
#define IDPF_FLEX_TXD_QW1_CMD_S 5
#define IDPF_FLEX_TXD_QW1_CMD_M GENMASK(15, 5)
__le16 cmd_dtype;
/* DTYPE=IDPF_TX_DESC_DTYPE_FLEX_L2TAG1_L2TAG2 (0x07) */
struct {
__le16 l2tag1;
__le16 l2tag2;
} l2tags;
__le16 buf_size;
} qw1;
};
struct idpf_flex_tx_sched_desc {
__le64 buf_addr; /* Packet buffer address */
/* DTYPE = IDPF_TX_DESC_DTYPE_FLEX_FLOW_SCHE_16B (0x0C) */
struct {
u8 cmd_dtype;
#define IDPF_TXD_FLEX_FLOW_DTYPE_M GENMASK(4, 0)
#define IDPF_TXD_FLEX_FLOW_CMD_EOP BIT(5)
#define IDPF_TXD_FLEX_FLOW_CMD_CS_EN BIT(6)
#define IDPF_TXD_FLEX_FLOW_CMD_RE BIT(7)
/* [23:23] Horizon Overflow bit, [22:0] timestamp */
u8 ts[3];
#define IDPF_TXD_FLOW_SCH_HORIZON_OVERFLOW_M BIT(7)
__le16 compl_tag;
__le16 rxr_bufsize;
#define IDPF_TXD_FLEX_FLOW_RXR BIT(14)
#define IDPF_TXD_FLEX_FLOW_BUFSIZE_M GENMASK(13, 0)
} qw1;
};
/* Common cmd fields for all flex context descriptors
* Note: these defines already account for the 5 bit dtype in the cmd_dtype
* field
*/
enum idpf_tx_flex_ctx_desc_cmd_bits {
IDPF_TX_FLEX_CTX_DESC_CMD_TSO = BIT(5),
IDPF_TX_FLEX_CTX_DESC_CMD_TSYN_EN = BIT(6),
IDPF_TX_FLEX_CTX_DESC_CMD_L2TAG2 = BIT(7),
IDPF_TX_FLEX_CTX_DESC_CMD_SWTCH_UPLNK = BIT(9),
IDPF_TX_FLEX_CTX_DESC_CMD_SWTCH_LOCAL = BIT(10),
IDPF_TX_FLEX_CTX_DESC_CMD_SWTCH_TARGETVSI = GENMASK(10, 9),
};
/* Standard flex descriptor TSO context quad word */
struct idpf_flex_tx_tso_ctx_qw {
__le32 flex_tlen;
#define IDPF_TXD_FLEX_CTX_TLEN_M GENMASK(17, 0)
#define IDPF_TXD_FLEX_TSO_CTX_FLEX_S 24
__le16 mss_rt;
#define IDPF_TXD_FLEX_CTX_MSS_RT_M GENMASK(13, 0)
u8 hdr_len;
u8 flex;
};
union idpf_flex_tx_ctx_desc {
/* DTYPE = IDPF_TX_DESC_DTYPE_CTX (0x01) */
struct {
__le64 qw0;
#define IDPF_TX_CTX_L2TAG2_M GENMASK_ULL(47, 32)
__le64 qw1;
#define IDPF_TX_CTX_DTYPE_M GENMASK_ULL(3, 0)
#define IDPF_TX_CTX_CMD_M GENMASK_ULL(15, 4)
#define IDPF_TX_CTX_TSYN_REG_M GENMASK_ULL(47, 30)
#define IDPF_TX_CTX_MSS_M GENMASK_ULL(50, 63)
} tsyn;
/* DTYPE = IDPF_TX_DESC_DTYPE_FLEX_TSO_CTX (0x05) */
struct {
struct idpf_flex_tx_tso_ctx_qw qw0;
struct {
__le16 cmd_dtype;
u8 flex[6];
} qw1;
} tso;
};
#endif /* _IDPF_LAN_TXRX_H_ */
@@ -0,0 +1,128 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (C) 2023 Intel Corporation */
#ifndef _IDPF_LAN_VF_REGS_H_
#define _IDPF_LAN_VF_REGS_H_
/* Reset */
#define VFGEN_RSTAT 0x00008800
#define VFGEN_RSTAT_VFR_STATE_S 0
#define VFGEN_RSTAT_VFR_STATE_M GENMASK(1, 0)
/* Control(VF Mailbox) Queue */
#define VF_BASE 0x00006000
#define VF_ATQBAL (VF_BASE + 0x1C00)
#define VF_ATQBAH (VF_BASE + 0x1800)
#define VF_ATQLEN (VF_BASE + 0x0800)
#define VF_ATQLEN_ATQLEN_S 0
#define VF_ATQLEN_ATQLEN_M GENMASK(9, 0)
#define VF_ATQLEN_ATQVFE_S 28
#define VF_ATQLEN_ATQVFE_M BIT(VF_ATQLEN_ATQVFE_S)
#define VF_ATQLEN_ATQOVFL_S 29
#define VF_ATQLEN_ATQOVFL_M BIT(VF_ATQLEN_ATQOVFL_S)
#define VF_ATQLEN_ATQCRIT_S 30
#define VF_ATQLEN_ATQCRIT_M BIT(VF_ATQLEN_ATQCRIT_S)
#define VF_ATQLEN_ATQENABLE_S 31
#define VF_ATQLEN_ATQENABLE_M BIT(VF_ATQLEN_ATQENABLE_S)
#define VF_ATQH (VF_BASE + 0x0400)
#define VF_ATQH_ATQH_S 0
#define VF_ATQH_ATQH_M GENMASK(9, 0)
#define VF_ATQT (VF_BASE + 0x2400)
#define VF_ARQBAL (VF_BASE + 0x0C00)
#define VF_ARQBAH (VF_BASE)
#define VF_ARQLEN (VF_BASE + 0x2000)
#define VF_ARQLEN_ARQLEN_S 0
#define VF_ARQLEN_ARQLEN_M GENMASK(9, 0)
#define VF_ARQLEN_ARQVFE_S 28
#define VF_ARQLEN_ARQVFE_M BIT(VF_ARQLEN_ARQVFE_S)
#define VF_ARQLEN_ARQOVFL_S 29
#define VF_ARQLEN_ARQOVFL_M BIT(VF_ARQLEN_ARQOVFL_S)
#define VF_ARQLEN_ARQCRIT_S 30
#define VF_ARQLEN_ARQCRIT_M BIT(VF_ARQLEN_ARQCRIT_S)
#define VF_ARQLEN_ARQENABLE_S 31
#define VF_ARQLEN_ARQENABLE_M BIT(VF_ARQLEN_ARQENABLE_S)
#define VF_ARQH (VF_BASE + 0x1400)
#define VF_ARQH_ARQH_S 0
#define VF_ARQH_ARQH_M GENMASK(12, 0)
#define VF_ARQT (VF_BASE + 0x1000)
/* Transmit queues */
#define VF_QTX_TAIL_BASE 0x00000000
#define VF_QTX_TAIL(_QTX) (VF_QTX_TAIL_BASE + (_QTX) * 0x4)
#define VF_QTX_TAIL_EXT_BASE 0x00040000
#define VF_QTX_TAIL_EXT(_QTX) (VF_QTX_TAIL_EXT_BASE + ((_QTX) * 4))
/* Receive queues */
#define VF_QRX_TAIL_BASE 0x00002000
#define VF_QRX_TAIL(_QRX) (VF_QRX_TAIL_BASE + ((_QRX) * 4))
#define VF_QRX_TAIL_EXT_BASE 0x00050000
#define VF_QRX_TAIL_EXT(_QRX) (VF_QRX_TAIL_EXT_BASE + ((_QRX) * 4))
#define VF_QRXB_TAIL_BASE 0x00060000
#define VF_QRXB_TAIL(_QRX) (VF_QRXB_TAIL_BASE + ((_QRX) * 4))
/* Interrupts */
#define VF_INT_DYN_CTL0 0x00005C00
#define VF_INT_DYN_CTL0_INTENA_S 0
#define VF_INT_DYN_CTL0_INTENA_M BIT(VF_INT_DYN_CTL0_INTENA_S)
#define VF_INT_DYN_CTL0_ITR_INDX_S 3
#define VF_INT_DYN_CTL0_ITR_INDX_M GENMASK(4, 3)
#define VF_INT_DYN_CTLN(_INT) (0x00003800 + ((_INT) * 4))
#define VF_INT_DYN_CTLN_EXT(_INT) (0x00070000 + ((_INT) * 4))
#define VF_INT_DYN_CTLN_INTENA_S 0
#define VF_INT_DYN_CTLN_INTENA_M BIT(VF_INT_DYN_CTLN_INTENA_S)
#define VF_INT_DYN_CTLN_CLEARPBA_S 1
#define VF_INT_DYN_CTLN_CLEARPBA_M BIT(VF_INT_DYN_CTLN_CLEARPBA_S)
#define VF_INT_DYN_CTLN_SWINT_TRIG_S 2
#define VF_INT_DYN_CTLN_SWINT_TRIG_M BIT(VF_INT_DYN_CTLN_SWINT_TRIG_S)
#define VF_INT_DYN_CTLN_ITR_INDX_S 3
#define VF_INT_DYN_CTLN_ITR_INDX_M GENMASK(4, 3)
#define VF_INT_DYN_CTLN_INTERVAL_S 5
#define VF_INT_DYN_CTLN_INTERVAL_M BIT(VF_INT_DYN_CTLN_INTERVAL_S)
#define VF_INT_DYN_CTLN_SW_ITR_INDX_ENA_S 24
#define VF_INT_DYN_CTLN_SW_ITR_INDX_ENA_M BIT(VF_INT_DYN_CTLN_SW_ITR_INDX_ENA_S)
#define VF_INT_DYN_CTLN_SW_ITR_INDX_S 25
#define VF_INT_DYN_CTLN_SW_ITR_INDX_M BIT(VF_INT_DYN_CTLN_SW_ITR_INDX_S)
#define VF_INT_DYN_CTLN_WB_ON_ITR_S 30
#define VF_INT_DYN_CTLN_WB_ON_ITR_M BIT(VF_INT_DYN_CTLN_WB_ON_ITR_S)
#define VF_INT_DYN_CTLN_INTENA_MSK_S 31
#define VF_INT_DYN_CTLN_INTENA_MSK_M BIT(VF_INT_DYN_CTLN_INTENA_MSK_S)
/* _ITR is ITR index, _INT is interrupt index, _itrn_indx_spacing is spacing
* b/w itrn registers of the same vector
*/
#define VF_INT_ITR0(_ITR) (0x00004C00 + ((_ITR) * 4))
#define VF_INT_ITRN_ADDR(_ITR, _reg_start, _itrn_indx_spacing) \
((_reg_start) + ((_ITR) * (_itrn_indx_spacing)))
/* For VF with 16 vector support, itrn_reg_spacing is 0x4, itrn_indx_spacing
* is 0x40 and base register offset is 0x00002800
*/
#define VF_INT_ITRN(_INT, _ITR) \
(0x00002800 + ((_INT) * 4) + ((_ITR) * 0x40))
/* For VF with 64 vector support, itrn_reg_spacing is 0x4, itrn_indx_spacing
* is 0x100 and base register offset is 0x00002C00
*/
#define VF_INT_ITRN_64(_INT, _ITR) \
(0x00002C00 + ((_INT) * 4) + ((_ITR) * 0x100))
/* For VF with 2k vector support, itrn_reg_spacing is 0x4, itrn_indx_spacing
* is 0x2000 and base register offset is 0x00072000
*/
#define VF_INT_ITRN_2K(_INT, _ITR) \
(0x00072000 + ((_INT) * 4) + ((_ITR) * 0x2000))
#define VF_INT_ITRN_MAX_INDEX 2
#define VF_INT_ITRN_INTERVAL_S 0
#define VF_INT_ITRN_INTERVAL_M GENMASK(11, 0)
#define VF_INT_PBA_CLEAR 0x00008900
#define VF_INT_ICR0_ENA1 0x00005000
#define VF_INT_ICR0_ENA1_ADMINQ_S 30
#define VF_INT_ICR0_ENA1_ADMINQ_M BIT(VF_INT_ICR0_ENA1_ADMINQ_S)
#define VF_INT_ICR0_ENA1_RSVD_S 31
#define VF_INT_ICR01 0x00004800
#define VF_QF_HENA(_i) (0x0000C400 + ((_i) * 4))
#define VF_QF_HENA_MAX_INDX 1
#define VF_QF_HKEY(_i) (0x0000CC00 + ((_i) * 4))
#define VF_QF_HKEY_MAX_INDX 12
#define VF_QF_HLUT(_i) (0x0000D000 + ((_i) * 4))
#define VF_QF_HLUT_MAX_INDX 15
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,392 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2023 Intel Corporation */
#include "idpf.h"
#include "idpf_devids.h"
#include "idpf_lan_vf_regs.h"
#include "idpf_virtchnl.h"
#define DRV_SUMMARY "Intel(R) Infrastructure Data Path Function Linux Driver"
#define IDPF_NETWORK_ETHERNET_PROGIF 0x01
#define IDPF_CLASS_NETWORK_ETHERNET_PROGIF \
(PCI_CLASS_NETWORK_ETHERNET << 8 | IDPF_NETWORK_ETHERNET_PROGIF)
#define IDPF_VF_TEST_VAL 0xfeed0000u
MODULE_DESCRIPTION(DRV_SUMMARY);
MODULE_IMPORT_NS("LIBETH");
MODULE_IMPORT_NS("LIBETH_XDP");
MODULE_LICENSE("GPL");
/**
* idpf_get_device_type - Helper to find if it is a VF or PF device
* @pdev: PCI device information struct
*
* Return: PF/VF device ID or -%errno on failure.
*/
static int idpf_get_device_type(struct pci_dev *pdev)
{
void __iomem *addr;
int ret;
addr = ioremap(pci_resource_start(pdev, 0) + VF_ARQBAL, 4);
if (!addr) {
pci_err(pdev, "Failed to allocate BAR0 mbx region\n");
return -EIO;
}
writel(IDPF_VF_TEST_VAL, addr);
if (readl(addr) == IDPF_VF_TEST_VAL)
ret = IDPF_DEV_ID_VF;
else
ret = IDPF_DEV_ID_PF;
iounmap(addr);
return ret;
}
/**
* idpf_dev_init - Initialize device specific parameters
* @adapter: adapter to initialize
* @ent: entry in idpf_pci_tbl
*
* Return: %0 on success, -%errno on failure.
*/
static int idpf_dev_init(struct idpf_adapter *adapter,
const struct pci_device_id *ent)
{
int ret;
if (ent->class == IDPF_CLASS_NETWORK_ETHERNET_PROGIF) {
ret = idpf_get_device_type(adapter->pdev);
switch (ret) {
case IDPF_DEV_ID_VF:
idpf_vf_dev_ops_init(adapter);
adapter->crc_enable = true;
break;
case IDPF_DEV_ID_PF:
idpf_dev_ops_init(adapter);
break;
default:
return ret;
}
return 0;
}
switch (ent->device) {
case IDPF_DEV_ID_PF:
idpf_dev_ops_init(adapter);
break;
case IDPF_DEV_ID_VF:
idpf_vf_dev_ops_init(adapter);
adapter->crc_enable = true;
break;
default:
return -ENODEV;
}
return 0;
}
/**
* idpf_remove - Device removal routine
* @pdev: PCI device information struct
*/
static void idpf_remove(struct pci_dev *pdev)
{
struct idpf_adapter *adapter = pci_get_drvdata(pdev);
int i;
set_bit(IDPF_REMOVE_IN_PROG, adapter->flags);
/* Wait until vc_event_task is done to consider if any hard reset is
* in progress else we may go ahead and release the resources but the
* thread doing the hard reset might continue the init path and
* end up in bad state.
*/
cancel_delayed_work_sync(&adapter->vc_event_task);
if (adapter->num_vfs)
idpf_sriov_configure(pdev, 0);
idpf_vc_core_deinit(adapter);
/* Be a good citizen and leave the device clean on exit */
adapter->dev_ops.reg_ops.trigger_reset(adapter, IDPF_HR_FUNC_RESET);
idpf_deinit_dflt_mbx(adapter);
if (!adapter->netdevs)
goto destroy_wqs;
/* There are some cases where it's possible to still have netdevs
* registered with the stack at this point, e.g. if the driver detected
* a HW reset and rmmod is called before it fully recovers. Unregister
* any stale netdevs here.
*/
for (i = 0; i < adapter->max_vports; i++) {
if (!adapter->netdevs[i])
continue;
if (adapter->netdevs[i]->reg_state != NETREG_UNINITIALIZED)
unregister_netdev(adapter->netdevs[i]);
free_netdev(adapter->netdevs[i]);
adapter->netdevs[i] = NULL;
}
destroy_wqs:
destroy_workqueue(adapter->init_wq);
destroy_workqueue(adapter->serv_wq);
destroy_workqueue(adapter->mbx_wq);
destroy_workqueue(adapter->stats_wq);
destroy_workqueue(adapter->vc_event_wq);
for (i = 0; i < adapter->max_vports; i++) {
if (!adapter->vport_config[i])
continue;
kfree(adapter->vport_config[i]->user_config.q_coalesce);
kfree(adapter->vport_config[i]);
adapter->vport_config[i] = NULL;
}
kfree(adapter->vport_config);
adapter->vport_config = NULL;
kfree(adapter->netdevs);
adapter->netdevs = NULL;
kfree(adapter->vcxn_mngr);
adapter->vcxn_mngr = NULL;
mutex_destroy(&adapter->vport_ctrl_lock);
mutex_destroy(&adapter->vector_lock);
mutex_destroy(&adapter->queue_lock);
mutex_destroy(&adapter->vc_buf_lock);
pci_set_drvdata(pdev, NULL);
kfree(adapter);
}
/**
* idpf_shutdown - PCI callback for shutting down device
* @pdev: PCI device information struct
*/
static void idpf_shutdown(struct pci_dev *pdev)
{
struct idpf_adapter *adapter = pci_get_drvdata(pdev);
cancel_delayed_work_sync(&adapter->serv_task);
cancel_delayed_work_sync(&adapter->vc_event_task);
idpf_vc_core_deinit(adapter);
idpf_deinit_dflt_mbx(adapter);
if (system_state == SYSTEM_POWER_OFF)
pci_set_power_state(pdev, PCI_D3hot);
}
/**
* idpf_cfg_hw - Initialize HW struct
* @adapter: adapter to setup hw struct for
*
* Returns 0 on success, negative on failure
*/
static int idpf_cfg_hw(struct idpf_adapter *adapter)
{
resource_size_t res_start, mbx_start, rstat_start;
struct pci_dev *pdev = adapter->pdev;
struct idpf_hw *hw = &adapter->hw;
struct device *dev = &pdev->dev;
long len;
res_start = pci_resource_start(pdev, 0);
/* Map mailbox space for virtchnl communication */
mbx_start = res_start + adapter->dev_ops.static_reg_info[0].start;
len = resource_size(&adapter->dev_ops.static_reg_info[0]);
hw->mbx.vaddr = devm_ioremap(dev, mbx_start, len);
if (!hw->mbx.vaddr) {
pci_err(pdev, "failed to allocate BAR0 mbx region\n");
return -ENOMEM;
}
hw->mbx.addr_start = adapter->dev_ops.static_reg_info[0].start;
hw->mbx.addr_len = len;
/* Map rstat space for resets */
rstat_start = res_start + adapter->dev_ops.static_reg_info[1].start;
len = resource_size(&adapter->dev_ops.static_reg_info[1]);
hw->rstat.vaddr = devm_ioremap(dev, rstat_start, len);
if (!hw->rstat.vaddr) {
pci_err(pdev, "failed to allocate BAR0 rstat region\n");
return -ENOMEM;
}
hw->rstat.addr_start = adapter->dev_ops.static_reg_info[1].start;
hw->rstat.addr_len = len;
hw->back = adapter;
return 0;
}
/**
* idpf_probe - Device initialization routine
* @pdev: PCI device information struct
* @ent: entry in idpf_pci_tbl
*
* Returns 0 on success, negative on failure
*/
static int idpf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct device *dev = &pdev->dev;
struct idpf_adapter *adapter;
int err;
adapter = kzalloc_obj(*adapter);
if (!adapter)
return -ENOMEM;
adapter->req_tx_splitq = true;
adapter->req_rx_splitq = true;
adapter->pdev = pdev;
err = pcim_enable_device(pdev);
if (err)
goto err_free;
err = pcim_request_region(pdev, 0, pci_name(pdev));
if (err) {
pci_err(pdev, "pcim_request_region failed %pe\n", ERR_PTR(err));
goto err_free;
}
err = pci_enable_ptm(pdev);
if (err)
pci_dbg(pdev, "PCIe PTM is not supported by PCIe bus/controller\n");
/* set up for high or low dma */
err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
if (err) {
pci_err(pdev, "DMA configuration failed: %pe\n", ERR_PTR(err));
goto err_free;
}
pci_set_master(pdev);
pci_set_drvdata(pdev, adapter);
adapter->init_wq = alloc_workqueue("%s-%s-init",
WQ_UNBOUND | WQ_MEM_RECLAIM, 0,
dev_driver_string(dev),
dev_name(dev));
if (!adapter->init_wq) {
dev_err(dev, "Failed to allocate init workqueue\n");
err = -ENOMEM;
goto err_free;
}
adapter->serv_wq = alloc_workqueue("%s-%s-service",
WQ_UNBOUND | WQ_MEM_RECLAIM, 0,
dev_driver_string(dev),
dev_name(dev));
if (!adapter->serv_wq) {
dev_err(dev, "Failed to allocate service workqueue\n");
err = -ENOMEM;
goto err_serv_wq_alloc;
}
adapter->mbx_wq = alloc_workqueue("%s-%s-mbx", WQ_UNBOUND | WQ_HIGHPRI,
0, dev_driver_string(dev),
dev_name(dev));
if (!adapter->mbx_wq) {
dev_err(dev, "Failed to allocate mailbox workqueue\n");
err = -ENOMEM;
goto err_mbx_wq_alloc;
}
adapter->stats_wq = alloc_workqueue("%s-%s-stats",
WQ_UNBOUND | WQ_MEM_RECLAIM, 0,
dev_driver_string(dev),
dev_name(dev));
if (!adapter->stats_wq) {
dev_err(dev, "Failed to allocate workqueue\n");
err = -ENOMEM;
goto err_stats_wq_alloc;
}
adapter->vc_event_wq = alloc_workqueue("%s-%s-vc_event",
WQ_UNBOUND | WQ_MEM_RECLAIM, 0,
dev_driver_string(dev),
dev_name(dev));
if (!adapter->vc_event_wq) {
dev_err(dev, "Failed to allocate virtchnl event workqueue\n");
err = -ENOMEM;
goto err_vc_event_wq_alloc;
}
/* setup msglvl */
adapter->msg_enable = netif_msg_init(-1, IDPF_AVAIL_NETIF_M);
err = idpf_dev_init(adapter, ent);
if (err) {
dev_err(&pdev->dev, "Unexpected dev ID 0x%x in idpf probe\n",
ent->device);
goto destroy_vc_event_wq;
}
err = idpf_cfg_hw(adapter);
if (err) {
dev_err(dev, "Failed to configure HW structure for adapter: %d\n",
err);
goto destroy_vc_event_wq;
}
mutex_init(&adapter->vport_ctrl_lock);
mutex_init(&adapter->vector_lock);
mutex_init(&adapter->queue_lock);
mutex_init(&adapter->vc_buf_lock);
INIT_DELAYED_WORK(&adapter->init_task, idpf_init_task);
INIT_DELAYED_WORK(&adapter->serv_task, idpf_service_task);
INIT_DELAYED_WORK(&adapter->mbx_task, idpf_mbx_task);
INIT_DELAYED_WORK(&adapter->stats_task, idpf_statistics_task);
INIT_DELAYED_WORK(&adapter->vc_event_task, idpf_vc_event_task);
adapter->dev_ops.reg_ops.reset_reg_init(adapter);
set_bit(IDPF_HR_DRV_LOAD, adapter->flags);
queue_delayed_work(adapter->vc_event_wq, &adapter->vc_event_task,
msecs_to_jiffies(10 * (pdev->devfn & 0x07)));
return 0;
destroy_vc_event_wq:
destroy_workqueue(adapter->vc_event_wq);
err_vc_event_wq_alloc:
destroy_workqueue(adapter->stats_wq);
err_stats_wq_alloc:
destroy_workqueue(adapter->mbx_wq);
err_mbx_wq_alloc:
destroy_workqueue(adapter->serv_wq);
err_serv_wq_alloc:
destroy_workqueue(adapter->init_wq);
err_free:
kfree(adapter);
return err;
}
/* idpf_pci_tbl - PCI Dev idpf ID Table
*/
static const struct pci_device_id idpf_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, IDPF_DEV_ID_PF)},
{ PCI_VDEVICE(INTEL, IDPF_DEV_ID_VF)},
{ PCI_DEVICE_CLASS(IDPF_CLASS_NETWORK_ETHERNET_PROGIF, ~0)},
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(pci, idpf_pci_tbl);
static struct pci_driver idpf_driver = {
.name = KBUILD_MODNAME,
.id_table = idpf_pci_tbl,
.probe = idpf_probe,
.sriov_configure = idpf_sriov_configure,
.remove = idpf_remove,
.shutdown = idpf_shutdown,
};
module_pci_driver(idpf_driver);
@@ -0,0 +1,20 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (C) 2023 Intel Corporation */
#ifndef _IDPF_MEM_H_
#define _IDPF_MEM_H_
#include <linux/io.h>
struct idpf_dma_mem {
void *va;
dma_addr_t pa;
size_t size;
};
#define idpf_mbx_wr32(a, reg, value) writel((value), ((a)->mbx.vaddr + (reg)))
#define idpf_mbx_rd32(a, reg) readl((a)->mbx.vaddr + (reg))
#define idpf_mbx_wr64(a, reg, value) writeq((value), ((a)->mbx.vaddr + (reg)))
#define idpf_mbx_rd64(a, reg) readq((a)->mbx.vaddr + (reg))
#endif /* _IDPF_MEM_H_ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,379 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (C) 2024 Intel Corporation */
#ifndef _IDPF_PTP_H
#define _IDPF_PTP_H
#include <linux/ptp_clock_kernel.h>
/**
* struct idpf_ptp_cmd - PTP command masks
* @exec_cmd_mask: mask to trigger command execution
* @shtime_enable_mask: mask to enable shadow time
*/
struct idpf_ptp_cmd {
u32 exec_cmd_mask;
u32 shtime_enable_mask;
};
/* struct idpf_ptp_dev_clk_regs - PTP device registers
* @dev_clk_ns_l: low part of the device clock register
* @dev_clk_ns_h: high part of the device clock register
* @phy_clk_ns_l: low part of the PHY clock register
* @phy_clk_ns_h: high part of the PHY clock register
* @sys_time_ns_l: low part of the system time register
* @sys_time_ns_h: high part of the system time register
* @incval_l: low part of the increment value register
* @incval_h: high part of the increment value register
* @shadj_l: low part of the shadow adjust register
* @shadj_h: high part of the shadow adjust register
* @phy_incval_l: low part of the PHY increment value register
* @phy_incval_h: high part of the PHY increment value register
* @phy_shadj_l: low part of the PHY shadow adjust register
* @phy_shadj_h: high part of the PHY shadow adjust register
* @cmd: PTP command register
* @phy_cmd: PHY command register
* @cmd_sync: PTP command synchronization register
*/
struct idpf_ptp_dev_clk_regs {
/* Main clock */
void __iomem *dev_clk_ns_l;
void __iomem *dev_clk_ns_h;
/* PHY timer */
void __iomem *phy_clk_ns_l;
void __iomem *phy_clk_ns_h;
/* System time */
void __iomem *sys_time_ns_l;
void __iomem *sys_time_ns_h;
/* Main timer adjustments */
void __iomem *incval_l;
void __iomem *incval_h;
void __iomem *shadj_l;
void __iomem *shadj_h;
/* PHY timer adjustments */
void __iomem *phy_incval_l;
void __iomem *phy_incval_h;
void __iomem *phy_shadj_l;
void __iomem *phy_shadj_h;
/* Command */
void __iomem *cmd;
void __iomem *phy_cmd;
void __iomem *cmd_sync;
};
/**
* enum idpf_ptp_access - the type of access to PTP operations
* @IDPF_PTP_NONE: no access
* @IDPF_PTP_DIRECT: direct access through BAR registers
* @IDPF_PTP_MAILBOX: access through mailbox messages
*/
enum idpf_ptp_access {
IDPF_PTP_NONE = 0,
IDPF_PTP_DIRECT,
IDPF_PTP_MAILBOX,
};
/**
* struct idpf_ptp_secondary_mbx - PTP secondary mailbox
* @peer_mbx_q_id: PTP mailbox queue ID
* @peer_id: Peer ID for PTP Device Control daemon
* @valid: indicates whether secondary mailblox is supported by the Control
* Plane
*/
struct idpf_ptp_secondary_mbx {
u16 peer_mbx_q_id;
u16 peer_id;
bool valid:1;
};
/**
* enum idpf_ptp_tx_tstamp_state - Tx timestamp states
* @IDPF_PTP_FREE: Tx timestamp index free to use
* @IDPF_PTP_REQUEST: Tx timestamp index set to the Tx descriptor
* @IDPF_PTP_READ_VALUE: Tx timestamp value ready to be read
*/
enum idpf_ptp_tx_tstamp_state {
IDPF_PTP_FREE,
IDPF_PTP_REQUEST,
IDPF_PTP_READ_VALUE,
};
/**
* struct idpf_ptp_tx_tstamp_status - Parameters to track Tx timestamp
* @skb: the pointer to the SKB that received the completion tag
* @state: the state of the Tx timestamp
*/
struct idpf_ptp_tx_tstamp_status {
struct sk_buff *skb;
enum idpf_ptp_tx_tstamp_state state;
};
/**
* struct idpf_ptp_tx_tstamp - Parameters for Tx timestamping
* @list_member: the list member structure
* @tx_latch_reg_offset_l: Tx tstamp latch low register offset
* @tx_latch_reg_offset_h: Tx tstamp latch high register offset
* @skb: the pointer to the SKB for this timestamp request
* @tstamp: the Tx tstamp value
* @idx: the index of the Tx tstamp
*/
struct idpf_ptp_tx_tstamp {
struct list_head list_member;
u32 tx_latch_reg_offset_l;
u32 tx_latch_reg_offset_h;
struct sk_buff *skb;
u64 tstamp;
u32 idx;
};
/**
* struct idpf_ptp_vport_tx_tstamp_caps - Tx timestamp capabilities
* @vport_id: the vport id
* @num_entries: the number of negotiated Tx timestamp entries
* @tstamp_ns_lo_bit: first bit for nanosecond part of the timestamp
* @latches_lock: the lock to the lists of free/used timestamp indexes
* @status_lock: the lock to the status tracker
* @access: indicates an access to Tx timestamp
* @latches_free: the list of the free Tx timestamps latches
* @latches_in_use: the list of the used Tx timestamps latches
* @tx_tstamp_status: Tx tstamp status tracker
*/
struct idpf_ptp_vport_tx_tstamp_caps {
u32 vport_id;
u16 num_entries;
u16 tstamp_ns_lo_bit;
spinlock_t latches_lock;
spinlock_t status_lock;
bool access:1;
struct list_head latches_free;
struct list_head latches_in_use;
struct idpf_ptp_tx_tstamp_status tx_tstamp_status[];
};
/**
* struct idpf_ptp - PTP parameters
* @info: structure defining PTP hardware capabilities
* @clock: pointer to registered PTP clock device
* @adapter: back pointer to the adapter
* @base_incval: base increment value of the PTP clock
* @max_adj: maximum adjustment of the PTP clock
* @cmd: HW specific command masks
* @cached_phc_time: a cached copy of the PHC time for timestamp extension
* @cached_phc_jiffies: jiffies when cached_phc_time was last updated
* @dev_clk_regs: the set of registers to access the device clock
* @caps: PTP capabilities negotiated with the Control Plane
* @get_dev_clk_time_access: access type for getting the device clock time
* @get_cross_tstamp_access: access type for the cross timestamping
* @set_dev_clk_time_access: access type for setting the device clock time
* @adj_dev_clk_time_access: access type for the adjusting the device clock
* @tx_tstamp_access: access type for the Tx timestamp value read
* @rsv: reserved bits
* @secondary_mbx: parameters for using dedicated PTP mailbox
* @read_dev_clk_lock: spinlock protecting access to the device clock read
* operation executed by the HW latch
*/
struct idpf_ptp {
struct ptp_clock_info info;
struct ptp_clock *clock;
struct idpf_adapter *adapter;
u64 base_incval;
u64 max_adj;
struct idpf_ptp_cmd cmd;
u64 cached_phc_time;
unsigned long cached_phc_jiffies;
struct idpf_ptp_dev_clk_regs dev_clk_regs;
u32 caps;
enum idpf_ptp_access get_dev_clk_time_access:2;
enum idpf_ptp_access get_cross_tstamp_access:2;
enum idpf_ptp_access set_dev_clk_time_access:2;
enum idpf_ptp_access adj_dev_clk_time_access:2;
enum idpf_ptp_access tx_tstamp_access:2;
u8 rsv;
struct idpf_ptp_secondary_mbx secondary_mbx;
spinlock_t read_dev_clk_lock;
};
/**
* idpf_ptp_info_to_adapter - get driver adapter struct from ptp_clock_info
* @info: pointer to ptp_clock_info struct
*
* Return: pointer to the corresponding adapter struct
*/
static inline struct idpf_adapter *
idpf_ptp_info_to_adapter(const struct ptp_clock_info *info)
{
const struct idpf_ptp *ptp = container_of_const(info, struct idpf_ptp,
info);
return ptp->adapter;
}
/**
* struct idpf_ptp_dev_timers - System time and device time values
* @sys_time_ns: system time value expressed in nanoseconds
* @dev_clk_time_ns: device clock time value expressed in nanoseconds
*/
struct idpf_ptp_dev_timers {
u64 sys_time_ns;
u64 dev_clk_time_ns;
};
/**
* idpf_ptp_is_vport_tx_tstamp_ena - Verify the Tx timestamping enablement for
* a given vport.
* @vport: Virtual port structure
*
* Tx timestamp capabilities are negotiated with the Control Plane only if the
* device clock value can be read, Tx timestamp access type is different than
* NONE, and the PTP clock for the adapter is created. When all those conditions
* are satisfied, Tx timestamp feature is enabled and tx_tstamp_caps is
* allocated and fulfilled.
*
* Return: true if the Tx timestamping is enabled, false otherwise.
*/
static inline bool idpf_ptp_is_vport_tx_tstamp_ena(struct idpf_vport *vport)
{
if (!vport->tx_tstamp_caps)
return false;
else
return true;
}
/**
* idpf_ptp_is_vport_rx_tstamp_ena - Verify the Rx timestamping enablement for
* a given vport.
* @vport: Virtual port structure
*
* Rx timestamp feature is enabled if the PTP clock for the adapter is created
* and it is possible to read the value of the device clock. The second
* assumption comes from the need to extend the Rx timestamp value to 64 bit
* based on the current device clock time.
*
* Return: true if the Rx timestamping is enabled, false otherwise.
*/
static inline bool idpf_ptp_is_vport_rx_tstamp_ena(struct idpf_vport *vport)
{
if (!vport->adapter->ptp ||
vport->adapter->ptp->get_dev_clk_time_access == IDPF_PTP_NONE)
return false;
else
return true;
}
#if IS_ENABLED(CONFIG_PTP_1588_CLOCK)
int idpf_ptp_init(struct idpf_adapter *adapter);
void idpf_ptp_release(struct idpf_adapter *adapter);
int idpf_ptp_get_caps(struct idpf_adapter *adapter);
void idpf_ptp_get_features_access(const struct idpf_adapter *adapter);
bool idpf_ptp_get_txq_tstamp_capability(struct idpf_tx_queue *txq);
int idpf_ptp_get_dev_clk_time(struct idpf_adapter *adapter,
struct idpf_ptp_dev_timers *dev_clk_time);
int idpf_ptp_get_cross_time(struct idpf_adapter *adapter,
struct idpf_ptp_dev_timers *cross_time);
int idpf_ptp_set_dev_clk_time(struct idpf_adapter *adapter, u64 time);
int idpf_ptp_adj_dev_clk_fine(struct idpf_adapter *adapter, u64 incval);
int idpf_ptp_adj_dev_clk_time(struct idpf_adapter *adapter, s64 delta);
int idpf_ptp_get_vport_tstamps_caps(struct idpf_vport *vport);
int idpf_ptp_get_tx_tstamp(struct idpf_vport *vport);
int idpf_ptp_set_timestamp_mode(struct idpf_vport *vport,
struct kernel_hwtstamp_config *config);
u64 idpf_ptp_extend_ts(struct idpf_vport *vport, u64 in_tstamp);
u64 idpf_ptp_tstamp_extend_32b_to_64b(u64 cached_phc_time, u32 in_timestamp);
int idpf_ptp_request_ts(struct idpf_tx_queue *tx_q, struct sk_buff *skb,
u32 *idx);
void idpf_tstamp_task(struct work_struct *work);
#else /* CONFIG_PTP_1588_CLOCK */
static inline int idpf_ptp_init(struct idpf_adapter *adapter)
{
return 0;
}
static inline void idpf_ptp_release(struct idpf_adapter *adapter) { }
static inline int idpf_ptp_get_caps(struct idpf_adapter *adapter)
{
return -EOPNOTSUPP;
}
static inline void
idpf_ptp_get_features_access(const struct idpf_adapter *adapter) { }
static inline bool
idpf_ptp_get_txq_tstamp_capability(struct idpf_tx_queue *txq)
{
return false;
}
static inline int
idpf_ptp_get_dev_clk_time(struct idpf_adapter *adapter,
struct idpf_ptp_dev_timers *dev_clk_time)
{
return -EOPNOTSUPP;
}
static inline int
idpf_ptp_get_cross_time(struct idpf_adapter *adapter,
struct idpf_ptp_dev_timers *cross_time)
{
return -EOPNOTSUPP;
}
static inline int idpf_ptp_set_dev_clk_time(struct idpf_adapter *adapter,
u64 time)
{
return -EOPNOTSUPP;
}
static inline int idpf_ptp_adj_dev_clk_fine(struct idpf_adapter *adapter,
u64 incval)
{
return -EOPNOTSUPP;
}
static inline int idpf_ptp_adj_dev_clk_time(struct idpf_adapter *adapter,
s64 delta)
{
return -EOPNOTSUPP;
}
static inline int idpf_ptp_get_vport_tstamps_caps(struct idpf_vport *vport)
{
return -EOPNOTSUPP;
}
static inline int idpf_ptp_get_tx_tstamp(struct idpf_vport *vport)
{
return -EOPNOTSUPP;
}
static inline int
idpf_ptp_set_timestamp_mode(struct idpf_vport *vport,
struct kernel_hwtstamp_config *config)
{
return -EOPNOTSUPP;
}
static inline u64 idpf_ptp_extend_ts(struct idpf_vport *vport, u32 in_tstamp)
{
return 0;
}
static inline u64 idpf_ptp_tstamp_extend_32b_to_64b(u64 cached_phc_time,
u32 in_timestamp)
{
return 0;
}
static inline int idpf_ptp_request_ts(struct idpf_tx_queue *tx_q,
struct sk_buff *skb, u32 *idx)
{
return -EOPNOTSUPP;
}
static inline void idpf_tstamp_task(struct work_struct *work) { }
#endif /* CONFIG_PTP_1588_CLOCK */
#endif /* _IDPF_PTP_H */
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,202 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2023 Intel Corporation */
#include "idpf.h"
#include "idpf_lan_vf_regs.h"
#include "idpf_virtchnl.h"
#define IDPF_VF_ITR_IDX_SPACING 0x40
/**
* idpf_vf_ctlq_reg_init - initialize default mailbox registers
* @adapter: adapter structure
* @cq: pointer to the array of create control queues
*/
static void idpf_vf_ctlq_reg_init(struct idpf_adapter *adapter,
struct idpf_ctlq_create_info *cq)
{
resource_size_t mbx_start = adapter->dev_ops.static_reg_info[0].start;
int i;
for (i = 0; i < IDPF_NUM_DFLT_MBX_Q; i++) {
struct idpf_ctlq_create_info *ccq = cq + i;
switch (ccq->type) {
case IDPF_CTLQ_TYPE_MAILBOX_TX:
/* set head and tail registers in our local struct */
ccq->reg.head = VF_ATQH - mbx_start;
ccq->reg.tail = VF_ATQT - mbx_start;
ccq->reg.len = VF_ATQLEN - mbx_start;
ccq->reg.bah = VF_ATQBAH - mbx_start;
ccq->reg.bal = VF_ATQBAL - mbx_start;
ccq->reg.len_mask = VF_ATQLEN_ATQLEN_M;
ccq->reg.len_ena_mask = VF_ATQLEN_ATQENABLE_M;
ccq->reg.head_mask = VF_ATQH_ATQH_M;
break;
case IDPF_CTLQ_TYPE_MAILBOX_RX:
/* set head and tail registers in our local struct */
ccq->reg.head = VF_ARQH - mbx_start;
ccq->reg.tail = VF_ARQT - mbx_start;
ccq->reg.len = VF_ARQLEN - mbx_start;
ccq->reg.bah = VF_ARQBAH - mbx_start;
ccq->reg.bal = VF_ARQBAL - mbx_start;
ccq->reg.len_mask = VF_ARQLEN_ARQLEN_M;
ccq->reg.len_ena_mask = VF_ARQLEN_ARQENABLE_M;
ccq->reg.head_mask = VF_ARQH_ARQH_M;
break;
default:
break;
}
}
}
/**
* idpf_vf_mb_intr_reg_init - Initialize the mailbox register
* @adapter: adapter structure
*/
static void idpf_vf_mb_intr_reg_init(struct idpf_adapter *adapter)
{
struct idpf_intr_reg *intr = &adapter->mb_vector.intr_reg;
u32 dyn_ctl = le32_to_cpu(adapter->caps.mailbox_dyn_ctl);
intr->dyn_ctl = idpf_get_reg_addr(adapter, dyn_ctl);
intr->dyn_ctl_intena_m = VF_INT_DYN_CTL0_INTENA_M;
intr->dyn_ctl_itridx_m = VF_INT_DYN_CTL0_ITR_INDX_M;
intr->icr_ena = idpf_get_reg_addr(adapter, VF_INT_ICR0_ENA1);
intr->icr_ena_ctlq_m = VF_INT_ICR0_ENA1_ADMINQ_M;
}
/**
* idpf_vf_intr_reg_init - Initialize interrupt registers
* @vport: virtual port structure
* @rsrc: pointer to queue and vector resources
*/
static int idpf_vf_intr_reg_init(struct idpf_vport *vport,
struct idpf_q_vec_rsrc *rsrc)
{
struct idpf_adapter *adapter = vport->adapter;
u16 num_vecs = rsrc->num_q_vectors;
struct idpf_vec_regs *reg_vals;
int num_regs, i, err = 0;
u32 rx_itr, tx_itr, val;
u16 total_vecs;
total_vecs = idpf_get_reserved_vecs(vport->adapter);
reg_vals = kzalloc_objs(struct idpf_vec_regs, total_vecs);
if (!reg_vals)
return -ENOMEM;
num_regs = idpf_get_reg_intr_vecs(adapter, reg_vals, total_vecs);
if (num_regs < num_vecs) {
err = -EINVAL;
goto free_reg_vals;
}
for (i = 0; i < num_vecs; i++) {
struct idpf_q_vector *q_vector = &rsrc->q_vectors[i];
u16 vec_id = rsrc->q_vector_idxs[i] - IDPF_MBX_Q_VEC;
struct idpf_intr_reg *intr = &q_vector->intr_reg;
u32 spacing;
intr->dyn_ctl = idpf_get_reg_addr(adapter,
reg_vals[vec_id].dyn_ctl_reg);
intr->dyn_ctl_intena_m = VF_INT_DYN_CTLN_INTENA_M;
intr->dyn_ctl_intena_msk_m = VF_INT_DYN_CTLN_INTENA_MSK_M;
intr->dyn_ctl_itridx_s = VF_INT_DYN_CTLN_ITR_INDX_S;
intr->dyn_ctl_intrvl_s = VF_INT_DYN_CTLN_INTERVAL_S;
intr->dyn_ctl_wb_on_itr_m = VF_INT_DYN_CTLN_WB_ON_ITR_M;
intr->dyn_ctl_swint_trig_m = VF_INT_DYN_CTLN_SWINT_TRIG_M;
intr->dyn_ctl_sw_itridx_ena_m =
VF_INT_DYN_CTLN_SW_ITR_INDX_ENA_M;
spacing = IDPF_ITR_IDX_SPACING(reg_vals[vec_id].itrn_index_spacing,
IDPF_VF_ITR_IDX_SPACING);
rx_itr = VF_INT_ITRN_ADDR(VIRTCHNL2_ITR_IDX_0,
reg_vals[vec_id].itrn_reg,
spacing);
tx_itr = VF_INT_ITRN_ADDR(VIRTCHNL2_ITR_IDX_1,
reg_vals[vec_id].itrn_reg,
spacing);
intr->rx_itr = idpf_get_reg_addr(adapter, rx_itr);
intr->tx_itr = idpf_get_reg_addr(adapter, tx_itr);
}
/* Data vector for NOIRQ queues */
val = reg_vals[rsrc->q_vector_idxs[i] - IDPF_MBX_Q_VEC].dyn_ctl_reg;
rsrc->noirq_dyn_ctl = idpf_get_reg_addr(adapter, val);
val = VF_INT_DYN_CTLN_WB_ON_ITR_M | VF_INT_DYN_CTLN_INTENA_MSK_M |
FIELD_PREP(VF_INT_DYN_CTLN_ITR_INDX_M, IDPF_NO_ITR_UPDATE_IDX);
rsrc->noirq_dyn_ctl_ena = val;
free_reg_vals:
kfree(reg_vals);
return err;
}
/**
* idpf_vf_reset_reg_init - Initialize reset registers
* @adapter: Driver specific private structure
*/
static void idpf_vf_reset_reg_init(struct idpf_adapter *adapter)
{
adapter->reset_reg.rstat = idpf_get_rstat_reg_addr(adapter, VFGEN_RSTAT);
adapter->reset_reg.rstat_m = VFGEN_RSTAT_VFR_STATE_M;
}
/**
* idpf_vf_trigger_reset - trigger reset
* @adapter: Driver specific private structure
* @trig_cause: Reason to trigger a reset
*/
static void idpf_vf_trigger_reset(struct idpf_adapter *adapter,
enum idpf_flags trig_cause)
{
/* Do not send VIRTCHNL2_OP_RESET_VF message on driver unload */
if (trig_cause == IDPF_HR_FUNC_RESET &&
!test_bit(IDPF_REMOVE_IN_PROG, adapter->flags))
idpf_send_mb_msg(adapter, adapter->hw.asq,
VIRTCHNL2_OP_RESET_VF, 0, NULL, 0);
}
/**
* idpf_idc_vf_register - register for IDC callbacks
* @adapter: Driver specific private structure
*
* Return: 0 on success or error code on failure.
*/
static int idpf_idc_vf_register(struct idpf_adapter *adapter)
{
return idpf_idc_init_aux_core_dev(adapter, IIDC_FUNCTION_TYPE_VF);
}
/**
* idpf_vf_reg_ops_init - Initialize register API function pointers
* @adapter: Driver specific private structure
*/
static void idpf_vf_reg_ops_init(struct idpf_adapter *adapter)
{
adapter->dev_ops.reg_ops.ctlq_reg_init = idpf_vf_ctlq_reg_init;
adapter->dev_ops.reg_ops.intr_reg_init = idpf_vf_intr_reg_init;
adapter->dev_ops.reg_ops.mb_intr_reg_init = idpf_vf_mb_intr_reg_init;
adapter->dev_ops.reg_ops.reset_reg_init = idpf_vf_reset_reg_init;
adapter->dev_ops.reg_ops.trigger_reset = idpf_vf_trigger_reset;
}
/**
* idpf_vf_dev_ops_init - Initialize device API function pointers
* @adapter: Driver specific private structure
*/
void idpf_vf_dev_ops_init(struct idpf_adapter *adapter)
{
idpf_vf_reg_ops_init(adapter);
adapter->dev_ops.idc_init = idpf_idc_vf_register;
resource_set_range(&adapter->dev_ops.static_reg_info[0],
VF_BASE, IDPF_VF_MBX_REGION_SZ);
resource_set_range(&adapter->dev_ops.static_reg_info[1],
VFGEN_RSTAT, IDPF_VF_RSTAT_REGION_SZ);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,222 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (C) 2024 Intel Corporation */
#ifndef _IDPF_VIRTCHNL_H_
#define _IDPF_VIRTCHNL_H_
#include "virtchnl2.h"
#define IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC (60 * 1000)
#define IDPF_VC_XN_IDX_M GENMASK(7, 0)
#define IDPF_VC_XN_SALT_M GENMASK(15, 8)
#define IDPF_VC_XN_RING_LEN U8_MAX
/**
* enum idpf_vc_xn_state - Virtchnl transaction status
* @IDPF_VC_XN_IDLE: not expecting a reply, ready to be used
* @IDPF_VC_XN_WAITING: expecting a reply, not yet received
* @IDPF_VC_XN_COMPLETED_SUCCESS: a reply was expected and received, buffer
* updated
* @IDPF_VC_XN_COMPLETED_FAILED: a reply was expected and received, but there
* was an error, buffer not updated
* @IDPF_VC_XN_SHUTDOWN: transaction object cannot be used, VC torn down
* @IDPF_VC_XN_ASYNC: transaction sent asynchronously and doesn't have the
* return context; a callback may be provided to handle
* return
*/
enum idpf_vc_xn_state {
IDPF_VC_XN_IDLE = 1,
IDPF_VC_XN_WAITING,
IDPF_VC_XN_COMPLETED_SUCCESS,
IDPF_VC_XN_COMPLETED_FAILED,
IDPF_VC_XN_SHUTDOWN,
IDPF_VC_XN_ASYNC,
};
struct idpf_vc_xn;
/* Callback for asynchronous messages */
typedef int (*async_vc_cb) (struct idpf_adapter *, struct idpf_vc_xn *,
const struct idpf_ctlq_msg *);
/**
* struct idpf_vc_xn - Data structure representing virtchnl transactions
* @completed: virtchnl event loop uses that to signal when a reply is
* available, uses kernel completion API
* @lock: protects the transaction state fields below
* @state: virtchnl event loop stores the data below, protected by @lock
* @reply_sz: Original size of reply, may be > reply_buf.iov_len; it will be
* truncated on its way to the receiver thread according to
* reply_buf.iov_len.
* @reply: Reference to the buffer(s) where the reply data should be written
* to. May be 0-length (then NULL address permitted) if the reply data
* should be ignored.
* @async_handler: if sent asynchronously, a callback can be provided to handle
* the reply when it's received
* @vc_op: corresponding opcode sent with this transaction
* @idx: index used as retrieval on reply receive, used for cookie
* @salt: changed every message to make unique, used for cookie
*/
struct idpf_vc_xn {
struct completion completed;
spinlock_t lock;
enum idpf_vc_xn_state state;
size_t reply_sz;
struct kvec reply;
async_vc_cb async_handler;
u32 vc_op;
u8 idx;
u8 salt;
};
/**
* struct idpf_vc_xn_params - Parameters for executing transaction
* @send_buf: kvec for send buffer
* @recv_buf: kvec for recv buffer, may be NULL, must then have zero length
* @timeout_ms: timeout to wait for reply
* @async: send message asynchronously, will not wait on completion
* @async_handler: If sent asynchronously, optional callback handler. The user
* must be careful when using async handlers as the memory for
* the recv_buf _cannot_ be on stack if this is async.
* @vc_op: virtchnl op to send
*/
struct idpf_vc_xn_params {
struct kvec send_buf;
struct kvec recv_buf;
int timeout_ms;
bool async;
async_vc_cb async_handler;
u32 vc_op;
};
struct idpf_adapter;
struct idpf_netdev_priv;
struct idpf_vec_regs;
struct idpf_vport;
struct idpf_vport_max_q;
struct idpf_vport_config;
struct idpf_vport_user_config_data;
ssize_t idpf_vc_xn_exec(struct idpf_adapter *adapter,
const struct idpf_vc_xn_params *params);
int idpf_init_dflt_mbx(struct idpf_adapter *adapter);
void idpf_deinit_dflt_mbx(struct idpf_adapter *adapter);
int idpf_vc_core_init(struct idpf_adapter *adapter);
void idpf_vc_core_deinit(struct idpf_adapter *adapter);
int idpf_get_reg_intr_vecs(struct idpf_adapter *adapter,
struct idpf_vec_regs *reg_vals, int num_vecs);
int idpf_queue_reg_init(struct idpf_vport *vport,
struct idpf_q_vec_rsrc *rsrc,
struct idpf_queue_id_reg_info *chunks);
int idpf_vport_queue_ids_init(struct idpf_vport *vport,
struct idpf_q_vec_rsrc *rsrc,
struct idpf_queue_id_reg_info *chunks);
static inline void
idpf_vport_deinit_queue_reg_chunks(struct idpf_vport_config *vport_cfg)
{
kfree(vport_cfg->qid_reg_info.queue_chunks);
vport_cfg->qid_reg_info.queue_chunks = NULL;
}
bool idpf_vport_is_cap_ena(struct idpf_vport *vport, u16 flag);
bool idpf_sideband_flow_type_ena(struct idpf_vport *vport, u32 flow_type);
bool idpf_sideband_action_ena(struct idpf_vport *vport,
struct ethtool_rx_flow_spec *fsp);
unsigned int idpf_fsteer_max_rules(struct idpf_vport *vport);
int idpf_recv_mb_msg(struct idpf_adapter *adapter, struct idpf_ctlq_info *arq);
int idpf_send_mb_msg(struct idpf_adapter *adapter, struct idpf_ctlq_info *asq,
u32 op, u16 msg_size, u8 *msg, u16 cookie);
struct idpf_queue_ptr {
enum virtchnl2_queue_type type;
union {
struct idpf_rx_queue *rxq;
struct idpf_tx_queue *txq;
struct idpf_buf_queue *bufq;
struct idpf_compl_queue *complq;
};
};
struct idpf_queue_set {
struct idpf_adapter *adapter;
struct idpf_q_vec_rsrc *qv_rsrc;
u32 vport_id;
u32 num;
struct idpf_queue_ptr qs[] __counted_by(num);
};
struct idpf_queue_set *idpf_alloc_queue_set(struct idpf_adapter *adapter,
struct idpf_q_vec_rsrc *rsrc,
u32 vport_id, u32 num);
int idpf_send_enable_queue_set_msg(const struct idpf_queue_set *qs);
int idpf_send_disable_queue_set_msg(const struct idpf_queue_set *qs);
int idpf_send_config_queue_set_msg(const struct idpf_queue_set *qs);
int idpf_send_disable_queues_msg(struct idpf_vport *vport);
int idpf_send_enable_queues_msg(struct idpf_vport *vport);
int idpf_send_config_queues_msg(struct idpf_adapter *adapter,
struct idpf_q_vec_rsrc *rsrc,
u32 vport_id);
int idpf_vport_init(struct idpf_vport *vport, struct idpf_vport_max_q *max_q);
u32 idpf_get_vport_id(struct idpf_vport *vport);
int idpf_send_create_vport_msg(struct idpf_adapter *adapter,
struct idpf_vport_max_q *max_q);
int idpf_send_destroy_vport_msg(struct idpf_adapter *adapter, u32 vport_id);
int idpf_send_enable_vport_msg(struct idpf_adapter *adapter, u32 vport_id);
int idpf_send_disable_vport_msg(struct idpf_adapter *adapter, u32 vport_id);
int idpf_vport_adjust_qs(struct idpf_vport *vport,
struct idpf_q_vec_rsrc *rsrc);
int idpf_vport_alloc_max_qs(struct idpf_adapter *adapter,
struct idpf_vport_max_q *max_q);
void idpf_vport_dealloc_max_qs(struct idpf_adapter *adapter,
struct idpf_vport_max_q *max_q);
int idpf_send_add_queues_msg(struct idpf_adapter *adapter,
struct idpf_vport_config *vport_config,
struct idpf_q_vec_rsrc *rsrc,
u32 vport_id);
int idpf_send_delete_queues_msg(struct idpf_adapter *adapter,
struct idpf_queue_id_reg_info *chunks,
u32 vport_id);
int idpf_vport_alloc_vec_indexes(struct idpf_vport *vport,
struct idpf_q_vec_rsrc *rsrc);
int idpf_get_vec_ids(struct idpf_adapter *adapter,
u16 *vecids, int num_vecids,
struct virtchnl2_vector_chunks *chunks);
int idpf_send_alloc_vectors_msg(struct idpf_adapter *adapter, u16 num_vectors);
int idpf_send_dealloc_vectors_msg(struct idpf_adapter *adapter);
int idpf_send_map_unmap_queue_vector_msg(struct idpf_adapter *adapter,
struct idpf_q_vec_rsrc *rsrc,
u32 vport_id,
bool map);
int idpf_add_del_mac_filters(struct idpf_adapter *adapter,
struct idpf_vport_config *vport_config,
const u8 *default_mac_addr, u32 vport_id,
bool add, bool async);
int idpf_set_promiscuous(struct idpf_adapter *adapter,
struct idpf_vport_user_config_data *config_data,
u32 vport_id);
int idpf_check_supported_desc_ids(struct idpf_vport *vport);
int idpf_send_ena_dis_loopback_msg(struct idpf_adapter *adapter, u32 vport_id,
bool loopback_ena);
int idpf_send_get_stats_msg(struct idpf_netdev_priv *np,
struct idpf_port_stats *port_stats);
int idpf_send_set_sriov_vfs_msg(struct idpf_adapter *adapter, u16 num_vfs);
int idpf_send_get_set_rss_key_msg(struct idpf_adapter *adapter,
struct idpf_rss_data *rss_data,
u32 vport_id, bool get);
int idpf_send_get_set_rss_lut_msg(struct idpf_adapter *adapter,
struct idpf_rss_data *rss_data,
u32 vport_id, bool get);
void idpf_vc_xn_shutdown(struct idpf_vc_xn_manager *vcxn_mngr);
int idpf_idc_rdma_vc_send_sync(struct iidc_rdma_core_dev_info *cdev_info,
u8 *send_msg, u16 msg_size,
u8 *recv_msg, u16 *recv_len);
#endif /* _IDPF_VIRTCHNL_H_ */
@@ -0,0 +1,672 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2024 Intel Corporation */
#include "idpf.h"
#include "idpf_ptp.h"
#include "idpf_virtchnl.h"
/**
* idpf_ptp_get_caps - Send virtchnl get ptp capabilities message
* @adapter: Driver specific private structure
*
* Send virtchnl get PTP capabilities message.
*
* Return: 0 on success, -errno on failure.
*/
int idpf_ptp_get_caps(struct idpf_adapter *adapter)
{
struct virtchnl2_ptp_get_caps *recv_ptp_caps_msg __free(kfree) = NULL;
struct virtchnl2_ptp_get_caps send_ptp_caps_msg = {
.caps = cpu_to_le32(VIRTCHNL2_CAP_PTP_GET_DEVICE_CLK_TIME |
VIRTCHNL2_CAP_PTP_GET_DEVICE_CLK_TIME_MB |
VIRTCHNL2_CAP_PTP_GET_CROSS_TIME |
VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME_MB |
VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK_MB |
VIRTCHNL2_CAP_PTP_TX_TSTAMPS_MB)
};
struct idpf_vc_xn_params xn_params = {
.vc_op = VIRTCHNL2_OP_PTP_GET_CAPS,
.send_buf.iov_base = &send_ptp_caps_msg,
.send_buf.iov_len = sizeof(send_ptp_caps_msg),
.timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC,
};
struct virtchnl2_ptp_cross_time_reg_offsets cross_tstamp_offsets;
struct virtchnl2_ptp_clk_adj_reg_offsets clk_adj_offsets;
struct virtchnl2_ptp_clk_reg_offsets clock_offsets;
struct idpf_ptp_secondary_mbx *scnd_mbx;
struct idpf_ptp *ptp = adapter->ptp;
enum idpf_ptp_access access_type;
u32 temp_offset;
int reply_sz;
recv_ptp_caps_msg = kzalloc_obj(struct virtchnl2_ptp_get_caps);
if (!recv_ptp_caps_msg)
return -ENOMEM;
xn_params.recv_buf.iov_base = recv_ptp_caps_msg;
xn_params.recv_buf.iov_len = sizeof(*recv_ptp_caps_msg);
reply_sz = idpf_vc_xn_exec(adapter, &xn_params);
if (reply_sz < 0)
return reply_sz;
else if (reply_sz != sizeof(*recv_ptp_caps_msg))
return -EIO;
ptp->caps = le32_to_cpu(recv_ptp_caps_msg->caps);
ptp->base_incval = le64_to_cpu(recv_ptp_caps_msg->base_incval);
ptp->max_adj = le32_to_cpu(recv_ptp_caps_msg->max_adj);
scnd_mbx = &ptp->secondary_mbx;
scnd_mbx->peer_mbx_q_id = le16_to_cpu(recv_ptp_caps_msg->peer_mbx_q_id);
/* if the ptp_mb_q_id holds invalid value (0xffff), the secondary
* mailbox is not supported.
*/
scnd_mbx->valid = scnd_mbx->peer_mbx_q_id != 0xffff;
if (scnd_mbx->valid)
scnd_mbx->peer_id = recv_ptp_caps_msg->peer_id;
/* Determine the access type for the PTP features */
idpf_ptp_get_features_access(adapter);
access_type = ptp->get_dev_clk_time_access;
if (access_type != IDPF_PTP_DIRECT)
goto cross_tstamp;
clock_offsets = recv_ptp_caps_msg->clk_offsets;
temp_offset = le32_to_cpu(clock_offsets.dev_clk_ns_l);
ptp->dev_clk_regs.dev_clk_ns_l = idpf_get_reg_addr(adapter,
temp_offset);
temp_offset = le32_to_cpu(clock_offsets.dev_clk_ns_h);
ptp->dev_clk_regs.dev_clk_ns_h = idpf_get_reg_addr(adapter,
temp_offset);
temp_offset = le32_to_cpu(clock_offsets.phy_clk_ns_l);
ptp->dev_clk_regs.phy_clk_ns_l = idpf_get_reg_addr(adapter,
temp_offset);
temp_offset = le32_to_cpu(clock_offsets.phy_clk_ns_h);
ptp->dev_clk_regs.phy_clk_ns_h = idpf_get_reg_addr(adapter,
temp_offset);
temp_offset = le32_to_cpu(clock_offsets.cmd_sync_trigger);
ptp->dev_clk_regs.cmd_sync = idpf_get_reg_addr(adapter, temp_offset);
cross_tstamp:
access_type = ptp->get_cross_tstamp_access;
if (access_type != IDPF_PTP_DIRECT)
goto discipline_clock;
cross_tstamp_offsets = recv_ptp_caps_msg->cross_time_offsets;
temp_offset = le32_to_cpu(cross_tstamp_offsets.sys_time_ns_l);
ptp->dev_clk_regs.sys_time_ns_l = idpf_get_reg_addr(adapter,
temp_offset);
temp_offset = le32_to_cpu(cross_tstamp_offsets.sys_time_ns_h);
ptp->dev_clk_regs.sys_time_ns_h = idpf_get_reg_addr(adapter,
temp_offset);
temp_offset = le32_to_cpu(cross_tstamp_offsets.cmd_sync_trigger);
ptp->dev_clk_regs.cmd_sync = idpf_get_reg_addr(adapter, temp_offset);
discipline_clock:
access_type = ptp->adj_dev_clk_time_access;
if (access_type != IDPF_PTP_DIRECT)
return 0;
clk_adj_offsets = recv_ptp_caps_msg->clk_adj_offsets;
/* Device clock offsets */
temp_offset = le32_to_cpu(clk_adj_offsets.dev_clk_cmd_type);
ptp->dev_clk_regs.cmd = idpf_get_reg_addr(adapter, temp_offset);
temp_offset = le32_to_cpu(clk_adj_offsets.dev_clk_incval_l);
ptp->dev_clk_regs.incval_l = idpf_get_reg_addr(adapter, temp_offset);
temp_offset = le32_to_cpu(clk_adj_offsets.dev_clk_incval_h);
ptp->dev_clk_regs.incval_h = idpf_get_reg_addr(adapter, temp_offset);
temp_offset = le32_to_cpu(clk_adj_offsets.dev_clk_shadj_l);
ptp->dev_clk_regs.shadj_l = idpf_get_reg_addr(adapter, temp_offset);
temp_offset = le32_to_cpu(clk_adj_offsets.dev_clk_shadj_h);
ptp->dev_clk_regs.shadj_h = idpf_get_reg_addr(adapter, temp_offset);
/* PHY clock offsets */
temp_offset = le32_to_cpu(clk_adj_offsets.phy_clk_cmd_type);
ptp->dev_clk_regs.phy_cmd = idpf_get_reg_addr(adapter, temp_offset);
temp_offset = le32_to_cpu(clk_adj_offsets.phy_clk_incval_l);
ptp->dev_clk_regs.phy_incval_l = idpf_get_reg_addr(adapter,
temp_offset);
temp_offset = le32_to_cpu(clk_adj_offsets.phy_clk_incval_h);
ptp->dev_clk_regs.phy_incval_h = idpf_get_reg_addr(adapter,
temp_offset);
temp_offset = le32_to_cpu(clk_adj_offsets.phy_clk_shadj_l);
ptp->dev_clk_regs.phy_shadj_l = idpf_get_reg_addr(adapter, temp_offset);
temp_offset = le32_to_cpu(clk_adj_offsets.phy_clk_shadj_h);
ptp->dev_clk_regs.phy_shadj_h = idpf_get_reg_addr(adapter, temp_offset);
return 0;
}
/**
* idpf_ptp_get_dev_clk_time - Send virtchnl get device clk time message
* @adapter: Driver specific private structure
* @dev_clk_time: Pointer to the device clock structure where the value is set
*
* Send virtchnl get time message to get the time of the clock.
*
* Return: 0 on success, -errno otherwise.
*/
int idpf_ptp_get_dev_clk_time(struct idpf_adapter *adapter,
struct idpf_ptp_dev_timers *dev_clk_time)
{
struct virtchnl2_ptp_get_dev_clk_time get_dev_clk_time_msg;
struct idpf_vc_xn_params xn_params = {
.vc_op = VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME,
.send_buf.iov_base = &get_dev_clk_time_msg,
.send_buf.iov_len = sizeof(get_dev_clk_time_msg),
.recv_buf.iov_base = &get_dev_clk_time_msg,
.recv_buf.iov_len = sizeof(get_dev_clk_time_msg),
.timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC,
};
int reply_sz;
u64 dev_time;
reply_sz = idpf_vc_xn_exec(adapter, &xn_params);
if (reply_sz < 0)
return reply_sz;
if (reply_sz != sizeof(get_dev_clk_time_msg))
return -EIO;
dev_time = le64_to_cpu(get_dev_clk_time_msg.dev_time_ns);
dev_clk_time->dev_clk_time_ns = dev_time;
return 0;
}
/**
* idpf_ptp_get_cross_time - Send virtchnl get cross time message
* @adapter: Driver specific private structure
* @cross_time: Pointer to the device clock structure where the value is set
*
* Send virtchnl get cross time message to get the time of the clock and the
* system time.
*
* Return: 0 on success, -errno otherwise.
*/
int idpf_ptp_get_cross_time(struct idpf_adapter *adapter,
struct idpf_ptp_dev_timers *cross_time)
{
struct virtchnl2_ptp_get_cross_time cross_time_msg;
struct idpf_vc_xn_params xn_params = {
.vc_op = VIRTCHNL2_OP_PTP_GET_CROSS_TIME,
.send_buf.iov_base = &cross_time_msg,
.send_buf.iov_len = sizeof(cross_time_msg),
.recv_buf.iov_base = &cross_time_msg,
.recv_buf.iov_len = sizeof(cross_time_msg),
.timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC,
};
int reply_sz;
reply_sz = idpf_vc_xn_exec(adapter, &xn_params);
if (reply_sz < 0)
return reply_sz;
if (reply_sz != sizeof(cross_time_msg))
return -EIO;
cross_time->dev_clk_time_ns = le64_to_cpu(cross_time_msg.dev_time_ns);
cross_time->sys_time_ns = le64_to_cpu(cross_time_msg.sys_time_ns);
return 0;
}
/**
* idpf_ptp_set_dev_clk_time - Send virtchnl set device time message
* @adapter: Driver specific private structure
* @time: New time value
*
* Send virtchnl set time message to set the time of the clock.
*
* Return: 0 on success, -errno otherwise.
*/
int idpf_ptp_set_dev_clk_time(struct idpf_adapter *adapter, u64 time)
{
struct virtchnl2_ptp_set_dev_clk_time set_dev_clk_time_msg = {
.dev_time_ns = cpu_to_le64(time),
};
struct idpf_vc_xn_params xn_params = {
.vc_op = VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME,
.send_buf.iov_base = &set_dev_clk_time_msg,
.send_buf.iov_len = sizeof(set_dev_clk_time_msg),
.recv_buf.iov_base = &set_dev_clk_time_msg,
.recv_buf.iov_len = sizeof(set_dev_clk_time_msg),
.timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC,
};
int reply_sz;
reply_sz = idpf_vc_xn_exec(adapter, &xn_params);
if (reply_sz < 0)
return reply_sz;
if (reply_sz != sizeof(set_dev_clk_time_msg))
return -EIO;
return 0;
}
/**
* idpf_ptp_adj_dev_clk_time - Send virtchnl adj device clock time message
* @adapter: Driver specific private structure
* @delta: Offset in nanoseconds to adjust the time by
*
* Send virtchnl adj time message to adjust the clock by the indicated delta.
*
* Return: 0 on success, -errno otherwise.
*/
int idpf_ptp_adj_dev_clk_time(struct idpf_adapter *adapter, s64 delta)
{
struct virtchnl2_ptp_adj_dev_clk_time adj_dev_clk_time_msg = {
.delta = cpu_to_le64(delta),
};
struct idpf_vc_xn_params xn_params = {
.vc_op = VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME,
.send_buf.iov_base = &adj_dev_clk_time_msg,
.send_buf.iov_len = sizeof(adj_dev_clk_time_msg),
.recv_buf.iov_base = &adj_dev_clk_time_msg,
.recv_buf.iov_len = sizeof(adj_dev_clk_time_msg),
.timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC,
};
int reply_sz;
reply_sz = idpf_vc_xn_exec(adapter, &xn_params);
if (reply_sz < 0)
return reply_sz;
if (reply_sz != sizeof(adj_dev_clk_time_msg))
return -EIO;
return 0;
}
/**
* idpf_ptp_adj_dev_clk_fine - Send virtchnl adj time message
* @adapter: Driver specific private structure
* @incval: Source timer increment value per clock cycle
*
* Send virtchnl adj fine message to adjust the frequency of the clock by
* incval.
*
* Return: 0 on success, -errno otherwise.
*/
int idpf_ptp_adj_dev_clk_fine(struct idpf_adapter *adapter, u64 incval)
{
struct virtchnl2_ptp_adj_dev_clk_fine adj_dev_clk_fine_msg = {
.incval = cpu_to_le64(incval),
};
struct idpf_vc_xn_params xn_params = {
.vc_op = VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE,
.send_buf.iov_base = &adj_dev_clk_fine_msg,
.send_buf.iov_len = sizeof(adj_dev_clk_fine_msg),
.recv_buf.iov_base = &adj_dev_clk_fine_msg,
.recv_buf.iov_len = sizeof(adj_dev_clk_fine_msg),
.timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC,
};
int reply_sz;
reply_sz = idpf_vc_xn_exec(adapter, &xn_params);
if (reply_sz < 0)
return reply_sz;
if (reply_sz != sizeof(adj_dev_clk_fine_msg))
return -EIO;
return 0;
}
/**
* idpf_ptp_get_vport_tstamps_caps - Send virtchnl to get tstamps caps for vport
* @vport: Virtual port structure
*
* Send virtchnl get vport tstamps caps message to receive the set of tstamp
* capabilities per vport.
*
* Return: 0 on success, -errno otherwise.
*/
int idpf_ptp_get_vport_tstamps_caps(struct idpf_vport *vport)
{
struct virtchnl2_ptp_get_vport_tx_tstamp_caps send_tx_tstamp_caps;
struct virtchnl2_ptp_get_vport_tx_tstamp_caps *rcv_tx_tstamp_caps;
struct virtchnl2_ptp_tx_tstamp_latch_caps tx_tstamp_latch_caps;
struct idpf_ptp_vport_tx_tstamp_caps *tstamp_caps;
struct idpf_ptp_tx_tstamp *ptp_tx_tstamp, *tmp;
struct idpf_vc_xn_params xn_params = {
.vc_op = VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS,
.send_buf.iov_base = &send_tx_tstamp_caps,
.send_buf.iov_len = sizeof(send_tx_tstamp_caps),
.recv_buf.iov_len = IDPF_CTLQ_MAX_BUF_LEN,
.timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC,
};
enum idpf_ptp_access tstamp_access, get_dev_clk_access;
struct idpf_ptp *ptp = vport->adapter->ptp;
struct list_head *head;
int err = 0, reply_sz;
u16 num_latches;
u32 size;
if (!ptp)
return -EOPNOTSUPP;
tstamp_access = ptp->tx_tstamp_access;
get_dev_clk_access = ptp->get_dev_clk_time_access;
if (tstamp_access == IDPF_PTP_NONE ||
get_dev_clk_access == IDPF_PTP_NONE)
return -EOPNOTSUPP;
rcv_tx_tstamp_caps = kzalloc(IDPF_CTLQ_MAX_BUF_LEN, GFP_KERNEL);
if (!rcv_tx_tstamp_caps)
return -ENOMEM;
send_tx_tstamp_caps.vport_id = cpu_to_le32(vport->vport_id);
xn_params.recv_buf.iov_base = rcv_tx_tstamp_caps;
reply_sz = idpf_vc_xn_exec(vport->adapter, &xn_params);
if (reply_sz < 0) {
err = reply_sz;
goto get_tstamp_caps_out;
}
num_latches = le16_to_cpu(rcv_tx_tstamp_caps->num_latches);
size = struct_size(rcv_tx_tstamp_caps, tstamp_latches, num_latches);
if (reply_sz != size) {
err = -EIO;
goto get_tstamp_caps_out;
}
size = struct_size(tstamp_caps, tx_tstamp_status, num_latches);
tstamp_caps = kzalloc(size, GFP_KERNEL);
if (!tstamp_caps) {
err = -ENOMEM;
goto get_tstamp_caps_out;
}
tstamp_caps->access = true;
tstamp_caps->num_entries = num_latches;
INIT_LIST_HEAD(&tstamp_caps->latches_in_use);
INIT_LIST_HEAD(&tstamp_caps->latches_free);
spin_lock_init(&tstamp_caps->latches_lock);
spin_lock_init(&tstamp_caps->status_lock);
tstamp_caps->tstamp_ns_lo_bit = rcv_tx_tstamp_caps->tstamp_ns_lo_bit;
for (u16 i = 0; i < tstamp_caps->num_entries; i++) {
__le32 offset_l, offset_h;
ptp_tx_tstamp = kzalloc_obj(*ptp_tx_tstamp);
if (!ptp_tx_tstamp) {
err = -ENOMEM;
goto err_free_ptp_tx_stamp_list;
}
tx_tstamp_latch_caps = rcv_tx_tstamp_caps->tstamp_latches[i];
if (tstamp_access != IDPF_PTP_DIRECT)
goto skip_offsets;
offset_l = tx_tstamp_latch_caps.tx_latch_reg_offset_l;
offset_h = tx_tstamp_latch_caps.tx_latch_reg_offset_h;
ptp_tx_tstamp->tx_latch_reg_offset_l = le32_to_cpu(offset_l);
ptp_tx_tstamp->tx_latch_reg_offset_h = le32_to_cpu(offset_h);
skip_offsets:
ptp_tx_tstamp->idx = tx_tstamp_latch_caps.index;
list_add(&ptp_tx_tstamp->list_member,
&tstamp_caps->latches_free);
tstamp_caps->tx_tstamp_status[i].state = IDPF_PTP_FREE;
}
vport->tx_tstamp_caps = tstamp_caps;
kfree(rcv_tx_tstamp_caps);
return 0;
err_free_ptp_tx_stamp_list:
head = &tstamp_caps->latches_free;
list_for_each_entry_safe(ptp_tx_tstamp, tmp, head, list_member) {
list_del(&ptp_tx_tstamp->list_member);
kfree(ptp_tx_tstamp);
}
kfree(tstamp_caps);
get_tstamp_caps_out:
kfree(rcv_tx_tstamp_caps);
return err;
}
/**
* idpf_ptp_update_tstamp_tracker - Update the Tx timestamp tracker based on
* the skb compatibility.
* @caps: Tx timestamp capabilities that monitor the latch status
* @skb: skb for which the tstamp value is returned through virtchnl message
* @current_state: Current state of the Tx timestamp latch
* @expected_state: Expected state of the Tx timestamp latch
*
* Find a proper skb tracker for which the Tx timestamp is received and change
* the state to expected value.
*
* Return: true if the tracker has been found and updated, false otherwise.
*/
static bool
idpf_ptp_update_tstamp_tracker(struct idpf_ptp_vport_tx_tstamp_caps *caps,
struct sk_buff *skb,
enum idpf_ptp_tx_tstamp_state current_state,
enum idpf_ptp_tx_tstamp_state expected_state)
{
bool updated = false;
spin_lock(&caps->status_lock);
for (u16 i = 0; i < caps->num_entries; i++) {
struct idpf_ptp_tx_tstamp_status *status;
status = &caps->tx_tstamp_status[i];
if (skb == status->skb && status->state == current_state) {
status->state = expected_state;
updated = true;
break;
}
}
spin_unlock(&caps->status_lock);
return updated;
}
/**
* idpf_ptp_get_tstamp_value - Get the Tx timestamp value and provide it
* back to the skb.
* @vport: Virtual port structure
* @tstamp_latch: Tx timestamp latch structure fulfilled by the Control Plane
* @ptp_tx_tstamp: Tx timestamp latch to add to the free list
*
* Read the value of the Tx timestamp for a given latch received from the
* Control Plane, extend it to 64 bit and provide back to the skb.
*
* Return: 0 on success, -errno otherwise.
*/
static int
idpf_ptp_get_tstamp_value(struct idpf_vport *vport,
struct virtchnl2_ptp_tx_tstamp_latch *tstamp_latch,
struct idpf_ptp_tx_tstamp *ptp_tx_tstamp)
{
struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps;
struct skb_shared_hwtstamps shhwtstamps;
bool state_upd = false;
u8 tstamp_ns_lo_bit;
u64 tstamp;
tx_tstamp_caps = vport->tx_tstamp_caps;
tstamp_ns_lo_bit = tx_tstamp_caps->tstamp_ns_lo_bit;
ptp_tx_tstamp->tstamp = le64_to_cpu(tstamp_latch->tstamp);
ptp_tx_tstamp->tstamp >>= tstamp_ns_lo_bit;
state_upd = idpf_ptp_update_tstamp_tracker(tx_tstamp_caps,
ptp_tx_tstamp->skb,
IDPF_PTP_READ_VALUE,
IDPF_PTP_FREE);
if (!state_upd)
return -EINVAL;
tstamp = idpf_ptp_extend_ts(vport, ptp_tx_tstamp->tstamp);
shhwtstamps.hwtstamp = ns_to_ktime(tstamp);
skb_tstamp_tx(ptp_tx_tstamp->skb, &shhwtstamps);
consume_skb(ptp_tx_tstamp->skb);
ptp_tx_tstamp->skb = NULL;
list_add(&ptp_tx_tstamp->list_member,
&tx_tstamp_caps->latches_free);
u64_stats_update_begin(&vport->tstamp_stats.stats_sync);
u64_stats_inc(&vport->tstamp_stats.packets);
u64_stats_update_end(&vport->tstamp_stats.stats_sync);
return 0;
}
/**
* idpf_ptp_get_tx_tstamp_async_handler - Async callback for getting Tx tstamps
* @adapter: Driver specific private structure
* @xn: transaction for message
* @ctlq_msg: received message
*
* Read the tstamps Tx tstamp values from a received message and put them
* directly to the skb. The number of timestamps to read is specified by
* the virtchnl message.
*
* Return: 0 on success, -errno otherwise.
*/
static int
idpf_ptp_get_tx_tstamp_async_handler(struct idpf_adapter *adapter,
struct idpf_vc_xn *xn,
const struct idpf_ctlq_msg *ctlq_msg)
{
struct virtchnl2_ptp_get_vport_tx_tstamp_latches *recv_tx_tstamp_msg;
struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps;
struct virtchnl2_ptp_tx_tstamp_latch tstamp_latch;
struct idpf_ptp_tx_tstamp *tx_tstamp, *tmp;
struct idpf_vport *tstamp_vport = NULL;
struct list_head *head;
u16 num_latches;
u32 vport_id;
int err = 0;
recv_tx_tstamp_msg = ctlq_msg->ctx.indirect.payload->va;
vport_id = le32_to_cpu(recv_tx_tstamp_msg->vport_id);
idpf_for_each_vport(adapter, vport) {
if (!vport)
continue;
if (vport->vport_id == vport_id) {
tstamp_vport = vport;
break;
}
}
if (!tstamp_vport || !tstamp_vport->tx_tstamp_caps)
return -EINVAL;
tx_tstamp_caps = tstamp_vport->tx_tstamp_caps;
num_latches = le16_to_cpu(recv_tx_tstamp_msg->num_latches);
spin_lock_bh(&tx_tstamp_caps->latches_lock);
head = &tx_tstamp_caps->latches_in_use;
for (u16 i = 0; i < num_latches; i++) {
tstamp_latch = recv_tx_tstamp_msg->tstamp_latches[i];
if (!tstamp_latch.valid)
continue;
if (list_empty(head)) {
err = -ENOBUFS;
goto unlock;
}
list_for_each_entry_safe(tx_tstamp, tmp, head, list_member) {
if (tstamp_latch.index == tx_tstamp->idx) {
list_del(&tx_tstamp->list_member);
err = idpf_ptp_get_tstamp_value(tstamp_vport,
&tstamp_latch,
tx_tstamp);
if (err)
goto unlock;
break;
}
}
}
unlock:
spin_unlock_bh(&tx_tstamp_caps->latches_lock);
return err;
}
/**
* idpf_ptp_get_tx_tstamp - Send virtchnl get Tx timestamp latches message
* @vport: Virtual port structure
*
* Send virtchnl get Tx tstamp message to read the value of the HW timestamp.
* The message contains a list of indexes set in the Tx descriptors.
*
* Return: 0 on success, -errno otherwise.
*/
int idpf_ptp_get_tx_tstamp(struct idpf_vport *vport)
{
struct virtchnl2_ptp_get_vport_tx_tstamp_latches *send_tx_tstamp_msg;
struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps;
struct idpf_vc_xn_params xn_params = {
.vc_op = VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP,
.timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC,
.async = true,
.async_handler = idpf_ptp_get_tx_tstamp_async_handler,
};
struct idpf_ptp_tx_tstamp *ptp_tx_tstamp;
int reply_sz, size, msg_size;
struct list_head *head;
bool state_upd;
u16 id = 0;
tx_tstamp_caps = vport->tx_tstamp_caps;
head = &tx_tstamp_caps->latches_in_use;
size = struct_size(send_tx_tstamp_msg, tstamp_latches,
tx_tstamp_caps->num_entries);
send_tx_tstamp_msg = kzalloc(size, GFP_KERNEL);
if (!send_tx_tstamp_msg)
return -ENOMEM;
spin_lock_bh(&tx_tstamp_caps->latches_lock);
list_for_each_entry(ptp_tx_tstamp, head, list_member) {
u8 idx;
state_upd = idpf_ptp_update_tstamp_tracker(tx_tstamp_caps,
ptp_tx_tstamp->skb,
IDPF_PTP_REQUEST,
IDPF_PTP_READ_VALUE);
if (!state_upd)
continue;
idx = ptp_tx_tstamp->idx;
send_tx_tstamp_msg->tstamp_latches[id].index = idx;
id++;
}
spin_unlock_bh(&tx_tstamp_caps->latches_lock);
msg_size = struct_size(send_tx_tstamp_msg, tstamp_latches, id);
send_tx_tstamp_msg->vport_id = cpu_to_le32(vport->vport_id);
send_tx_tstamp_msg->num_latches = cpu_to_le16(id);
xn_params.send_buf.iov_base = send_tx_tstamp_msg;
xn_params.send_buf.iov_len = msg_size;
reply_sz = idpf_vc_xn_exec(vport->adapter, &xn_params);
kfree(send_tx_tstamp_msg);
return min(reply_sz, 0);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,451 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (C) 2023 Intel Corporation */
#ifndef _VIRTCHNL2_LAN_DESC_H_
#define _VIRTCHNL2_LAN_DESC_H_
#include <linux/bits.h>
/* This is an interface definition file where existing enums and their values
* must remain unchanged over time, so we specify explicit values for all enums.
*/
/* Transmit descriptor ID flags
*/
enum virtchnl2_tx_desc_ids {
VIRTCHNL2_TXDID_DATA = BIT(0),
VIRTCHNL2_TXDID_CTX = BIT(1),
/* TXDID bit 2 is reserved
* TXDID bit 3 is free for future use
* TXDID bit 4 is reserved
*/
VIRTCHNL2_TXDID_FLEX_TSO_CTX = BIT(5),
/* TXDID bit 6 is reserved */
VIRTCHNL2_TXDID_FLEX_L2TAG1_L2TAG2 = BIT(7),
/* TXDID bits 8 and 9 are free for future use
* TXDID bit 10 is reserved
* TXDID bit 11 is free for future use
*/
VIRTCHNL2_TXDID_FLEX_FLOW_SCHED = BIT(12),
/* TXDID bits 13 and 14 are free for future use */
VIRTCHNL2_TXDID_DESC_DONE = BIT(15),
};
/* Receive descriptor IDs */
enum virtchnl2_rx_desc_ids {
VIRTCHNL2_RXDID_1_32B_BASE = 1,
/* FLEX_SQ_NIC and FLEX_SPLITQ share desc ids because they can be
* differentiated based on queue model; e.g. single queue model can
* only use FLEX_SQ_NIC and split queue model can only use FLEX_SPLITQ
* for DID 2.
*/
VIRTCHNL2_RXDID_2_FLEX_SPLITQ = 2,
VIRTCHNL2_RXDID_2_FLEX_SQ_NIC = VIRTCHNL2_RXDID_2_FLEX_SPLITQ,
/* 3 through 6 are reserved */
VIRTCHNL2_RXDID_7_HW_RSVD = 7,
/* 8 through 15 are free */
};
/* Receive descriptor ID bitmasks */
#define VIRTCHNL2_RXDID_M(bit) BIT_ULL(VIRTCHNL2_RXDID_##bit)
enum virtchnl2_rx_desc_id_bitmasks {
VIRTCHNL2_RXDID_1_32B_BASE_M = VIRTCHNL2_RXDID_M(1_32B_BASE),
VIRTCHNL2_RXDID_2_FLEX_SPLITQ_M = VIRTCHNL2_RXDID_M(2_FLEX_SPLITQ),
VIRTCHNL2_RXDID_2_FLEX_SQ_NIC_M = VIRTCHNL2_RXDID_M(2_FLEX_SQ_NIC),
VIRTCHNL2_RXDID_7_HW_RSVD_M = VIRTCHNL2_RXDID_M(7_HW_RSVD),
};
/* For splitq virtchnl2_rx_flex_desc_adv_nic_3 desc members */
#define VIRTCHNL2_RX_FLEX_DESC_ADV_RXDID_M GENMASK(3, 0)
#define VIRTCHNL2_RX_FLEX_DESC_ADV_UMBCAST_M GENMASK(7, 6)
#define VIRTCHNL2_RX_FLEX_DESC_ADV_PTYPE_M GENMASK(9, 0)
#define VIRTCHNL2_RX_FLEX_DESC_ADV_RAW_CSUM_INV_S 12
#define VIRTCHNL2_RX_FLEX_DESC_ADV_RAW_CSUM_INV_M \
BIT_ULL(VIRTCHNL2_RX_FLEX_DESC_ADV_RAW_CSUM_INV_S)
#define VIRTCHNL2_RX_FLEX_DESC_ADV_FF0_M GENMASK(15, 13)
#define VIRTCHNL2_RX_FLEX_DESC_ADV_LEN_PBUF_M GENMASK(13, 0)
#define VIRTCHNL2_RX_FLEX_DESC_ADV_GEN_S 14
#define VIRTCHNL2_RX_FLEX_DESC_ADV_GEN_M \
BIT_ULL(VIRTCHNL2_RX_FLEX_DESC_ADV_GEN_S)
#define VIRTCHNL2_RX_FLEX_DESC_ADV_BUFQ_ID_S 15
#define VIRTCHNL2_RX_FLEX_DESC_ADV_BUFQ_ID_M \
BIT_ULL(VIRTCHNL2_RX_FLEX_DESC_ADV_BUFQ_ID_S)
#define VIRTCHNL2_RX_FLEX_DESC_ADV_LEN_HDR_M GENMASK(9, 0)
#define VIRTCHNL2_RX_FLEX_DESC_ADV_RSC_S 10
#define VIRTCHNL2_RX_FLEX_DESC_ADV_RSC_M \
BIT_ULL(VIRTCHNL2_RX_FLEX_DESC_ADV_RSC_S)
#define VIRTCHNL2_RX_FLEX_DESC_ADV_SPH_S 11
#define VIRTCHNL2_RX_FLEX_DESC_ADV_SPH_M \
BIT_ULL(VIRTCHNL2_RX_FLEX_DESC_ADV_SPH_S)
#define VIRTCHNL2_RX_FLEX_DESC_ADV_FF1_S 12
#define VIRTCHNL2_RX_FLEX_DESC_ADV_FF1_M GENMASK(14, 12)
#define VIRTCHNL2_RX_FLEX_DESC_ADV_MISS_S 15
#define VIRTCHNL2_RX_FLEX_DESC_ADV_MISS_M \
BIT_ULL(VIRTCHNL2_RX_FLEX_DESC_ADV_MISS_S)
/* Bitmasks for splitq virtchnl2_rx_flex_desc_adv_nic_3 */
enum virtchl2_rx_flex_desc_adv_status_error_0_qw1_bits {
VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_DD_M = BIT(0),
VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_EOF_M = BIT(1),
VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_HBO_M = BIT(2),
VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_L3L4P_M = BIT(3),
VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XSUM_IPE_M = BIT(4),
VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XSUM_L4E_M = BIT(5),
VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XSUM_EIPE_M = BIT(6),
VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XSUM_EUDPE_M = BIT(7),
};
/* Bitmasks for splitq virtchnl2_rx_flex_desc_adv_nic_3 */
enum virtchnl2_rx_flex_desc_adv_status_error_0_qw0_bits {
VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_LPBK_M = BIT(0),
VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_IPV6EXADD_M = BIT(1),
VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_RXE_M = BIT(2),
VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_CRCP_M = BIT(3),
VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_RSS_VALID_M = BIT(4),
VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_L2TAG1P_M = BIT(5),
VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XTRMD0_VALID_M = BIT(6),
VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XTRMD1_VALID_M = BIT(7),
};
/* Bitmasks for splitq virtchnl2_rx_flex_desc_adv_nic_3 */
enum virtchnl2_rx_flex_desc_adv_status_error_1_bits {
VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS1_RSVD_M = GENMASK(1, 0),
VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS1_ATRAEFAIL_M = BIT(2),
VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS1_L2TAG2P_M = BIT(3),
VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS1_XTRMD2_VALID_M = BIT(4),
VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS1_XTRMD3_VALID_M = BIT(5),
VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS1_XTRMD4_VALID_M = BIT(6),
VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS1_XTRMD5_VALID_M = BIT(7),
};
/* For singleq (flex) virtchnl2_rx_flex_desc fields
* For virtchnl2_rx_flex_desc.ptype_flex_flags0 member
*/
#define VIRTCHNL2_RX_FLEX_DESC_PTYPE_M GENMASK(9, 0)
/* For virtchnl2_rx_flex_desc.pkt_len member */
#define VIRTCHNL2_RX_FLEX_DESC_PKT_LEN_M GENMASK(13, 0)
/* Bitmasks for singleq (flex) virtchnl2_rx_flex_desc */
enum virtchnl2_rx_flex_desc_status_error_0_bits {
VIRTCHNL2_RX_FLEX_DESC_STATUS0_DD_M = BIT(0),
VIRTCHNL2_RX_FLEX_DESC_STATUS0_EOF_M = BIT(1),
VIRTCHNL2_RX_FLEX_DESC_STATUS0_HBO_M = BIT(2),
VIRTCHNL2_RX_FLEX_DESC_STATUS0_L3L4P_M = BIT(3),
VIRTCHNL2_RX_FLEX_DESC_STATUS0_XSUM_IPE_M = BIT(4),
VIRTCHNL2_RX_FLEX_DESC_STATUS0_XSUM_L4E_M = BIT(5),
VIRTCHNL2_RX_FLEX_DESC_STATUS0_XSUM_EIPE_M = BIT(6),
VIRTCHNL2_RX_FLEX_DESC_STATUS0_XSUM_EUDPE_M = BIT(7),
VIRTCHNL2_RX_FLEX_DESC_STATUS0_LPBK_M = BIT(8),
VIRTCHNL2_RX_FLEX_DESC_STATUS0_IPV6EXADD_M = BIT(9),
VIRTCHNL2_RX_FLEX_DESC_STATUS0_RXE_M = BIT(10),
VIRTCHNL2_RX_FLEX_DESC_STATUS0_CRCP_M = BIT(11),
VIRTCHNL2_RX_FLEX_DESC_STATUS0_RSS_VALID_M = BIT(12),
VIRTCHNL2_RX_FLEX_DESC_STATUS0_L2TAG1P_M = BIT(13),
VIRTCHNL2_RX_FLEX_DESC_STATUS0_XTRMD0_VALID_M = BIT(14),
VIRTCHNL2_RX_FLEX_DESC_STATUS0_XTRMD1_VALID_M = BIT(15),
};
/* Bitmasks for singleq (flex) virtchnl2_rx_flex_desc */
enum virtchnl2_rx_flex_desc_status_error_1_bits {
VIRTCHNL2_RX_FLEX_DESC_STATUS1_CPM_M = GENMASK(3, 0),
VIRTCHNL2_RX_FLEX_DESC_STATUS1_NAT_M = BIT(4),
VIRTCHNL2_RX_FLEX_DESC_STATUS1_CRYPTO_M = BIT(5),
/* [10:6] reserved */
VIRTCHNL2_RX_FLEX_DESC_STATUS1_L2TAG2P_M = BIT(11),
VIRTCHNL2_RX_FLEX_DESC_STATUS1_XTRMD2_VALID_M = BIT(12),
VIRTCHNL2_RX_FLEX_DESC_STATUS1_XTRMD3_VALID_M = BIT(13),
VIRTCHNL2_RX_FLEX_DESC_STATUS1_XTRMD4_VALID_M = BIT(14),
VIRTCHNL2_RX_FLEX_DESC_STATUS1_XTRMD5_VALID_M = BIT(15),
};
/* For virtchnl2_rx_flex_desc.ts_low member */
#define VIRTCHNL2_RX_FLEX_TSTAMP_VALID BIT(0)
/* For singleq (non flex) virtchnl2_singleq_base_rx_desc legacy desc members */
#define VIRTCHNL2_RX_BASE_DESC_QW1_LEN_PBUF_M GENMASK_ULL(51, 38)
#define VIRTCHNL2_RX_BASE_DESC_QW1_PTYPE_M GENMASK_ULL(37, 30)
#define VIRTCHNL2_RX_BASE_DESC_QW1_ERROR_M GENMASK_ULL(26, 19)
#define VIRTCHNL2_RX_BASE_DESC_QW1_STATUS_M GENMASK_ULL(18, 0)
/* Bitmasks for singleq (base) virtchnl2_rx_base_desc */
enum virtchnl2_rx_base_desc_status_bits {
VIRTCHNL2_RX_BASE_DESC_STATUS_DD_M = BIT(0),
VIRTCHNL2_RX_BASE_DESC_STATUS_EOF_M = BIT(1),
VIRTCHNL2_RX_BASE_DESC_STATUS_L2TAG1P_M = BIT(2),
VIRTCHNL2_RX_BASE_DESC_STATUS_L3L4P_M = BIT(3),
VIRTCHNL2_RX_BASE_DESC_STATUS_CRCP_M = BIT(4),
VIRTCHNL2_RX_BASE_DESC_STATUS_RSVD_M = GENMASK(7, 5),
VIRTCHNL2_RX_BASE_DESC_STATUS_EXT_UDP_0_M = BIT(8),
VIRTCHNL2_RX_BASE_DESC_STATUS_UMBCAST_M = GENMASK(10, 9),
VIRTCHNL2_RX_BASE_DESC_STATUS_FLM_M = BIT(11),
VIRTCHNL2_RX_BASE_DESC_STATUS_FLTSTAT_M = GENMASK(13, 12),
VIRTCHNL2_RX_BASE_DESC_STATUS_LPBK_M = BIT(14),
VIRTCHNL2_RX_BASE_DESC_STATUS_IPV6EXADD_M = BIT(15),
VIRTCHNL2_RX_BASE_DESC_STATUS_RSVD1_M = GENMASK(17, 16),
VIRTCHNL2_RX_BASE_DESC_STATUS_INT_UDP_0_M = BIT(18),
};
/* Bitmasks for singleq (base) virtchnl2_rx_base_desc */
enum virtchnl2_rx_base_desc_error_bits {
VIRTCHNL2_RX_BASE_DESC_ERROR_RXE_M = BIT(0),
VIRTCHNL2_RX_BASE_DESC_ERROR_ATRAEFAIL_M = BIT(1),
VIRTCHNL2_RX_BASE_DESC_ERROR_HBO_M = BIT(2),
VIRTCHNL2_RX_BASE_DESC_ERROR_L3L4E_M = GENMASK(5, 3),
VIRTCHNL2_RX_BASE_DESC_ERROR_IPE_M = BIT(3),
VIRTCHNL2_RX_BASE_DESC_ERROR_L4E_M = BIT(4),
VIRTCHNL2_RX_BASE_DESC_ERROR_EIPE_M = BIT(5),
VIRTCHNL2_RX_BASE_DESC_ERROR_OVERSIZE_M = BIT(6),
VIRTCHNL2_RX_BASE_DESC_ERROR_PPRS_M = BIT(7),
};
/* Bitmasks for singleq (base) virtchnl2_rx_base_desc */
#define VIRTCHNL2_RX_BASE_DESC_FLTSTAT_RSS_HASH_M GENMASK(13, 12)
/**
* struct virtchnl2_splitq_rx_buf_desc - SplitQ RX buffer descriptor format
* @qword0: RX buffer struct.
* @qword0.buf_id: Buffer identifier.
* @qword0.rsvd0: Reserved.
* @qword0.rsvd1: Reserved.
* @pkt_addr: Packet buffer address.
* @hdr_addr: Header buffer address.
* @rsvd2: Rerserved.
*
* Receive Descriptors
* SplitQ buffer
* | 16| 0|
* ----------------------------------------------------------------
* | RSV | Buffer ID |
* ----------------------------------------------------------------
* | Rx packet buffer address |
* ----------------------------------------------------------------
* | Rx header buffer address |
* ----------------------------------------------------------------
* | RSV |
* ----------------------------------------------------------------
* | 0|
*/
struct virtchnl2_splitq_rx_buf_desc {
struct {
__le16 buf_id;
__le16 rsvd0;
__le32 rsvd1;
} qword0;
__le64 pkt_addr;
__le64 hdr_addr;
__le64 rsvd2;
};
/**
* struct virtchnl2_singleq_rx_buf_desc - SingleQ RX buffer descriptor format.
* @pkt_addr: Packet buffer address.
* @hdr_addr: Header buffer address.
* @rsvd1: Reserved.
* @rsvd2: Reserved.
*
* SingleQ buffer
* | 0|
* ----------------------------------------------------------------
* | Rx packet buffer address |
* ----------------------------------------------------------------
* | Rx header buffer address |
* ----------------------------------------------------------------
* | RSV |
* ----------------------------------------------------------------
* | RSV |
* ----------------------------------------------------------------
* | 0|
*/
struct virtchnl2_singleq_rx_buf_desc {
__le64 pkt_addr;
__le64 hdr_addr;
__le64 rsvd1;
__le64 rsvd2;
};
/**
* struct virtchnl2_singleq_base_rx_desc - RX descriptor writeback format.
* @qword0: First quad word struct.
* @qword0.lo_dword: Lower dual word struct.
* @qword0.lo_dword.mirroring_status: Mirrored packet status.
* @qword0.lo_dword.l2tag1: Stripped L2 tag from the received packet.
* @qword0.hi_dword: High dual word union.
* @qword0.hi_dword.rss: RSS hash.
* @qword0.hi_dword.fd_id: Flow director filter id.
* @qword1: Second quad word struct.
* @qword1.status_error_ptype_len: Status/error/PTYPE/length.
* @qword2: Third quad word struct.
* @qword2.ext_status: Extended status.
* @qword2.rsvd: Reserved.
* @qword2.l2tag2_1: Extracted L2 tag 2 from the packet.
* @qword2.l2tag2_2: Reserved.
* @qword3: Fourth quad word struct.
* @qword3.reserved: Reserved.
* @qword3.fd_id: Flow director filter id.
*
* Profile ID 0x1, SingleQ, base writeback format
*/
struct virtchnl2_singleq_base_rx_desc {
struct {
struct {
__le16 mirroring_status;
__le16 l2tag1;
} lo_dword;
union {
__le32 rss;
__le32 fd_id;
} hi_dword;
} qword0;
struct {
__le64 status_error_ptype_len;
} qword1;
struct {
__le16 ext_status;
__le16 rsvd;
__le16 l2tag2_1;
__le16 l2tag2_2;
} qword2;
struct {
__le32 reserved;
__le32 fd_id;
} qword3;
};
/**
* struct virtchnl2_rx_flex_desc_nic - RX descriptor writeback format.
*
* @rxdid: Descriptor builder profile id.
* @mir_id_umb_cast: umb_cast=[7:6], mirror=[5:0]
* @ptype_flex_flags0: ff0=[15:10], ptype=[9:0]
* @pkt_len: Packet length, [15:14] are reserved.
* @hdr_len_sph_flex_flags1: ff1/ext=[15:12], sph=[11], header=[10:0].
* @status_error0: Status/Error section 0.
* @l2tag1: Stripped L2 tag from the received packet
* @rss_hash: RSS hash.
* @status_error1: Status/Error section 1.
* @flexi_flags2: Flexible flags section 2.
* @ts_low: Lower word of timestamp value.
* @l2tag2_1st: First L2TAG2.
* @l2tag2_2nd: Second L2TAG2.
* @flow_id: Flow id.
* @flex_ts: Timestamp and flexible flow id union.
* @flex_ts.ts_high: Timestamp higher word of the timestamp value.
* @flex_ts.flex.rsvd: Reserved.
* @flex_ts.flex.flow_id_ipv6: IPv6 flow id.
*
* Profile ID 0x2, SingleQ, flex writeback format
*/
struct virtchnl2_rx_flex_desc_nic {
/* Qword 0 */
u8 rxdid;
u8 mir_id_umb_cast;
__le16 ptype_flex_flags0;
__le16 pkt_len;
__le16 hdr_len_sph_flex_flags1;
/* Qword 1 */
__le16 status_error0;
__le16 l2tag1;
__le32 rss_hash;
/* Qword 2 */
__le16 status_error1;
u8 flexi_flags2;
u8 ts_low;
__le16 l2tag2_1st;
__le16 l2tag2_2nd;
/* Qword 3 */
__le32 flow_id;
union {
struct {
__le16 rsvd;
__le16 flow_id_ipv6;
} flex;
__le32 ts_high;
} flex_ts;
};
/**
* struct virtchnl2_rx_flex_desc_adv_nic_3 - RX descriptor writeback format.
* @rxdid_ucast: ucast=[7:6], rsvd=[5:4], profile_id=[3:0].
* @status_err0_qw0: Status/Error section 0 in quad word 0.
* @ptype_err_fflags0: ff0=[15:12], udp_len_err=[11], ip_hdr_err=[10],
* ptype=[9:0].
* @pktlen_gen_bufq_id: bufq_id=[15] only in splitq, gen=[14] only in splitq,
* plen=[13:0].
* @hdrlen_flags: miss_prepend=[15], trunc_mirr=[14], int_udp_0=[13],
* ext_udp0=[12], sph=[11] only in splitq, rsc=[10]
* only in splitq, header=[9:0].
* @status_err0_qw1: Status/Error section 0 in quad word 1.
* @status_err1: Status/Error section 1.
* @fflags1: Flexible flags section 1.
* @ts_low: Lower word of timestamp value.
* @buf_id: Buffer identifier. Only in splitq mode.
* @misc: Union.
* @misc.raw_cs: Raw checksum.
* @misc.l2tag1: Stripped L2 tag from the received packet
* @misc.rscseglen:
* @hash1: Lower bits of Rx hash value.
* @ff2_mirrid_hash2: Union.
* @ff2_mirrid_hash2.fflags2: Flexible flags section 2.
* @ff2_mirrid_hash2.mirrorid: Mirror id.
* @ff2_mirrid_hash2.rscseglen: RSC segment length.
* @hash3: Upper bits of Rx hash value.
* @l2tag2: Extracted L2 tag 2 from the packet.
* @fmd4: Flexible metadata container 4.
* @l2tag1: Stripped L2 tag from the received packet
* @fmd6: Flexible metadata container 6.
* @ts_high: Timestamp higher word of the timestamp value.
*
* Profile ID 0x2, SplitQ, flex writeback format
*
* Flex-field 0: BufferID
* Flex-field 1: Raw checksum/L2TAG1/RSC Seg Len (determined by HW)
* Flex-field 2: Hash[15:0]
* Flex-flags 2: Hash[23:16]
* Flex-field 3: L2TAG2
* Flex-field 5: L2TAG1
* Flex-field 7: Timestamp (upper 32 bits)
*/
struct virtchnl2_rx_flex_desc_adv_nic_3 {
/* Qword 0 */
u8 rxdid_ucast;
u8 status_err0_qw0;
__le16 ptype_err_fflags0;
__le16 pktlen_gen_bufq_id;
__le16 hdrlen_flags;
/* Qword 1 */
u8 status_err0_qw1;
u8 status_err1;
u8 fflags1;
u8 ts_low;
__le16 buf_id;
union {
__le16 raw_cs;
__le16 l2tag1;
__le16 rscseglen;
} misc;
/* Qword 2 */
__le16 hash1;
union {
u8 fflags2;
u8 mirrorid;
u8 hash2;
} ff2_mirrid_hash2;
u8 hash3;
__le16 l2tag2;
__le16 fmd4;
/* Qword 3 */
__le16 l2tag1;
__le16 fmd6;
__le32 ts_high;
};
/* Common union for accessing descriptor format structs */
union virtchnl2_rx_desc {
struct virtchnl2_singleq_base_rx_desc base_wb;
struct virtchnl2_rx_flex_desc_nic flex_nic_wb;
struct virtchnl2_rx_flex_desc_adv_nic_3 flex_adv_nic_3_wb;
};
#endif /* _VIRTCHNL_LAN_DESC_H_ */
@@ -0,0 +1,529 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2025 Intel Corporation */
#include "idpf.h"
#include "idpf_ptp.h"
#include "idpf_virtchnl.h"
#include "xdp.h"
#include "xsk.h"
static int idpf_rxq_for_each(const struct idpf_q_vec_rsrc *rsrc,
int (*fn)(struct idpf_rx_queue *rxq, void *arg),
void *arg)
{
bool splitq = idpf_is_queue_model_split(rsrc->rxq_model);
if (!rsrc->rxq_grps)
return -ENETDOWN;
for (u32 i = 0; i < rsrc->num_rxq_grp; i++) {
const struct idpf_rxq_group *rx_qgrp = &rsrc->rxq_grps[i];
u32 num_rxq;
if (splitq)
num_rxq = rx_qgrp->splitq.num_rxq_sets;
else
num_rxq = rx_qgrp->singleq.num_rxq;
for (u32 j = 0; j < num_rxq; j++) {
struct idpf_rx_queue *q;
int err;
if (splitq)
q = &rx_qgrp->splitq.rxq_sets[j]->rxq;
else
q = rx_qgrp->singleq.rxqs[j];
err = fn(q, arg);
if (err)
return err;
}
}
return 0;
}
static int __idpf_xdp_rxq_info_init(struct idpf_rx_queue *rxq, void *arg)
{
const struct idpf_vport *vport = rxq->q_vector->vport;
const struct idpf_q_vec_rsrc *rsrc;
u32 frag_size = 0;
bool split;
int err;
if (idpf_queue_has(XSK, rxq))
frag_size = rxq->bufq_sets[0].bufq.truesize;
err = __xdp_rxq_info_reg(&rxq->xdp_rxq, vport->netdev, rxq->idx,
rxq->q_vector->napi.napi_id,
frag_size);
if (err)
return err;
rsrc = &vport->dflt_qv_rsrc;
split = idpf_is_queue_model_split(rsrc->rxq_model);
if (idpf_queue_has(XSK, rxq)) {
err = xdp_rxq_info_reg_mem_model(&rxq->xdp_rxq,
MEM_TYPE_XSK_BUFF_POOL,
rxq->pool);
if (err)
goto unreg;
} else {
const struct page_pool *pp;
pp = split ? rxq->bufq_sets[0].bufq.pp : rxq->pp;
xdp_rxq_info_attach_page_pool(&rxq->xdp_rxq, pp);
}
if (!split)
return 0;
rxq->xdpsqs = &vport->txqs[rsrc->xdp_txq_offset];
rxq->num_xdp_txq = vport->num_xdp_txq;
return 0;
unreg:
xdp_rxq_info_unreg(&rxq->xdp_rxq);
return err;
}
int idpf_xdp_rxq_info_init(struct idpf_rx_queue *rxq)
{
return __idpf_xdp_rxq_info_init(rxq, NULL);
}
int idpf_xdp_rxq_info_init_all(const struct idpf_q_vec_rsrc *rsrc)
{
return idpf_rxq_for_each(rsrc, __idpf_xdp_rxq_info_init, NULL);
}
static int __idpf_xdp_rxq_info_deinit(struct idpf_rx_queue *rxq, void *arg)
{
if (idpf_is_queue_model_split((size_t)arg)) {
rxq->xdpsqs = NULL;
rxq->num_xdp_txq = 0;
}
if (!idpf_queue_has(XSK, rxq))
xdp_rxq_info_detach_mem_model(&rxq->xdp_rxq);
xdp_rxq_info_unreg(&rxq->xdp_rxq);
return 0;
}
void idpf_xdp_rxq_info_deinit(struct idpf_rx_queue *rxq, u32 model)
{
__idpf_xdp_rxq_info_deinit(rxq, (void *)(size_t)model);
}
void idpf_xdp_rxq_info_deinit_all(const struct idpf_q_vec_rsrc *rsrc)
{
idpf_rxq_for_each(rsrc, __idpf_xdp_rxq_info_deinit,
(void *)(size_t)rsrc->rxq_model);
}
static int idpf_xdp_rxq_assign_prog(struct idpf_rx_queue *rxq, void *arg)
{
struct bpf_prog *prog = arg;
struct bpf_prog *old;
if (prog)
bpf_prog_inc(prog);
old = rcu_replace_pointer(rxq->xdp_prog, prog, lockdep_rtnl_is_held());
if (old)
bpf_prog_put(old);
return 0;
}
void idpf_xdp_copy_prog_to_rqs(const struct idpf_q_vec_rsrc *rsrc,
struct bpf_prog *xdp_prog)
{
idpf_rxq_for_each(rsrc, idpf_xdp_rxq_assign_prog, xdp_prog);
}
static void idpf_xdp_tx_timer(struct work_struct *work);
int idpf_xdpsqs_get(const struct idpf_vport *vport)
{
struct libeth_xdpsq_timer **timers __free(kvfree) = NULL;
struct net_device *dev;
u32 sqs;
if (!idpf_xdp_enabled(vport))
return 0;
timers = kvzalloc_objs(*timers, vport->num_xdp_txq);
if (!timers)
return -ENOMEM;
for (u32 i = 0; i < vport->num_xdp_txq; i++) {
timers[i] = kzalloc_node(sizeof(*timers[i]), GFP_KERNEL,
cpu_to_mem(i));
if (!timers[i]) {
for (int j = i - 1; j >= 0; j--)
kfree(timers[j]);
return -ENOMEM;
}
}
dev = vport->netdev;
sqs = vport->dflt_qv_rsrc.xdp_txq_offset;
for (u32 i = sqs; i < vport->num_txq; i++) {
struct idpf_tx_queue *xdpsq = vport->txqs[i];
xdpsq->complq = xdpsq->txq_grp->complq;
kfree(xdpsq->refillq);
xdpsq->refillq = NULL;
idpf_queue_clear(FLOW_SCH_EN, xdpsq);
idpf_queue_clear(FLOW_SCH_EN, xdpsq->complq);
idpf_queue_set(NOIRQ, xdpsq);
idpf_queue_set(XDP, xdpsq);
idpf_queue_set(XDP, xdpsq->complq);
xdpsq->timer = timers[i - sqs];
libeth_xdpsq_get(&xdpsq->xdp_lock, dev, vport->xdpsq_share);
libeth_xdpsq_init_timer(xdpsq->timer, xdpsq, &xdpsq->xdp_lock,
idpf_xdp_tx_timer);
xdpsq->pending = 0;
xdpsq->xdp_tx = 0;
xdpsq->thresh = libeth_xdp_queue_threshold(xdpsq->desc_count);
}
return 0;
}
void idpf_xdpsqs_put(const struct idpf_vport *vport)
{
struct net_device *dev;
u32 sqs;
if (!idpf_xdp_enabled(vport))
return;
dev = vport->netdev;
sqs = vport->dflt_qv_rsrc.xdp_txq_offset;
for (u32 i = sqs; i < vport->num_txq; i++) {
struct idpf_tx_queue *xdpsq = vport->txqs[i];
if (!idpf_queue_has_clear(XDP, xdpsq))
continue;
libeth_xdpsq_deinit_timer(xdpsq->timer);
libeth_xdpsq_put(&xdpsq->xdp_lock, dev);
kfree(xdpsq->timer);
xdpsq->refillq = NULL;
idpf_queue_clear(NOIRQ, xdpsq);
}
}
static int idpf_xdp_parse_cqe(const struct idpf_splitq_4b_tx_compl_desc *desc,
bool gen)
{
u32 val;
#ifdef __LIBETH_WORD_ACCESS
val = *(const u32 *)desc;
#else
val = ((u32)le16_to_cpu(desc->q_head_compl_tag.q_head) << 16) |
le16_to_cpu(desc->qid_comptype_gen);
#endif
if (!!(val & IDPF_TXD_COMPLQ_GEN_M) != gen)
return -ENODATA;
if (unlikely((val & GENMASK(IDPF_TXD_COMPLQ_GEN_S - 1, 0)) !=
FIELD_PREP(IDPF_TXD_COMPLQ_COMPL_TYPE_M,
IDPF_TXD_COMPLT_RS)))
return -EINVAL;
return upper_16_bits(val);
}
u32 idpf_xdpsq_poll(struct idpf_tx_queue *xdpsq, u32 budget)
{
struct idpf_compl_queue *cq = xdpsq->complq;
u32 tx_ntc = xdpsq->next_to_clean;
u32 tx_cnt = xdpsq->desc_count;
u32 ntc = cq->next_to_clean;
u32 cnt = cq->desc_count;
u32 done_frames;
bool gen;
gen = idpf_queue_has(GEN_CHK, cq);
for (done_frames = 0; done_frames < budget; ) {
int ret;
ret = idpf_xdp_parse_cqe(&cq->comp_4b[ntc], gen);
if (ret >= 0) {
done_frames = ret > tx_ntc ? ret - tx_ntc :
ret + tx_cnt - tx_ntc;
goto next;
}
switch (ret) {
case -ENODATA:
goto out;
case -EINVAL:
break;
}
next:
if (unlikely(++ntc == cnt)) {
ntc = 0;
gen = !gen;
idpf_queue_change(GEN_CHK, cq);
}
}
out:
cq->next_to_clean = ntc;
return done_frames;
}
static u32 idpf_xdpsq_complete(void *_xdpsq, u32 budget)
{
struct libeth_xdpsq_napi_stats ss = { };
struct idpf_tx_queue *xdpsq = _xdpsq;
u32 tx_ntc = xdpsq->next_to_clean;
u32 tx_cnt = xdpsq->desc_count;
struct xdp_frame_bulk bq;
struct libeth_cq_pp cp = {
.dev = xdpsq->dev,
.bq = &bq,
.xss = &ss,
.napi = true,
};
u32 done_frames;
done_frames = idpf_xdpsq_poll(xdpsq, budget);
if (unlikely(!done_frames))
return 0;
xdp_frame_bulk_init(&bq);
for (u32 i = 0; likely(i < done_frames); i++) {
libeth_xdp_complete_tx(&xdpsq->tx_buf[tx_ntc], &cp);
if (unlikely(++tx_ntc == tx_cnt))
tx_ntc = 0;
}
xdp_flush_frame_bulk(&bq);
xdpsq->next_to_clean = tx_ntc;
xdpsq->pending -= done_frames;
xdpsq->xdp_tx -= cp.xdp_tx;
return done_frames;
}
static u32 idpf_xdp_tx_prep(void *_xdpsq, struct libeth_xdpsq *sq)
{
struct idpf_tx_queue *xdpsq = _xdpsq;
u32 free;
libeth_xdpsq_lock(&xdpsq->xdp_lock);
free = xdpsq->desc_count - xdpsq->pending;
if (free < xdpsq->thresh)
free += idpf_xdpsq_complete(xdpsq, xdpsq->thresh);
*sq = (struct libeth_xdpsq){
.sqes = xdpsq->tx_buf,
.descs = xdpsq->desc_ring,
.count = xdpsq->desc_count,
.lock = &xdpsq->xdp_lock,
.ntu = &xdpsq->next_to_use,
.pending = &xdpsq->pending,
.xdp_tx = &xdpsq->xdp_tx,
};
return free;
}
LIBETH_XDP_DEFINE_START();
LIBETH_XDP_DEFINE_TIMER(static idpf_xdp_tx_timer, idpf_xdpsq_complete);
LIBETH_XDP_DEFINE_FLUSH_TX(idpf_xdp_tx_flush_bulk, idpf_xdp_tx_prep,
idpf_xdp_tx_xmit);
LIBETH_XDP_DEFINE_FLUSH_XMIT(static idpf_xdp_xmit_flush_bulk, idpf_xdp_tx_prep,
idpf_xdp_tx_xmit);
LIBETH_XDP_DEFINE_END();
int idpf_xdp_xmit(struct net_device *dev, int n, struct xdp_frame **frames,
u32 flags)
{
const struct idpf_netdev_priv *np = netdev_priv(dev);
const struct idpf_vport *vport = np->vport;
u32 xdp_txq_offset;
if (unlikely(!netif_carrier_ok(dev) || !vport->link_up))
return -ENETDOWN;
xdp_txq_offset = vport->dflt_qv_rsrc.xdp_txq_offset;
return libeth_xdp_xmit_do_bulk(dev, n, frames, flags,
&vport->txqs[xdp_txq_offset],
vport->num_xdp_txq,
idpf_xdp_xmit_flush_bulk,
idpf_xdp_tx_finalize);
}
static int idpf_xdpmo_rx_hash(const struct xdp_md *ctx, u32 *hash,
enum xdp_rss_hash_type *rss_type)
{
const struct libeth_xdp_buff *xdp = (typeof(xdp))ctx;
struct idpf_xdp_rx_desc desc __uninitialized;
const struct idpf_rx_queue *rxq;
struct libeth_rx_pt pt;
rxq = libeth_xdp_buff_to_rq(xdp, typeof(*rxq), xdp_rxq);
idpf_xdp_get_qw0(&desc, xdp->desc);
pt = rxq->rx_ptype_lkup[idpf_xdp_rx_pt(&desc)];
if (!libeth_rx_pt_has_hash(rxq->xdp_rxq.dev, pt))
return -ENODATA;
idpf_xdp_get_qw2(&desc, xdp->desc);
return libeth_xdpmo_rx_hash(hash, rss_type, idpf_xdp_rx_hash(&desc),
pt);
}
static int idpf_xdpmo_rx_timestamp(const struct xdp_md *ctx, u64 *timestamp)
{
const struct libeth_xdp_buff *xdp = (typeof(xdp))ctx;
struct idpf_xdp_rx_desc desc __uninitialized;
const struct idpf_rx_queue *rxq;
u64 cached_time, ts_ns;
u32 ts_high;
rxq = libeth_xdp_buff_to_rq(xdp, typeof(*rxq), xdp_rxq);
if (!idpf_queue_has(PTP, rxq))
return -ENODATA;
idpf_xdp_get_qw1(&desc, xdp->desc);
if (!(idpf_xdp_rx_ts_low(&desc) & VIRTCHNL2_RX_FLEX_TSTAMP_VALID))
return -ENODATA;
cached_time = READ_ONCE(rxq->cached_phc_time);
idpf_xdp_get_qw3(&desc, xdp->desc);
ts_high = idpf_xdp_rx_ts_high(&desc);
ts_ns = idpf_ptp_tstamp_extend_32b_to_64b(cached_time, ts_high);
*timestamp = ts_ns;
return 0;
}
static const struct xdp_metadata_ops idpf_xdpmo = {
.xmo_rx_hash = idpf_xdpmo_rx_hash,
.xmo_rx_timestamp = idpf_xdpmo_rx_timestamp,
};
void idpf_xdp_set_features(const struct idpf_vport *vport)
{
if (!idpf_is_queue_model_split(vport->dflt_qv_rsrc.rxq_model))
return;
libeth_xdp_set_features_noredir(vport->netdev, &idpf_xdpmo,
idpf_get_max_tx_bufs(vport->adapter),
libeth_xsktmo);
}
static int idpf_xdp_setup_prog(struct idpf_vport *vport,
const struct netdev_bpf *xdp)
{
const struct idpf_netdev_priv *np = netdev_priv(vport->netdev);
const struct idpf_q_vec_rsrc *rsrc = &vport->dflt_qv_rsrc;
struct bpf_prog *old, *prog = xdp->prog;
struct idpf_vport_config *cfg;
int ret;
cfg = vport->adapter->vport_config[vport->idx];
if (test_bit(IDPF_REMOVE_IN_PROG, vport->adapter->flags) ||
!test_bit(IDPF_VPORT_REG_NETDEV, cfg->flags) ||
!!vport->xdp_prog == !!prog) {
if (test_bit(IDPF_VPORT_UP, np->state))
idpf_xdp_copy_prog_to_rqs(rsrc, prog);
old = xchg(&vport->xdp_prog, prog);
if (old)
bpf_prog_put(old);
cfg->user_config.xdp_prog = prog;
return 0;
}
if (!vport->num_xdp_txq && vport->num_txq == cfg->max_q.max_txq) {
NL_SET_ERR_MSG_MOD(xdp->extack,
"No Tx queues available for XDP, please decrease the number of regular SQs");
return -ENOSPC;
}
old = cfg->user_config.xdp_prog;
cfg->user_config.xdp_prog = prog;
ret = idpf_initiate_soft_reset(vport, IDPF_SR_Q_CHANGE);
if (ret) {
NL_SET_ERR_MSG_MOD(xdp->extack,
"Could not reopen the vport after XDP setup");
cfg->user_config.xdp_prog = old;
old = prog;
}
if (old)
bpf_prog_put(old);
libeth_xdp_set_redirect(vport->netdev, vport->xdp_prog);
return ret;
}
int idpf_xdp(struct net_device *dev, struct netdev_bpf *xdp)
{
struct idpf_vport *vport;
int ret;
idpf_vport_ctrl_lock(dev);
vport = idpf_netdev_to_vport(dev);
if (!idpf_is_queue_model_split(vport->dflt_qv_rsrc.txq_model))
goto notsupp;
switch (xdp->command) {
case XDP_SETUP_PROG:
ret = idpf_xdp_setup_prog(vport, xdp);
break;
case XDP_SETUP_XSK_POOL:
ret = idpf_xsk_pool_setup(vport, xdp);
break;
default:
notsupp:
ret = -EOPNOTSUPP;
break;
}
idpf_vport_ctrl_unlock(dev);
return ret;
}
@@ -0,0 +1,195 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (C) 2025 Intel Corporation */
#ifndef _IDPF_XDP_H_
#define _IDPF_XDP_H_
#include <net/libeth/xdp.h>
#include "idpf_txrx.h"
int idpf_xdp_rxq_info_init(struct idpf_rx_queue *rxq);
int idpf_xdp_rxq_info_init_all(const struct idpf_q_vec_rsrc *rsrc);
void idpf_xdp_rxq_info_deinit(struct idpf_rx_queue *rxq, u32 model);
void idpf_xdp_rxq_info_deinit_all(const struct idpf_q_vec_rsrc *rsrc);
void idpf_xdp_copy_prog_to_rqs(const struct idpf_q_vec_rsrc *rsrc,
struct bpf_prog *xdp_prog);
int idpf_xdpsqs_get(const struct idpf_vport *vport);
void idpf_xdpsqs_put(const struct idpf_vport *vport);
u32 idpf_xdpsq_poll(struct idpf_tx_queue *xdpsq, u32 budget);
bool idpf_xdp_tx_flush_bulk(struct libeth_xdp_tx_bulk *bq, u32 flags);
/**
* idpf_xdp_tx_xmit - produce a single HW Tx descriptor out of XDP desc
* @desc: XDP descriptor to pull the DMA address and length from
* @i: descriptor index on the queue to fill
* @sq: XDP queue to produce the HW Tx descriptor on
* @priv: &xsk_tx_metadata_ops on XSk xmit or %NULL
*/
static inline void idpf_xdp_tx_xmit(struct libeth_xdp_tx_desc desc, u32 i,
const struct libeth_xdpsq *sq, u64 priv)
{
struct idpf_flex_tx_desc *tx_desc = sq->descs;
u32 cmd;
cmd = FIELD_PREP(IDPF_FLEX_TXD_QW1_DTYPE_M,
IDPF_TX_DESC_DTYPE_FLEX_L2TAG1_L2TAG2);
if (desc.flags & LIBETH_XDP_TX_LAST)
cmd |= FIELD_PREP(IDPF_FLEX_TXD_QW1_CMD_M,
IDPF_TX_DESC_CMD_EOP);
if (priv && (desc.flags & LIBETH_XDP_TX_CSUM))
cmd |= FIELD_PREP(IDPF_FLEX_TXD_QW1_CMD_M,
IDPF_TX_FLEX_DESC_CMD_CS_EN);
tx_desc = &tx_desc[i];
tx_desc->buf_addr = cpu_to_le64(desc.addr);
#ifdef __LIBETH_WORD_ACCESS
*(u64 *)&tx_desc->qw1 = ((u64)desc.len << 48) | cmd;
#else
tx_desc->qw1.buf_size = cpu_to_le16(desc.len);
tx_desc->qw1.cmd_dtype = cpu_to_le16(cmd);
#endif
}
static inline void idpf_xdpsq_set_rs(const struct idpf_tx_queue *xdpsq)
{
u32 ntu, cmd;
ntu = xdpsq->next_to_use;
if (unlikely(!ntu))
ntu = xdpsq->desc_count;
cmd = FIELD_PREP(IDPF_FLEX_TXD_QW1_CMD_M, IDPF_TX_DESC_CMD_RS);
#ifdef __LIBETH_WORD_ACCESS
*(u64 *)&xdpsq->flex_tx[ntu - 1].q.qw1 |= cmd;
#else
xdpsq->flex_tx[ntu - 1].q.qw1.cmd_dtype |= cpu_to_le16(cmd);
#endif
}
static inline void idpf_xdpsq_update_tail(const struct idpf_tx_queue *xdpsq)
{
dma_wmb();
writel_relaxed(xdpsq->next_to_use, xdpsq->tail);
}
/**
* idpf_xdp_tx_finalize - finalize sending over XDPSQ
* @_xdpsq: XDP Tx queue
* @sent: whether any frames were sent
* @flush: whether to update RS bit and the tail register
*
* Set the RS bit ("end of batch"), bump the tail, and queue the cleanup timer.
* To be called after a NAPI polling loop, at the end of .ndo_xdp_xmit() etc.
*/
static inline void idpf_xdp_tx_finalize(void *_xdpsq, bool sent, bool flush)
{
struct idpf_tx_queue *xdpsq = _xdpsq;
if ((!flush || unlikely(!sent)) &&
likely(xdpsq->desc_count - 1 != xdpsq->pending))
return;
libeth_xdpsq_lock(&xdpsq->xdp_lock);
idpf_xdpsq_set_rs(xdpsq);
idpf_xdpsq_update_tail(xdpsq);
libeth_xdpsq_queue_timer(xdpsq->timer);
libeth_xdpsq_unlock(&xdpsq->xdp_lock);
}
struct idpf_xdp_rx_desc {
aligned_u64 qw0;
#define IDPF_XDP_RX_BUFQ BIT_ULL(47)
#define IDPF_XDP_RX_GEN BIT_ULL(46)
#define IDPF_XDP_RX_LEN GENMASK_ULL(45, 32)
#define IDPF_XDP_RX_PT GENMASK_ULL(25, 16)
aligned_u64 qw1;
#define IDPF_XDP_RX_BUF GENMASK_ULL(47, 32)
#define IDPF_XDP_RX_EOP BIT_ULL(1)
#define IDPF_XDP_RX_TS_LOW GENMASK_ULL(31, 24)
aligned_u64 qw2;
#define IDPF_XDP_RX_HASH GENMASK_ULL(31, 0)
aligned_u64 qw3;
#define IDPF_XDP_RX_TS_HIGH GENMASK_ULL(63, 32)
} __aligned(4 * sizeof(u64));
static_assert(sizeof(struct idpf_xdp_rx_desc) ==
sizeof(struct virtchnl2_rx_flex_desc_adv_nic_3));
#define idpf_xdp_rx_bufq(desc) !!((desc)->qw0 & IDPF_XDP_RX_BUFQ)
#define idpf_xdp_rx_gen(desc) !!((desc)->qw0 & IDPF_XDP_RX_GEN)
#define idpf_xdp_rx_len(desc) FIELD_GET(IDPF_XDP_RX_LEN, (desc)->qw0)
#define idpf_xdp_rx_pt(desc) FIELD_GET(IDPF_XDP_RX_PT, (desc)->qw0)
#define idpf_xdp_rx_buf(desc) FIELD_GET(IDPF_XDP_RX_BUF, (desc)->qw1)
#define idpf_xdp_rx_eop(desc) !!((desc)->qw1 & IDPF_XDP_RX_EOP)
#define idpf_xdp_rx_hash(desc) FIELD_GET(IDPF_XDP_RX_HASH, (desc)->qw2)
#define idpf_xdp_rx_ts_low(desc) FIELD_GET(IDPF_XDP_RX_TS_LOW, (desc)->qw1)
#define idpf_xdp_rx_ts_high(desc) FIELD_GET(IDPF_XDP_RX_TS_HIGH, (desc)->qw3)
static inline void
idpf_xdp_get_qw0(struct idpf_xdp_rx_desc *desc,
const struct virtchnl2_rx_flex_desc_adv_nic_3 *rxd)
{
#ifdef __LIBETH_WORD_ACCESS
desc->qw0 = ((const typeof(desc))rxd)->qw0;
#else
desc->qw0 = ((u64)le16_to_cpu(rxd->pktlen_gen_bufq_id) << 32) |
((u64)le16_to_cpu(rxd->ptype_err_fflags0) << 16);
#endif
}
static inline void
idpf_xdp_get_qw1(struct idpf_xdp_rx_desc *desc,
const struct virtchnl2_rx_flex_desc_adv_nic_3 *rxd)
{
#ifdef __LIBETH_WORD_ACCESS
desc->qw1 = ((const typeof(desc))rxd)->qw1;
#else
desc->qw1 = ((u64)le16_to_cpu(rxd->buf_id) << 32) |
((u64)rxd->ts_low << 24) |
((u64)rxd->fflags1 << 16) |
((u64)rxd->status_err1 << 8) |
rxd->status_err0_qw1;
#endif
}
static inline void
idpf_xdp_get_qw2(struct idpf_xdp_rx_desc *desc,
const struct virtchnl2_rx_flex_desc_adv_nic_3 *rxd)
{
#ifdef __LIBETH_WORD_ACCESS
desc->qw2 = ((const typeof(desc))rxd)->qw2;
#else
desc->qw2 = ((u64)rxd->hash3 << 24) |
((u64)rxd->ff2_mirrid_hash2.hash2 << 16) |
le16_to_cpu(rxd->hash1);
#endif
}
static inline void
idpf_xdp_get_qw3(struct idpf_xdp_rx_desc *desc,
const struct virtchnl2_rx_flex_desc_adv_nic_3 *rxd)
{
#ifdef __LIBETH_WORD_ACCESS
desc->qw3 = ((const typeof(desc))rxd)->qw3;
#else
desc->qw3 = ((u64)le32_to_cpu(rxd->ts_high) << 32) |
((u64)le16_to_cpu(rxd->fmd6) << 16) |
le16_to_cpu(rxd->l2tag1);
#endif
}
void idpf_xdp_set_features(const struct idpf_vport *vport);
int idpf_xdp(struct net_device *dev, struct netdev_bpf *xdp);
int idpf_xdp_xmit(struct net_device *dev, int n, struct xdp_frame **frames,
u32 flags);
#endif /* _IDPF_XDP_H_ */
@@ -0,0 +1,636 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2025 Intel Corporation */
#include <net/libeth/xsk.h>
#include "idpf.h"
#include "xdp.h"
#include "xsk.h"
static void idpf_xsk_tx_timer(struct work_struct *work);
static void idpf_xsk_setup_rxq(const struct idpf_vport *vport,
struct idpf_rx_queue *rxq)
{
struct xsk_buff_pool *pool;
pool = xsk_get_pool_from_qid(vport->netdev, rxq->idx);
if (!pool || !pool->dev || !xsk_buff_can_alloc(pool, 1))
return;
rxq->pool = pool;
idpf_queue_set(XSK, rxq);
}
static void idpf_xsk_setup_bufq(const struct idpf_vport *vport,
struct idpf_buf_queue *bufq)
{
const struct idpf_q_vec_rsrc *rsrc = &vport->dflt_qv_rsrc;
struct xsk_buff_pool *pool;
u32 qid = U32_MAX;
for (u32 i = 0; i < rsrc->num_rxq_grp; i++) {
const struct idpf_rxq_group *grp = &rsrc->rxq_grps[i];
for (u32 j = 0; j < rsrc->num_bufqs_per_qgrp; j++) {
if (&grp->splitq.bufq_sets[j].bufq == bufq) {
qid = grp->splitq.rxq_sets[0]->rxq.idx;
goto setup;
}
}
}
setup:
pool = xsk_get_pool_from_qid(vport->netdev, qid);
if (!pool || !pool->dev || !xsk_buff_can_alloc(pool, 1))
return;
bufq->pool = pool;
idpf_queue_set(XSK, bufq);
}
static void idpf_xsk_setup_txq(const struct idpf_vport *vport,
struct idpf_tx_queue *txq)
{
struct xsk_buff_pool *pool;
u32 qid;
idpf_queue_clear(XSK, txq);
if (!idpf_queue_has(XDP, txq))
return;
qid = txq->idx - vport->dflt_qv_rsrc.xdp_txq_offset;
pool = xsk_get_pool_from_qid(vport->netdev, qid);
if (!pool || !pool->dev)
return;
txq->pool = pool;
libeth_xdpsq_init_timer(txq->timer, txq, &txq->xdp_lock,
idpf_xsk_tx_timer);
idpf_queue_assign(NOIRQ, txq, xsk_uses_need_wakeup(pool));
idpf_queue_set(XSK, txq);
}
static void idpf_xsk_setup_complq(const struct idpf_vport *vport,
struct idpf_compl_queue *complq)
{
const struct xsk_buff_pool *pool;
u32 qid;
idpf_queue_clear(XSK, complq);
if (!idpf_queue_has(XDP, complq))
return;
qid = complq->txq_grp->txqs[0]->idx -
vport->dflt_qv_rsrc.xdp_txq_offset;
pool = xsk_get_pool_from_qid(vport->netdev, qid);
if (!pool || !pool->dev)
return;
idpf_queue_set(XSK, complq);
}
void idpf_xsk_setup_queue(const struct idpf_vport *vport, void *q,
enum virtchnl2_queue_type type)
{
if (!idpf_xdp_enabled(vport))
return;
switch (type) {
case VIRTCHNL2_QUEUE_TYPE_RX:
idpf_xsk_setup_rxq(vport, q);
break;
case VIRTCHNL2_QUEUE_TYPE_RX_BUFFER:
idpf_xsk_setup_bufq(vport, q);
break;
case VIRTCHNL2_QUEUE_TYPE_TX:
idpf_xsk_setup_txq(vport, q);
break;
case VIRTCHNL2_QUEUE_TYPE_TX_COMPLETION:
idpf_xsk_setup_complq(vport, q);
break;
default:
break;
}
}
void idpf_xsk_clear_queue(void *q, enum virtchnl2_queue_type type)
{
struct idpf_compl_queue *complq;
struct idpf_buf_queue *bufq;
struct idpf_rx_queue *rxq;
struct idpf_tx_queue *txq;
switch (type) {
case VIRTCHNL2_QUEUE_TYPE_RX:
rxq = q;
if (!idpf_queue_has_clear(XSK, rxq))
return;
rxq->pool = NULL;
break;
case VIRTCHNL2_QUEUE_TYPE_RX_BUFFER:
bufq = q;
if (!idpf_queue_has_clear(XSK, bufq))
return;
bufq->pool = NULL;
break;
case VIRTCHNL2_QUEUE_TYPE_TX:
txq = q;
if (!idpf_queue_has_clear(XSK, txq))
return;
idpf_queue_set(NOIRQ, txq);
txq->dev = txq->netdev->dev.parent;
break;
case VIRTCHNL2_QUEUE_TYPE_TX_COMPLETION:
complq = q;
idpf_queue_clear(XSK, complq);
break;
default:
break;
}
}
void idpf_xsk_init_wakeup(struct idpf_q_vector *qv)
{
libeth_xsk_init_wakeup(&qv->csd, &qv->napi);
}
void idpf_xsksq_clean(struct idpf_tx_queue *xdpsq)
{
struct libeth_xdpsq_napi_stats ss = { };
u32 ntc = xdpsq->next_to_clean;
struct xdp_frame_bulk bq;
struct libeth_cq_pp cp = {
.dev = xdpsq->pool->dev,
.bq = &bq,
.xss = &ss,
};
u32 xsk_frames = 0;
xdp_frame_bulk_init(&bq);
while (ntc != xdpsq->next_to_use) {
struct libeth_sqe *sqe = &xdpsq->tx_buf[ntc];
if (sqe->type)
libeth_xdp_complete_tx(sqe, &cp);
else
xsk_frames++;
if (unlikely(++ntc == xdpsq->desc_count))
ntc = 0;
}
xdp_flush_frame_bulk(&bq);
if (xsk_frames)
xsk_tx_completed(xdpsq->pool, xsk_frames);
}
static noinline u32 idpf_xsksq_complete_slow(struct idpf_tx_queue *xdpsq,
u32 done)
{
struct libeth_xdpsq_napi_stats ss = { };
u32 ntc = xdpsq->next_to_clean;
u32 cnt = xdpsq->desc_count;
struct xdp_frame_bulk bq;
struct libeth_cq_pp cp = {
.dev = xdpsq->pool->dev,
.bq = &bq,
.xss = &ss,
.napi = true,
};
u32 xsk_frames = 0;
xdp_frame_bulk_init(&bq);
for (u32 i = 0; likely(i < done); i++) {
struct libeth_sqe *sqe = &xdpsq->tx_buf[ntc];
if (sqe->type)
libeth_xdp_complete_tx(sqe, &cp);
else
xsk_frames++;
if (unlikely(++ntc == cnt))
ntc = 0;
}
xdp_flush_frame_bulk(&bq);
xdpsq->next_to_clean = ntc;
xdpsq->xdp_tx -= cp.xdp_tx;
return xsk_frames;
}
static __always_inline u32 idpf_xsksq_complete(void *_xdpsq, u32 budget)
{
struct idpf_tx_queue *xdpsq = _xdpsq;
u32 tx_ntc = xdpsq->next_to_clean;
u32 tx_cnt = xdpsq->desc_count;
u32 done_frames;
u32 xsk_frames;
done_frames = idpf_xdpsq_poll(xdpsq, budget);
if (unlikely(!done_frames))
return 0;
if (likely(!xdpsq->xdp_tx)) {
tx_ntc += done_frames;
if (tx_ntc >= tx_cnt)
tx_ntc -= tx_cnt;
xdpsq->next_to_clean = tx_ntc;
xsk_frames = done_frames;
goto finalize;
}
xsk_frames = idpf_xsksq_complete_slow(xdpsq, done_frames);
if (xsk_frames)
finalize:
xsk_tx_completed(xdpsq->pool, xsk_frames);
xdpsq->pending -= done_frames;
return done_frames;
}
static u32 idpf_xsk_tx_prep(void *_xdpsq, struct libeth_xdpsq *sq)
{
struct idpf_tx_queue *xdpsq = _xdpsq;
u32 free;
libeth_xdpsq_lock(&xdpsq->xdp_lock);
free = xdpsq->desc_count - xdpsq->pending;
if (free < xdpsq->thresh)
free += idpf_xsksq_complete(xdpsq, xdpsq->thresh);
*sq = (struct libeth_xdpsq){
.pool = xdpsq->pool,
.sqes = xdpsq->tx_buf,
.descs = xdpsq->desc_ring,
.count = xdpsq->desc_count,
.lock = &xdpsq->xdp_lock,
.ntu = &xdpsq->next_to_use,
.pending = &xdpsq->pending,
.xdp_tx = &xdpsq->xdp_tx,
};
return free;
}
static u32 idpf_xsk_xmit_prep(void *_xdpsq, struct libeth_xdpsq *sq)
{
struct idpf_tx_queue *xdpsq = _xdpsq;
*sq = (struct libeth_xdpsq){
.pool = xdpsq->pool,
.sqes = xdpsq->tx_buf,
.descs = xdpsq->desc_ring,
.count = xdpsq->desc_count,
.lock = &xdpsq->xdp_lock,
.ntu = &xdpsq->next_to_use,
.pending = &xdpsq->pending,
};
/*
* The queue is cleaned, the budget is already known, optimize out
* the second min() by passing the type limit.
*/
return U32_MAX;
}
bool idpf_xsk_xmit(struct idpf_tx_queue *xsksq)
{
u32 free;
libeth_xdpsq_lock(&xsksq->xdp_lock);
free = xsksq->desc_count - xsksq->pending;
if (free < xsksq->thresh)
free += idpf_xsksq_complete(xsksq, xsksq->thresh);
return libeth_xsk_xmit_do_bulk(xsksq->pool, xsksq,
min(free - 1, xsksq->thresh),
libeth_xsktmo, idpf_xsk_xmit_prep,
idpf_xdp_tx_xmit, idpf_xdp_tx_finalize);
}
LIBETH_XDP_DEFINE_START();
LIBETH_XDP_DEFINE_TIMER(static idpf_xsk_tx_timer, idpf_xsksq_complete);
LIBETH_XSK_DEFINE_FLUSH_TX(static idpf_xsk_tx_flush_bulk, idpf_xsk_tx_prep,
idpf_xdp_tx_xmit);
LIBETH_XSK_DEFINE_RUN(static idpf_xsk_run_pass, idpf_xsk_run_prog,
idpf_xsk_tx_flush_bulk, idpf_rx_process_skb_fields);
LIBETH_XSK_DEFINE_FINALIZE(static idpf_xsk_finalize_rx, idpf_xsk_tx_flush_bulk,
idpf_xdp_tx_finalize);
LIBETH_XDP_DEFINE_END();
static void idpf_xskfqe_init(const struct libeth_xskfq_fp *fq, u32 i)
{
struct virtchnl2_splitq_rx_buf_desc *desc = fq->descs;
desc = &desc[i];
#ifdef __LIBETH_WORD_ACCESS
*(u64 *)&desc->qword0 = i;
#else
desc->qword0.buf_id = cpu_to_le16(i);
#endif
desc->pkt_addr = cpu_to_le64(libeth_xsk_buff_xdp_get_dma(fq->fqes[i]));
}
static bool idpf_xskfq_refill_thresh(struct idpf_buf_queue *bufq, u32 count)
{
struct libeth_xskfq_fp fq = {
.pool = bufq->pool,
.fqes = bufq->xsk_buf,
.descs = bufq->split_buf,
.ntu = bufq->next_to_use,
.count = bufq->desc_count,
};
u32 done;
done = libeth_xskfqe_alloc(&fq, count, idpf_xskfqe_init);
writel(fq.ntu, bufq->tail);
bufq->next_to_use = fq.ntu;
bufq->pending -= done;
return done == count;
}
static bool idpf_xskfq_refill(struct idpf_buf_queue *bufq)
{
u32 count, rx_thresh = bufq->thresh;
count = ALIGN_DOWN(bufq->pending - 1, rx_thresh);
for (u32 i = 0; i < count; i += rx_thresh) {
if (unlikely(!idpf_xskfq_refill_thresh(bufq, rx_thresh)))
return false;
}
return true;
}
int idpf_xskfq_init(struct idpf_buf_queue *bufq)
{
struct libeth_xskfq fq = {
.pool = bufq->pool,
.count = bufq->desc_count,
.nid = idpf_q_vector_to_mem(bufq->q_vector),
};
int ret;
ret = libeth_xskfq_create(&fq);
if (ret)
return ret;
bufq->xsk_buf = fq.fqes;
bufq->pending = fq.pending;
bufq->thresh = fq.thresh;
bufq->rx_buf_size = fq.buf_len;
bufq->truesize = fq.truesize;
if (!idpf_xskfq_refill(bufq))
netdev_err(bufq->pool->netdev,
"failed to allocate XSk buffers for qid %d\n",
bufq->pool->queue_id);
bufq->next_to_alloc = bufq->next_to_use;
idpf_queue_clear(HSPLIT_EN, bufq);
bufq->rx_hbuf_size = 0;
return 0;
}
void idpf_xskfq_rel(struct idpf_buf_queue *bufq)
{
struct libeth_xskfq fq = {
.fqes = bufq->xsk_buf,
};
libeth_xskfq_destroy(&fq);
bufq->rx_buf_size = fq.buf_len;
bufq->thresh = fq.thresh;
bufq->pending = fq.pending;
}
struct idpf_xskfq_refill_set {
struct {
struct idpf_buf_queue *q;
u32 buf_id;
u32 pending;
} bufqs[IDPF_MAX_BUFQS_PER_RXQ_GRP];
};
static bool idpf_xskfq_refill_set(const struct idpf_xskfq_refill_set *set)
{
bool ret = true;
for (u32 i = 0; i < ARRAY_SIZE(set->bufqs); i++) {
struct idpf_buf_queue *bufq = set->bufqs[i].q;
u32 ntc;
if (!bufq)
continue;
ntc = set->bufqs[i].buf_id;
if (unlikely(++ntc == bufq->desc_count))
ntc = 0;
bufq->next_to_clean = ntc;
bufq->pending += set->bufqs[i].pending;
if (bufq->pending > bufq->thresh)
ret &= idpf_xskfq_refill(bufq);
}
return ret;
}
int idpf_xskrq_poll(struct idpf_rx_queue *rxq, u32 budget)
{
struct idpf_xskfq_refill_set set = { };
struct libeth_rq_napi_stats rs = { };
bool wake, gen, fail = false;
u32 ntc = rxq->next_to_clean;
struct libeth_xdp_buff *xdp;
LIBETH_XDP_ONSTACK_BULK(bq);
u32 cnt = rxq->desc_count;
wake = xsk_uses_need_wakeup(rxq->pool);
if (wake)
xsk_clear_rx_need_wakeup(rxq->pool);
gen = idpf_queue_has(GEN_CHK, rxq);
libeth_xsk_tx_init_bulk(&bq, rxq->xdp_prog, rxq->xdp_rxq.dev,
rxq->xdpsqs, rxq->num_xdp_txq);
xdp = rxq->xsk;
while (likely(rs.packets < budget)) {
const struct virtchnl2_rx_flex_desc_adv_nic_3 *rx_desc;
struct idpf_xdp_rx_desc desc __uninitialized;
struct idpf_buf_queue *bufq;
u32 bufq_id, buf_id;
rx_desc = &rxq->rx[ntc].flex_adv_nic_3_wb;
idpf_xdp_get_qw0(&desc, rx_desc);
if (idpf_xdp_rx_gen(&desc) != gen)
break;
dma_rmb();
bufq_id = idpf_xdp_rx_bufq(&desc);
bufq = set.bufqs[bufq_id].q;
if (!bufq) {
bufq = &rxq->bufq_sets[bufq_id].bufq;
set.bufqs[bufq_id].q = bufq;
}
idpf_xdp_get_qw1(&desc, rx_desc);
buf_id = idpf_xdp_rx_buf(&desc);
set.bufqs[bufq_id].buf_id = buf_id;
set.bufqs[bufq_id].pending++;
xdp = libeth_xsk_process_buff(xdp, bufq->xsk_buf[buf_id],
idpf_xdp_rx_len(&desc));
if (unlikely(++ntc == cnt)) {
ntc = 0;
gen = !gen;
idpf_queue_change(GEN_CHK, rxq);
}
if (!idpf_xdp_rx_eop(&desc) || unlikely(!xdp))
continue;
fail = !idpf_xsk_run_pass(xdp, &bq, rxq->napi, &rs, rx_desc);
xdp = NULL;
if (fail)
break;
}
idpf_xsk_finalize_rx(&bq);
rxq->next_to_clean = ntc;
rxq->xsk = xdp;
fail |= !idpf_xskfq_refill_set(&set);
u64_stats_update_begin(&rxq->stats_sync);
u64_stats_add(&rxq->q_stats.packets, rs.packets);
u64_stats_add(&rxq->q_stats.bytes, rs.bytes);
u64_stats_update_end(&rxq->stats_sync);
if (!wake)
return unlikely(fail) ? budget : rs.packets;
if (unlikely(fail))
xsk_set_rx_need_wakeup(rxq->pool);
return rs.packets;
}
int idpf_xsk_pool_setup(struct idpf_vport *vport, struct netdev_bpf *bpf)
{
struct xsk_buff_pool *pool = bpf->xsk.pool;
u32 qid = bpf->xsk.queue_id;
bool restart;
int ret;
if (pool && !IS_ALIGNED(xsk_pool_get_rx_frame_size(pool),
LIBETH_RX_BUF_STRIDE)) {
NL_SET_ERR_MSG_FMT_MOD(bpf->extack,
"%s: HW doesn't support frames sizes not aligned to %u (qid %u: %u)",
netdev_name(vport->netdev),
LIBETH_RX_BUF_STRIDE, qid,
xsk_pool_get_rx_frame_size(pool));
return -EINVAL;
}
restart = idpf_xdp_enabled(vport) && netif_running(vport->netdev);
if (!restart)
goto pool;
ret = idpf_qp_switch(vport, qid, false);
if (ret) {
NL_SET_ERR_MSG_FMT_MOD(bpf->extack,
"%s: failed to disable queue pair %u: %pe",
netdev_name(vport->netdev), qid,
ERR_PTR(ret));
return ret;
}
pool:
ret = libeth_xsk_setup_pool(vport->netdev, qid, pool);
if (ret) {
NL_SET_ERR_MSG_FMT_MOD(bpf->extack,
"%s: failed to configure XSk pool for pair %u: %pe",
netdev_name(vport->netdev), qid,
ERR_PTR(ret));
return ret;
}
if (!restart)
return 0;
ret = idpf_qp_switch(vport, qid, true);
if (ret) {
NL_SET_ERR_MSG_FMT_MOD(bpf->extack,
"%s: failed to enable queue pair %u: %pe",
netdev_name(vport->netdev), qid,
ERR_PTR(ret));
goto err_dis;
}
return 0;
err_dis:
libeth_xsk_setup_pool(vport->netdev, qid, false);
return ret;
}
int idpf_xsk_wakeup(struct net_device *dev, u32 qid, u32 flags)
{
const struct idpf_netdev_priv *np = netdev_priv(dev);
const struct idpf_vport *vport = np->vport;
struct idpf_q_vector *q_vector;
if (unlikely(idpf_vport_ctrl_is_locked(dev)))
return -EBUSY;
if (unlikely(!vport->link_up))
return -ENETDOWN;
if (unlikely(!vport->num_xdp_txq))
return -ENXIO;
q_vector = idpf_find_rxq_vec(vport, qid);
if (unlikely(!q_vector->xsksq))
return -ENXIO;
libeth_xsk_wakeup(&q_vector->csd, qid);
return 0;
}
@@ -0,0 +1,33 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (C) 2025 Intel Corporation */
#ifndef _IDPF_XSK_H_
#define _IDPF_XSK_H_
#include <linux/types.h>
enum virtchnl2_queue_type;
struct idpf_buf_queue;
struct idpf_q_vector;
struct idpf_rx_queue;
struct idpf_tx_queue;
struct idpf_vport;
struct net_device;
struct netdev_bpf;
void idpf_xsk_setup_queue(const struct idpf_vport *vport, void *q,
enum virtchnl2_queue_type type);
void idpf_xsk_clear_queue(void *q, enum virtchnl2_queue_type type);
void idpf_xsk_init_wakeup(struct idpf_q_vector *qv);
int idpf_xskfq_init(struct idpf_buf_queue *bufq);
void idpf_xskfq_rel(struct idpf_buf_queue *bufq);
void idpf_xsksq_clean(struct idpf_tx_queue *xdpq);
int idpf_xskrq_poll(struct idpf_rx_queue *rxq, u32 budget);
bool idpf_xsk_xmit(struct idpf_tx_queue *xsksq);
int idpf_xsk_pool_setup(struct idpf_vport *vport, struct netdev_bpf *xdp);
int idpf_xsk_wakeup(struct net_device *dev, u32 qid, u32 flags);
#endif /* !_IDPF_XSK_H_ */