Merge branch '200GbE' of git://git.kernel.org/pub/scm/linux/kernel/git/tnguy/next-queue

Tony Nguyen says:

====================
Introduce iXD driver

Larysa Zaremba says:

This patch series adds the iXD driver, which supports the Intel(R)
Control Plane PCI Function on Intel E2100 and later IPUs and FNICs.
It facilitates a centralized control over multiple IDPF PFs/VFs/SFs
exposed by the same card. The reason for the separation is to be able
to offload the control plane to the host different from where the data
plane is running.

This is the first phase in the release of this driver where we implement the
initialization of the core PCI driver. Subsequent phases will implement
advanced features like usage of idpf ethernet aux device, link management,
NVM update via devlink, switchdev port representors, data and exception path,
flow rule programming, etc.

The first phase entails the following aspects:

1. Additional libie functionalities:
Patches 1-5 introduce additional common library API for drivers to
communicate with the control plane through mailbox communication.
A control queue is a hardware interface which is used by the driver
to interact with other subsystems (like firmware). The library APIs
allow the driver to setup and configure the control queues to send and
receive virtchnl messages. The library has an internal bookkeeping
(XN API) mechanism to keep track of the send messages. It supports both
synchronous as well as asynchronous way of handling the messages. The
library also handles the timeout internally for synchronous messages
using events. This reduces the driver's overhead in handling the timeout
error cases.

The current patch series supports only APIs that are needed for device
initialization. These include APIs in the libie_pci module:
* Allocating/freeing the DMA memory and mapping the MMIO regions for
  BAR0, read/write APIs for drivers to access the MMIO memory

and libie_cp module:
* Control queue initialization and configuration
* Transport initialization for bookkeeping
* Blocking and asynchronous mailbox transactions

Once the mailbox is initialized, the drivers can send and receive virtchnl
messages to/from the control plane.

The modules above are not supposed to be linked with the main libie library,
but do share the folder with it.

2. idpf:
Patches 6-11 refactor the idpf driver to use the libie APIs for control
queue configuration, virtchnl transaction, device initialization
and reset and adjust related code accordingly.

3. ixd:
Patches 12-15 add the ixd driver and implement multiple pieces of the
initialization flow as follows:
* Add the ability to load
* A reset is issued to ensure a clean device state, followed by
  initialization of the mailbox
* Device capabilities:
  As part of initialization, the driver has to determine what the device is
  capable of (ex. max queues, vports, etc). This information is obtained from
  the firmware and stored by the driver.
* Enable initial support for the devlink interface

* '200GbE' of git://git.kernel.org/pub/scm/linux/kernel/git/tnguy/next-queue:
  ixd: add devlink support
  ixd: add the core initialization
  ixd: add reset checks and initialize the mailbox
  ixd: add basic driver framework for Intel(R) Control Plane Function
  idpf: print a debug message and bail in case of non-event ctlq message
  idpf: make mbx_task queueing and cancelling more consistent
  idpf: refactor idpf to use libie control queues
  idpf: refactor idpf to use libie_pci APIs
  idpf: remove unused code for getting RSS info from device
  idpf: remove 'vport_params_reqd' field
  libie: add bookkeeping support for control queue messages
  libie: add control queue support
  libeth: allow to create fill queues without NAPI
  libie: add PCI device initialization helpers to libie
  virtchnl: move virtchnl and virtchnl2 headers to 'include/linux/net/intel'
====================

Link: https://patch.msgid.link/20260812212532.905873-1-anthony.l.nguyen@intel.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-08-17 18:01:55 -07:00
commit 768af21721
62 changed files with 4396 additions and 2936 deletions

View File

@ -35,6 +35,7 @@ Contents:
intel/idpf
intel/igb
intel/igbvf
intel/ixd
intel/ixgbe
intel/ixgbevf
intel/i40e

View File

@ -0,0 +1,39 @@
.. SPDX-License-Identifier: GPL-2.0+
==========================================================================
iXD Linux* Base Driver for the Intel(R) Control Plane Function
==========================================================================
Intel iXD Linux driver.
Copyright(C) 2025 Intel Corporation.
.. contents::
For questions related to hardware requirements, refer to the documentation
supplied with your Intel adapter. All hardware requirements listed apply to use
with Linux.
Identifying Your Adapter
========================
For information on how to identify your adapter, and for the latest Intel
network drivers, refer to the Intel Support website:
http://www.intel.com/support
Support
=======
For general information, go to the Intel support website at:
http://www.intel.com/support/
If an issue is identified with the released source code on a supported kernel
with a supported adapter, email the specific information related to the issue
to intel-wired-lan@lists.osuosl.org.
Trademarks
==========
Intel is a trademark or registered trademark of Intel Corporation or its
subsidiaries in the United States and/or other countries.
* Other names and brands may be claimed as the property of others.

View File

@ -87,6 +87,7 @@ parameters, info versions, and other features it supports.
ice
ionic
iosm
ixd
ixgbe
kvaser_pciefd
kvaser_usb

View File

@ -0,0 +1,30 @@
.. SPDX-License-Identifier: GPL-2.0
===================
ixd devlink support
===================
This document describes the devlink features implemented by the ``ixd``
device driver.
Info versions
=============
The ``ixd`` driver reports the following versions
.. list-table:: devlink info versions implemented
:widths: 5 5 5 90
* - Name
- Type
- Example
- Description
* - ``device.type``
- fixed
- MEV
- The hardware type for this device
* - ``fw.mgmt.api``
- running
- 2.0
- 2-digit version number (major.minor) of the communication channel
(virtchnl) used by the device.

View File

@ -13034,8 +13034,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/tnguy/next-queue.git
F: Documentation/networking/device_drivers/ethernet/intel/
F: drivers/net/ethernet/intel/
F: drivers/net/ethernet/intel/*/
F: include/linux/avf/virtchnl.h
F: include/linux/net/intel/*/
F: include/linux/net/intel/
INTEL ETHERNET PROTOCOL DRIVER FOR RDMA
M: Tatyana Nikolova <tatyana.e.nikolova@intel.com>

View File

@ -398,4 +398,6 @@ config IGC_LEDS
source "drivers/net/ethernet/intel/idpf/Kconfig"
source "drivers/net/ethernet/intel/ixd/Kconfig"
endif # NET_VENDOR_INTEL

View File

@ -19,3 +19,4 @@ obj-$(CONFIG_IAVF) += iavf/
obj-$(CONFIG_FM10K) += fm10k/
obj-$(CONFIG_ICE) += ice/
obj-$(CONFIG_IDPF) += idpf/
obj-$(CONFIG_IXD) += ixd/

View File

@ -8,8 +8,8 @@
#include <linux/pci.h>
#include <linux/ptp_clock_kernel.h>
#include <linux/types.h>
#include <linux/avf/virtchnl.h>
#include <linux/net/intel/i40e_client.h>
#include <linux/net/intel/virtchnl.h>
#include <net/devlink.h>
#include <net/pkt_cls.h>
#include <net/udp_tunnel.h>

View File

@ -1,10 +1,10 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2013 - 2021 Intel Corporation. */
#include <linux/avf/virtchnl.h>
#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/etherdevice.h>
#include <linux/net/intel/virtchnl.h>
#include <linux/pci.h>
#include "i40e_adminq_cmd.h"
#include "i40e_devids.h"

View File

@ -5,7 +5,7 @@
#define _I40E_PROTOTYPE_H_
#include <linux/ethtool.h>
#include <linux/avf/virtchnl.h>
#include <linux/net/intel/virtchnl.h>
#include "i40e_debug.h"
#include "i40e_type.h"

View File

@ -4,7 +4,7 @@
#ifndef _I40E_VIRTCHNL_PF_H_
#define _I40E_VIRTCHNL_PF_H_
#include <linux/avf/virtchnl.h>
#include <linux/net/intel/virtchnl.h>
#include <linux/netdevice.h>
#include "i40e_type.h"

View File

@ -27,6 +27,7 @@
#include <linux/etherdevice.h>
#include <linux/socket.h>
#include <linux/jiffies.h>
#include <linux/net/intel/virtchnl.h>
#include <net/ip6_checksum.h>
#include <net/pkt_cls.h>
#include <net/pkt_sched.h>
@ -37,7 +38,6 @@
#include <net/net_shaper.h>
#include "iavf_type.h"
#include <linux/avf/virtchnl.h>
#include "iavf_txrx.h"
#include "iavf_fdir.h"
#include "iavf_adv_rss.h"

View File

@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2013 - 2018 Intel Corporation. */
#include <linux/avf/virtchnl.h>
#include <linux/net/intel/virtchnl.h>
#include <linux/bitfield.h>
#include "iavf_type.h"
#include "iavf_adminq.h"

View File

@ -4,9 +4,10 @@
#ifndef _IAVF_PROTOTYPE_H_
#define _IAVF_PROTOTYPE_H_
#include <linux/net/intel/virtchnl.h>
#include "iavf_type.h"
#include "iavf_alloc.h"
#include <linux/avf/virtchnl.h>
/* Prototypes for shared code functions that are not in
* the standard function pointer structures. These are

View File

@ -4,9 +4,7 @@
#ifndef _IAVF_TYPES_H_
#define _IAVF_TYPES_H_
#include "iavf_types.h"
#include <linux/avf/virtchnl.h>
#include <linux/net/intel/virtchnl.h>
#include <linux/ptp_clock_kernel.h>
/* structure used to queue PTP commands for processing */

View File

@ -36,7 +36,7 @@
#include <linux/bpf.h>
#include <linux/btf.h>
#include <linux/auxiliary_bus.h>
#include <linux/avf/virtchnl.h>
#include <linux/net/intel/virtchnl.h>
#include <linux/cpu_rmap.h>
#include <linux/dim.h>
#include <linux/gnss.h>

View File

@ -5,13 +5,13 @@
#define _ICE_COMMON_H_
#include <linux/bitfield.h>
#include <linux/net/intel/virtchnl.h>
#include "ice.h"
#include "ice_type.h"
#include "ice_nvm.h"
#include "ice_flex_pipe.h"
#include "ice_parser.h"
#include <linux/avf/virtchnl.h>
#include "ice_switch.h"
#include "ice_fdir.h"

View File

@ -8,9 +8,9 @@
#include <linux/hashtable.h>
#include <linux/bitmap.h>
#include <linux/mutex.h>
#include <linux/net/intel/virtchnl.h>
#include <linux/pci.h>
#include <net/devlink.h>
#include <linux/avf/virtchnl.h>
#include "ice_type.h"
#include "ice_flow.h"
#include "virt/fdir.h"

View File

@ -7,7 +7,7 @@
#include <linux/types.h>
#include <linux/bitops.h>
#include <linux/if_ether.h>
#include <linux/avf/virtchnl.h>
#include <linux/net/intel/virtchnl.h>
#include "ice_vf_lib.h"
/* Restrict number of MAC Addr and VLAN that non-trusted VF can programmed */

View File

@ -6,6 +6,7 @@ config IDPF
depends on PCI_MSI
depends on PTP_1588_CLOCK_OPTIONAL
select DIMLIB
select LIBIE_CP
select LIBETH_XDP
help
This driver supports Intel(R) Infrastructure Data Path Function

View File

@ -6,8 +6,6 @@
obj-$(CONFIG_IDPF) += idpf.o
idpf-y := \
idpf_controlq.o \
idpf_controlq_setup.o \
idpf_dev.o \
idpf_ethtool.o \
idpf_idc.o \

View File

@ -23,10 +23,10 @@ struct idpf_rss_data;
#include <linux/net/intel/iidc_rdma.h>
#include <linux/net/intel/iidc_rdma_idpf.h>
#include <linux/net/intel/libie/controlq.h>
#include <linux/net/intel/virtchnl2.h>
#include "virtchnl2.h"
#include "idpf_txrx.h"
#include "idpf_controlq.h"
#define GETMAXVAL(num_bits) GENMASK((num_bits) - 1, 0)
@ -36,11 +36,10 @@ struct idpf_rss_data;
#define IDPF_NUM_FILTERS_PER_MSG 20
#define IDPF_NUM_DFLT_MBX_Q 2 /* includes both TX and RX */
#define IDPF_DFLT_MBX_Q_LEN 64
#define IDPF_DFLT_MBX_ID -1
/* maximum number of times to try before resetting mailbox */
#define IDPF_MB_MAX_ERR 20
#define IDPF_NUM_CHUNKS_PER_MSG(struct_sz, chunk_sz) \
((IDPF_CTLQ_MAX_BUF_LEN - (struct_sz)) / (chunk_sz))
((LIBIE_CTLQ_MAX_BUF_LEN - (struct_sz)) / (chunk_sz))
#define IDPF_WAIT_FOR_MARKER_TIMEO 500
#define IDPF_MAX_WAIT 500
@ -201,8 +200,8 @@ struct idpf_vport_max_q {
* @ptp_reg_init: PTP register initialization
*/
struct idpf_reg_ops {
void (*ctlq_reg_init)(struct idpf_adapter *adapter,
struct idpf_ctlq_create_info *cq);
void (*ctlq_reg_init)(struct libie_mmio_info *mmio,
struct libie_ctlq_create_info *cctlq_info);
int (*intr_reg_init)(struct idpf_vport *vport,
struct idpf_q_vec_rsrc *rsrc);
void (*mb_intr_reg_init)(struct idpf_adapter *adapter);
@ -605,8 +604,6 @@ struct idpf_vport_config {
DECLARE_BITMAP(flags, IDPF_VPORT_CONFIG_FLAGS_NBITS);
};
struct idpf_vc_xn_manager;
#define idpf_for_each_vport(adapter, iter) \
for (struct idpf_vport **__##iter = &(adapter)->vports[0], \
*iter = (adapter)->max_vports ? *__##iter : NULL; \
@ -624,7 +621,10 @@ struct idpf_vc_xn_manager;
* @state: Init state machine
* @flags: See enum idpf_flags
* @reset_reg: See struct idpf_reset_reg
* @hw: Device access data
* @ctlq_ctx: controlq context
* @asq: Send control queue info
* @arq: Receive control queue info
* @xnm: Xn transaction manager
* @num_avail_msix: Available number of MSIX vectors
* @num_msix_entries: Number of entries in MSIX table
* @msix_entries: MSIX table
@ -638,7 +638,6 @@ struct idpf_vc_xn_manager;
* @avail_queues: Device given queue limits
* @vports: Array to store vports created by the driver
* @netdevs: Associated Vport netdevs
* @vport_params_reqd: Vport params requested
* @vport_params_recvd: Vport params received
* @vport_ids: Array of device given vport identifiers
* @singleq_pt_lkup: Lookup table for singleq RX ptypes
@ -658,7 +657,6 @@ struct idpf_vc_xn_manager;
* @stats_task: Periodic statistics retrieval task
* @stats_wq: Workqueue for statistics task
* @caps: Negotiated capabilities with device
* @vcxn_mngr: Virtchnl transaction manager
* @dev_ops: See idpf_dev_ops
* @cdev_info: IDC core device info pointer
* @num_vfs: Number of allocated VFs through sysfs. PF does not directly talk
@ -682,7 +680,10 @@ struct idpf_adapter {
enum idpf_state state;
DECLARE_BITMAP(flags, IDPF_FLAGS_NBITS);
struct idpf_reset_reg reset_reg;
struct idpf_hw hw;
struct libie_ctlq_ctx ctlq_ctx;
struct libie_ctlq_info *asq;
struct libie_ctlq_info *arq;
struct libie_ctlq_xn_manager *xnm;
u16 num_avail_msix;
u16 num_msix_entries;
struct msix_entry *msix_entries;
@ -697,7 +698,6 @@ struct idpf_adapter {
struct idpf_avail_queue_info avail_queues;
struct idpf_vport **vports;
struct net_device **netdevs;
struct virtchnl2_create_vport **vport_params_reqd;
struct virtchnl2_create_vport **vport_params_recvd;
u32 *vport_ids;
@ -720,7 +720,6 @@ struct idpf_adapter {
struct delayed_work stats_task;
struct workqueue_struct *stats_wq;
struct virtchnl2_get_capabilities caps;
struct idpf_vc_xn_manager *vcxn_mngr;
struct idpf_dev_ops dev_ops;
struct iidc_rdma_core_dev_info *cdev_info;
@ -872,70 +871,6 @@ static inline u8 idpf_get_min_tx_pkt_len(struct idpf_adapter *adapter)
return pkt_len ? pkt_len : IDPF_TX_MIN_PKT_LEN;
}
/**
* idpf_get_mbx_reg_addr - Get BAR0 mailbox register address
* @adapter: private data struct
* @reg_offset: register offset value
*
* Return: BAR0 mailbox register address based on register offset.
*/
static inline void __iomem *idpf_get_mbx_reg_addr(struct idpf_adapter *adapter,
resource_size_t reg_offset)
{
return adapter->hw.mbx.vaddr + reg_offset;
}
/**
* idpf_get_rstat_reg_addr - Get BAR0 rstat register address
* @adapter: private data struct
* @reg_offset: register offset value
*
* Return: BAR0 rstat register address based on register offset.
*/
static inline void __iomem *idpf_get_rstat_reg_addr(struct idpf_adapter *adapter,
resource_size_t reg_offset)
{
reg_offset -= adapter->dev_ops.static_reg_info[1].start;
return adapter->hw.rstat.vaddr + reg_offset;
}
/**
* idpf_get_reg_addr - Get BAR0 register address
* @adapter: private data struct
* @reg_offset: register offset value
*
* Based on the register offset, return the actual BAR0 register address
*/
static inline void __iomem *idpf_get_reg_addr(struct idpf_adapter *adapter,
resource_size_t reg_offset)
{
struct idpf_hw *hw = &adapter->hw;
for (int i = 0; i < hw->num_lan_regs; i++) {
struct idpf_mmio_reg *region = &hw->lan_regs[i];
if (reg_offset >= region->addr_start &&
reg_offset < (region->addr_start + region->addr_len)) {
/* Convert the offset so that it is relative to the
* start of the region. Then add the base address of
* the region to get the final address.
*/
reg_offset -= region->addr_start;
return region->vaddr + reg_offset;
}
}
/* It's impossible to hit this case with offsets from the CP. But if we
* do for any other reason, the kernel will panic on that register
* access. Might as well do it here to make it clear what's happening.
*/
BUG();
return NULL;
}
/**
* idpf_is_reset_detected - check if we were reset at some point
* @adapter: driver specific private structure
@ -944,11 +879,12 @@ static inline void __iomem *idpf_get_reg_addr(struct idpf_adapter *adapter,
*/
static inline bool idpf_is_reset_detected(struct idpf_adapter *adapter)
{
if (!adapter->hw.arq)
struct libie_ctlq_info *arq = adapter->arq;
if (!arq)
return true;
return !(readl(idpf_get_mbx_reg_addr(adapter, adapter->hw.arq->reg.len)) &
adapter->hw.arq->reg.len_mask);
return !(readl(arq->reg.len) & arq->reg.len_mask);
}
/**
@ -1048,6 +984,7 @@ void idpf_vc_event_task(struct work_struct *work);
void idpf_dev_ops_init(struct idpf_adapter *adapter);
void idpf_vf_dev_ops_init(struct idpf_adapter *adapter);
int idpf_intr_req(struct idpf_adapter *adapter);
void idpf_mb_intr_rel_irq(struct idpf_adapter *adapter);
void idpf_intr_rel(struct idpf_adapter *adapter);
u16 idpf_get_max_tx_hdr_size(struct idpf_adapter *adapter);
int idpf_initiate_soft_reset(struct idpf_vport *vport,

View File

@ -1,621 +0,0 @@
// 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;
}

View File

@ -1,144 +0,0 @@
/* 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_ */

View File

@ -1,177 +0,0 @@
/* 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_ */

View File

@ -1,169 +0,0 @@
// 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;
}

View File

@ -10,45 +10,32 @@
/**
* idpf_ctlq_reg_init - initialize default mailbox registers
* @adapter: adapter structure
* @cq: pointer to the array of create control queues
* @mmio: struct that contains MMIO region info
* @cci: struct where the register offset pointer to be copied to
*/
static void idpf_ctlq_reg_init(struct idpf_adapter *adapter,
struct idpf_ctlq_create_info *cq)
static void idpf_ctlq_reg_init(struct libie_mmio_info *mmio,
struct libie_ctlq_create_info *cci)
{
resource_size_t mbx_start = adapter->dev_ops.static_reg_info[0].start;
int i;
struct libie_ctlq_reg *tx_reg = &cci[LIBIE_CTLQ_TYPE_TX].reg;
struct libie_ctlq_reg *rx_reg = &cci[LIBIE_CTLQ_TYPE_RX].reg;
for (i = 0; i < IDPF_NUM_DFLT_MBX_Q; i++) {
struct idpf_ctlq_create_info *ccq = cq + i;
tx_reg->head = libie_pci_get_mmio_addr(mmio, PF_FW_ATQH);
tx_reg->tail = libie_pci_get_mmio_addr(mmio, PF_FW_ATQT);
tx_reg->len = libie_pci_get_mmio_addr(mmio, PF_FW_ATQLEN);
tx_reg->addr_high = libie_pci_get_mmio_addr(mmio, PF_FW_ATQBAH);
tx_reg->addr_low = libie_pci_get_mmio_addr(mmio, PF_FW_ATQBAL);
tx_reg->len_mask = PF_FW_ATQLEN_ATQLEN_M;
tx_reg->len_ena_mask = PF_FW_ATQLEN_ATQENABLE_M;
tx_reg->head_mask = PF_FW_ATQH_ATQH_M;
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;
}
}
rx_reg->head = libie_pci_get_mmio_addr(mmio, PF_FW_ARQH);
rx_reg->tail = libie_pci_get_mmio_addr(mmio, PF_FW_ARQT);
rx_reg->len = libie_pci_get_mmio_addr(mmio, PF_FW_ARQLEN);
rx_reg->addr_high = libie_pci_get_mmio_addr(mmio, PF_FW_ARQBAH);
rx_reg->addr_low = libie_pci_get_mmio_addr(mmio, PF_FW_ARQBAL);
rx_reg->len_mask = PF_FW_ARQLEN_ARQLEN_M;
rx_reg->len_ena_mask = PF_FW_ARQLEN_ARQENABLE_M;
rx_reg->head_mask = PF_FW_ARQH_ARQH_M;
}
/**
@ -57,13 +44,14 @@ static void idpf_ctlq_reg_init(struct idpf_adapter *adapter,
*/
static void idpf_mb_intr_reg_init(struct idpf_adapter *adapter)
{
struct libie_mmio_info *mmio = &adapter->ctlq_ctx.mmio_info;
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 = libie_pci_get_mmio_addr(mmio, 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 = libie_pci_get_mmio_addr(mmio, PF_INT_DIR_OICR_ENA);
intr->icr_ena_ctlq_m = PF_INT_DIR_OICR_ENA_M;
}
@ -78,6 +66,7 @@ static int idpf_intr_reg_init(struct idpf_vport *vport,
struct idpf_adapter *adapter = vport->adapter;
u16 num_vecs = rsrc->num_q_vectors;
struct idpf_vec_regs *reg_vals;
struct libie_mmio_info *mmio;
int num_regs, i, err = 0;
u32 rx_itr, tx_itr, val;
u16 total_vecs;
@ -93,14 +82,17 @@ static int idpf_intr_reg_init(struct idpf_vport *vport,
goto free_reg_vals;
}
mmio = &adapter->ctlq_ctx.mmio_info;
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;
struct idpf_vec_regs *reg = &reg_vals[vec_id];
u32 spacing;
intr->dyn_ctl = idpf_get_reg_addr(adapter,
reg_vals[vec_id].dyn_ctl_reg);
intr->dyn_ctl = libie_pci_get_mmio_addr(mmio,
reg->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;
@ -110,22 +102,21 @@ static int idpf_intr_reg_init(struct idpf_vport *vport,
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,
spacing = IDPF_ITR_IDX_SPACING(reg->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);
reg->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);
reg->itrn_reg, spacing);
intr->rx_itr = libie_pci_get_mmio_addr(mmio, rx_itr);
intr->tx_itr = libie_pci_get_mmio_addr(mmio, 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);
rsrc->noirq_dyn_ctl =
libie_pci_get_mmio_addr(&adapter->ctlq_ctx.mmio_info, 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);
@ -143,7 +134,9 @@ static int idpf_intr_reg_init(struct idpf_vport *vport,
*/
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 =
libie_pci_get_mmio_addr(&adapter->ctlq_ctx.mmio_info,
PFGEN_RSTAT);
adapter->reset_reg.rstat_m = PFGEN_RSTAT_PFR_STATE_M;
}
@ -155,11 +148,11 @@ static void idpf_reset_reg_init(struct idpf_adapter *adapter)
static void idpf_trigger_reset(struct idpf_adapter *adapter,
enum idpf_flags __always_unused trig_cause)
{
u32 reset_reg;
void __iomem *addr;
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));
addr = libie_pci_get_mmio_addr(&adapter->ctlq_ctx.mmio_info,
PFGEN_CTRL);
writel(readl(addr) | PFGEN_CTRL_PFSWR, addr);
}
/**

View File

@ -225,7 +225,7 @@ static int idpf_add_flow_steer(struct net_device *netdev,
spin_unlock_bh(&vport_config->flow_steer_list_lock);
if (err)
goto out;
goto out_free_fltr;
rule->vport_id = cpu_to_le32(vport->vport_id);
rule->count = cpu_to_le32(1);
@ -252,17 +252,15 @@ static int idpf_add_flow_steer(struct net_device *netdev,
break;
default:
err = -EINVAL;
goto out;
goto out_free_fltr;
}
err = idpf_add_del_fsteer_filters(vport->adapter, rule,
VIRTCHNL2_OP_ADD_FLOW_RULE);
if (err)
goto out;
if (info->status != cpu_to_le32(VIRTCHNL2_FLOW_RULE_SUCCESS)) {
err = -EIO;
goto out;
if (err) {
/* virtchnl2 rule is already consumed */
kfree(fltr);
return err;
}
/* Save a copy of the user's flow spec so ethtool can later retrieve it */
@ -274,9 +272,10 @@ static int idpf_add_flow_steer(struct net_device *netdev,
user_config->num_fsteer_fltrs++;
spin_unlock_bh(&vport_config->flow_steer_list_lock);
goto out_free_rule;
out:
return 0;
out_free_fltr:
kfree(fltr);
out_free_rule:
kfree(rule);
@ -319,12 +318,7 @@ static int idpf_del_flow_steer(struct net_device *netdev,
err = idpf_add_del_fsteer_filters(vport->adapter, rule,
VIRTCHNL2_OP_DEL_FLOW_RULE);
if (err)
goto out;
if (info->status != cpu_to_le32(VIRTCHNL2_FLOW_RULE_SUCCESS)) {
err = -EIO;
goto out;
}
return err;
spin_lock_bh(&vport_config->flow_steer_list_lock);
list_for_each_entry_safe(f, iter,
@ -340,8 +334,6 @@ static int idpf_del_flow_steer(struct net_device *netdev,
out_unlock:
spin_unlock_bh(&vport_config->flow_steer_list_lock);
out:
kfree(rule);
return err;
}

View File

@ -416,9 +416,12 @@ idpf_idc_init_msix_data(struct idpf_adapter *adapter)
int idpf_idc_init_aux_core_dev(struct idpf_adapter *adapter,
enum iidc_function_type ftype)
{
struct libie_mmio_info *mmio = &adapter->ctlq_ctx.mmio_info;
struct iidc_rdma_core_dev_info *cdev_info;
struct iidc_rdma_priv_dev_info *privd;
int err, i;
struct libie_pci_mmio_region *mr;
size_t num_mem_regions;
int err, i = 0;
adapter->cdev_info = kzalloc_obj(*cdev_info);
if (!adapter->cdev_info)
@ -436,22 +439,31 @@ int idpf_idc_init_aux_core_dev(struct idpf_adapter *adapter,
cdev_info->rdma_protocol = IIDC_RDMA_PROTOCOL_ROCEV2;
privd->ftype = ftype;
num_mem_regions = list_count_nodes(&mmio->mmio_list);
if (num_mem_regions <= IDPF_MMIO_REG_NUM_STATIC) {
err = -EINVAL;
goto err_plug_aux_dev;
}
num_mem_regions -= IDPF_MMIO_REG_NUM_STATIC;
privd->mapped_mem_regions =
kzalloc_objs(struct iidc_rdma_lan_mapped_mem_region,
adapter->hw.num_lan_regs);
num_mem_regions);
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);
privd->num_memory_regions = cpu_to_le16(num_mem_regions);
list_for_each_entry(mr, &mmio->mmio_list, list) {
if (!idpf_mmio_region_non_static(&adapter->ctlq_ctx.mmio_info,
mr))
continue;
privd->mapped_mem_regions[i].region_addr = mr->addr;
privd->mapped_mem_regions[i].size = cpu_to_le64(mr->size);
privd->mapped_mem_regions[i++].start_offset =
cpu_to_le64(mr->offset);
}
idpf_idc_init_msix_data(adapter);

View File

@ -68,9 +68,11 @@ static void idpf_deinit_vector_stack(struct idpf_adapter *adapter)
* This will also disable interrupt mode and queue up mailbox task. Mailbox
* task will reschedule itself if not in interrupt mode.
*/
static void idpf_mb_intr_rel_irq(struct idpf_adapter *adapter)
void idpf_mb_intr_rel_irq(struct idpf_adapter *adapter)
{
clear_bit(IDPF_MB_INTR_MODE, adapter->flags);
if (!test_and_clear_bit(IDPF_MB_INTR_MODE, adapter->flags))
return;
kfree(free_irq(adapter->msix_entries[0].vector, adapter));
queue_delayed_work(adapter->mbx_wq, &adapter->mbx_task, 0);
}
@ -1109,8 +1111,6 @@ static void idpf_vport_rel(struct idpf_vport *vport)
kfree(adapter->vport_params_recvd[idx]);
adapter->vport_params_recvd[idx] = NULL;
kfree(adapter->vport_params_reqd[idx]);
adapter->vport_params_reqd[idx] = NULL;
kfree(vport);
adapter->num_alloc_vports--;
@ -1373,6 +1373,7 @@ void idpf_statistics_task(struct work_struct *work)
*/
void idpf_mbx_task(struct work_struct *work)
{
struct libie_ctlq_xn_recv_params xn_params;
struct idpf_adapter *adapter;
adapter = container_of(work, struct idpf_adapter, mbx_task.work);
@ -1383,7 +1384,14 @@ void idpf_mbx_task(struct work_struct *work)
queue_delayed_work(adapter->mbx_wq, &adapter->mbx_task,
usecs_to_jiffies(300));
idpf_recv_mb_msg(adapter, adapter->hw.arq);
xn_params = (struct libie_ctlq_xn_recv_params) {
.xnm = adapter->xnm,
.ctlq = adapter->arq,
.ctlq_msg_handler = idpf_recv_event_msg,
.budget = LIBIE_CTLQ_MAX_XN_ENTRIES,
};
libie_ctlq_xn_recv(&xn_params);
}
/**
@ -1849,15 +1857,14 @@ void idpf_deinit_task(struct idpf_adapter *adapter)
/**
* idpf_check_reset_complete - check that reset is complete
* @hw: pointer to hw struct
* @adapter: adapter to check
* @reset_reg: struct with reset registers
*
* Returns 0 if device is ready to use, or -EBUSY if it's in reset.
**/
static int idpf_check_reset_complete(struct idpf_hw *hw,
static int idpf_check_reset_complete(struct idpf_adapter *adapter,
struct idpf_reset_reg *reset_reg)
{
struct idpf_adapter *adapter = hw->back;
int i;
for (i = 0; i < 2000; i++) {
@ -1920,7 +1927,7 @@ static void idpf_init_hard_reset(struct idpf_adapter *adapter)
}
/* Wait for reset to complete */
err = idpf_check_reset_complete(&adapter->hw, &adapter->reset_reg);
err = idpf_check_reset_complete(adapter, &adapter->reset_reg);
if (err) {
dev_err(dev, "The driver was unable to contact the device's firmware. Check that the FW is running. Driver state= 0x%x\n",
adapter->state);
@ -1934,14 +1941,11 @@ static void idpf_init_hard_reset(struct idpf_adapter *adapter)
goto unlock_mutex;
}
queue_delayed_work(adapter->mbx_wq, &adapter->mbx_task, 0);
/* Initialize the state machine, also allocate memory and request
* resources
*/
err = idpf_vc_core_init(adapter);
if (err) {
cancel_delayed_work_sync(&adapter->mbx_task);
idpf_deinit_dflt_mbx(adapter);
goto unlock_mutex;
}
@ -1987,7 +1991,8 @@ void idpf_vc_event_task(struct work_struct *work)
return;
func_reset:
idpf_vc_xn_shutdown(adapter->vcxn_mngr);
if (adapter->xnm)
libie_ctlq_xn_shutdown(adapter->xnm);
drv_load:
set_bit(IDPF_HR_RESET_IN_PROG, adapter->flags);
idpf_init_hard_reset(adapter);
@ -2570,44 +2575,6 @@ static int idpf_set_mac(struct net_device *netdev, void *p)
return err;
}
/**
* idpf_alloc_dma_mem - Allocate dma memory
* @hw: pointer to hw struct
* @mem: pointer to dma_mem struct
* @size: size of the memory to allocate
*/
void *idpf_alloc_dma_mem(struct idpf_hw *hw, struct idpf_dma_mem *mem, u64 size)
{
struct idpf_adapter *adapter = hw->back;
size_t sz = ALIGN(size, 4096);
/* The control queue resources are freed under a spinlock, contiguous
* pages will avoid IOMMU remapping and the use vmap (and vunmap in
* dma_free_*() path.
*/
mem->va = dma_alloc_attrs(&adapter->pdev->dev, sz, &mem->pa,
GFP_KERNEL, DMA_ATTR_FORCE_CONTIGUOUS);
mem->size = sz;
return mem->va;
}
/**
* idpf_free_dma_mem - Free the allocated dma memory
* @hw: pointer to hw struct
* @mem: pointer to dma_mem struct
*/
void idpf_free_dma_mem(struct idpf_hw *hw, struct idpf_dma_mem *mem)
{
struct idpf_adapter *adapter = hw->back;
dma_free_attrs(&adapter->pdev->dev, mem->size,
mem->va, mem->pa, DMA_ATTR_FORCE_CONTIGUOUS);
mem->size = 0;
mem->va = NULL;
mem->pa = 0;
}
static int idpf_hwtstamp_set(struct net_device *netdev,
struct kernel_hwtstamp_config *config,
struct netlink_ext_ack *extack)

View File

@ -15,6 +15,8 @@
MODULE_DESCRIPTION(DRV_SUMMARY);
MODULE_IMPORT_NS("LIBETH");
MODULE_IMPORT_NS("LIBIE_CP");
MODULE_IMPORT_NS("LIBIE_PCI");
MODULE_IMPORT_NS("LIBETH_XDP");
MODULE_LICENSE("GPL");
@ -56,8 +58,16 @@ static int idpf_get_device_type(struct pci_dev *pdev)
static int idpf_dev_init(struct idpf_adapter *adapter,
const struct pci_device_id *ent)
{
struct libie_mmio_info *mmio_info = &adapter->ctlq_ctx.mmio_info;
int ret;
ret = libie_pci_init_dev(adapter->pdev);
if (ret)
return ret;
mmio_info->pdev = adapter->pdev;
INIT_LIST_HEAD(&mmio_info->mmio_list);
if (ent->class == IDPF_CLASS_NETWORK_ETHERNET_PROGIF) {
ret = idpf_get_device_type(adapter->pdev);
switch (ret) {
@ -90,6 +100,15 @@ static int idpf_dev_init(struct idpf_adapter *adapter,
return 0;
}
/**
* idpf_decfg_device - deconfigure device and device specific resources
* @adapter: driver specific private structure
*/
static void idpf_decfg_device(struct idpf_adapter *adapter)
{
libie_pci_unmap_all_mmio_regions(&adapter->ctlq_ctx.mmio_info);
}
/**
* idpf_remove - Device removal routine
* @pdev: PCI device information struct
@ -151,14 +170,13 @@ static void idpf_remove(struct pci_dev *pdev)
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);
idpf_decfg_device(adapter);
pci_set_drvdata(pdev, NULL);
kfree(adapter);
}
@ -181,46 +199,44 @@ static void idpf_shutdown(struct pci_dev *pdev)
}
/**
* idpf_cfg_hw - Initialize HW struct
* @adapter: adapter to setup hw struct for
* idpf_cfg_device - configure device and device specific resources
* @adapter: driver specific private structure
*
* Returns 0 on success, negative on failure
* Return: %0 on success, -%errno on failure.
*/
static int idpf_cfg_hw(struct idpf_adapter *adapter)
static int idpf_cfg_device(struct idpf_adapter *adapter)
{
resource_size_t res_start, mbx_start, rstat_start;
struct libie_mmio_info *mmio_info = &adapter->ctlq_ctx.mmio_info;
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);
struct resource *region;
bool mapped;
int err;
/* 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");
region = &adapter->dev_ops.static_reg_info[0];
mapped = libie_pci_map_mmio_region(mmio_info, region->start,
resource_size(region));
if (!mapped) {
pci_err(pdev, "failed to map 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");
region = &adapter->dev_ops.static_reg_info[1];
mapped = libie_pci_map_mmio_region(mmio_info, region->start,
resource_size(region));
if (!mapped) {
pci_err(pdev, "failed to map BAR0 rstat region\n");
libie_pci_unmap_all_mmio_regions(mmio_info);
return -ENOMEM;
}
hw->rstat.addr_start = adapter->dev_ops.static_reg_info[1].start;
hw->rstat.addr_len = len;
hw->back = adapter;
err = pci_enable_ptm(pdev);
if (err)
pci_dbg(pdev, "PCIe PTM is not supported by PCIe bus/controller\n");
pci_set_drvdata(pdev, adapter);
return 0;
}
@ -246,32 +262,21 @@ static int idpf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
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));
err = idpf_dev_init(adapter, ent);
if (err) {
pci_err(pdev, "pcim_request_region failed %pe\n", ERR_PTR(err));
dev_err(&pdev->dev, "Failed to initialize device (ID 0x%x): %d\n",
ent->device, 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));
err = idpf_cfg_device(adapter);
if (err) {
pci_err(pdev, "DMA configuration failed: %pe\n", ERR_PTR(err));
pci_err(pdev, "Failed to configure device specific resources: %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),
@ -279,7 +284,7 @@ static int idpf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (!adapter->init_wq) {
dev_err(dev, "Failed to allocate init workqueue\n");
err = -ENOMEM;
goto err_free;
goto err_init_wq;
}
adapter->serv_wq = alloc_workqueue("%s-%s-service",
@ -324,20 +329,6 @@ static int idpf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
/* 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);
@ -356,8 +347,6 @@ static int idpf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
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:
@ -366,6 +355,8 @@ static int idpf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
destroy_workqueue(adapter->serv_wq);
err_serv_wq_alloc:
destroy_workqueue(adapter->init_wq);
err_init_wq:
idpf_decfg_device(adapter);
err_free:
kfree(adapter);
return err;

View File

@ -1,20 +0,0 @@
/* 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_ */

View File

@ -4676,11 +4676,11 @@ int idpf_config_rss(struct idpf_vport *vport, struct idpf_rss_data *rss_data)
u32 vport_id = vport->vport_id;
int err;
err = idpf_send_get_set_rss_key_msg(adapter, rss_data, vport_id, false);
err = idpf_send_set_rss_key_msg(adapter, rss_data, vport_id);
if (err)
return err;
return idpf_send_get_set_rss_lut_msg(adapter, rss_data, vport_id, false);
return idpf_send_set_rss_lut_msg(adapter, rss_data, vport_id);
}
/**

View File

@ -5,6 +5,7 @@
#define _IDPF_TXRX_H_
#include <linux/dim.h>
#include <linux/net/intel/virtchnl2_lan_desc.h>
#include <net/libeth/cache.h>
#include <net/libeth/types.h>
@ -13,7 +14,6 @@
#include <net/xdp.h>
#include "idpf_lan_txrx.h"
#include "virtchnl2_lan_desc.h"
#define IDPF_LARGE_MAX_Q 256
#define IDPF_MAX_Q 16
@ -236,7 +236,7 @@ enum idpf_tx_ctx_desc_eipt_offload {
(sizeof(u16) * IDPF_RX_MAX_PTYPE_PROTO_IDS))
#define IDPF_RX_PTYPE_HDR_SZ sizeof(struct virtchnl2_get_ptype_info)
#define IDPF_RX_MAX_PTYPES_PER_BUF \
DIV_ROUND_DOWN_ULL((IDPF_CTLQ_MAX_BUF_LEN - IDPF_RX_PTYPE_HDR_SZ), \
DIV_ROUND_DOWN_ULL(LIBIE_CTLQ_MAX_BUF_LEN - IDPF_RX_PTYPE_HDR_SZ, \
IDPF_RX_MAX_PTYPE_SZ)
#define IDPF_GET_PTYPE_SIZE(p) struct_size((p), proto_id, (p)->proto_id_count)

View File

@ -9,45 +9,32 @@
/**
* idpf_vf_ctlq_reg_init - initialize default mailbox registers
* @adapter: adapter structure
* @cq: pointer to the array of create control queues
* @mmio: struct that contains MMIO region info
* @cci: struct where the register offset pointer to be copied to
*/
static void idpf_vf_ctlq_reg_init(struct idpf_adapter *adapter,
struct idpf_ctlq_create_info *cq)
static void idpf_vf_ctlq_reg_init(struct libie_mmio_info *mmio,
struct libie_ctlq_create_info *cci)
{
resource_size_t mbx_start = adapter->dev_ops.static_reg_info[0].start;
int i;
struct libie_ctlq_reg *tx_reg = &cci[LIBIE_CTLQ_TYPE_TX].reg;
struct libie_ctlq_reg *rx_reg = &cci[LIBIE_CTLQ_TYPE_RX].reg;
for (i = 0; i < IDPF_NUM_DFLT_MBX_Q; i++) {
struct idpf_ctlq_create_info *ccq = cq + i;
tx_reg->head = libie_pci_get_mmio_addr(mmio, VF_ATQH);
tx_reg->tail = libie_pci_get_mmio_addr(mmio, VF_ATQT);
tx_reg->len = libie_pci_get_mmio_addr(mmio, VF_ATQLEN);
tx_reg->addr_high = libie_pci_get_mmio_addr(mmio, VF_ATQBAH);
tx_reg->addr_low = libie_pci_get_mmio_addr(mmio, VF_ATQBAL);
tx_reg->len_mask = VF_ATQLEN_ATQLEN_M;
tx_reg->len_ena_mask = VF_ATQLEN_ATQENABLE_M;
tx_reg->head_mask = VF_ATQH_ATQH_M;
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;
}
}
rx_reg->head = libie_pci_get_mmio_addr(mmio, VF_ARQH);
rx_reg->tail = libie_pci_get_mmio_addr(mmio, VF_ARQT);
rx_reg->len = libie_pci_get_mmio_addr(mmio, VF_ARQLEN);
rx_reg->addr_high = libie_pci_get_mmio_addr(mmio, VF_ARQBAH);
rx_reg->addr_low = libie_pci_get_mmio_addr(mmio, VF_ARQBAL);
rx_reg->len_mask = VF_ARQLEN_ARQLEN_M;
rx_reg->len_ena_mask = VF_ARQLEN_ARQENABLE_M;
rx_reg->head_mask = VF_ARQH_ARQH_M;
}
/**
@ -56,13 +43,14 @@ static void idpf_vf_ctlq_reg_init(struct idpf_adapter *adapter,
*/
static void idpf_vf_mb_intr_reg_init(struct idpf_adapter *adapter)
{
struct libie_mmio_info *mmio = &adapter->ctlq_ctx.mmio_info;
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 = libie_pci_get_mmio_addr(mmio, 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 = libie_pci_get_mmio_addr(mmio, VF_INT_ICR0_ENA1);
intr->icr_ena_ctlq_m = VF_INT_ICR0_ENA1_ADMINQ_M;
}
@ -77,6 +65,7 @@ static int idpf_vf_intr_reg_init(struct idpf_vport *vport,
struct idpf_adapter *adapter = vport->adapter;
u16 num_vecs = rsrc->num_q_vectors;
struct idpf_vec_regs *reg_vals;
struct libie_mmio_info *mmio;
int num_regs, i, err = 0;
u32 rx_itr, tx_itr, val;
u16 total_vecs;
@ -92,14 +81,17 @@ static int idpf_vf_intr_reg_init(struct idpf_vport *vport,
goto free_reg_vals;
}
mmio = &adapter->ctlq_ctx.mmio_info;
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;
struct idpf_vec_regs *reg = &reg_vals[vec_id];
u32 spacing;
intr->dyn_ctl = idpf_get_reg_addr(adapter,
reg_vals[vec_id].dyn_ctl_reg);
intr->dyn_ctl = libie_pci_get_mmio_addr(mmio,
reg->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;
@ -109,22 +101,21 @@ static int idpf_vf_intr_reg_init(struct idpf_vport *vport,
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,
spacing = IDPF_ITR_IDX_SPACING(reg->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);
reg->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);
reg->itrn_reg, spacing);
intr->rx_itr = libie_pci_get_mmio_addr(mmio, rx_itr);
intr->tx_itr = libie_pci_get_mmio_addr(mmio, 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);
rsrc->noirq_dyn_ctl =
libie_pci_get_mmio_addr(&adapter->ctlq_ctx.mmio_info, 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);
@ -142,7 +133,9 @@ static int idpf_vf_intr_reg_init(struct idpf_vport *vport,
*/
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 =
libie_pci_get_mmio_addr(&adapter->ctlq_ctx.mmio_info,
VFGEN_RSTAT);
adapter->reset_reg.rstat_m = VFGEN_RSTAT_VFR_STATE_M;
}
@ -157,8 +150,7 @@ static void idpf_vf_trigger_reset(struct idpf_adapter *adapter,
/* 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_send_vf_reset_msg(adapter);
}
/**

File diff suppressed because it is too large Load Diff

View File

@ -4,89 +4,9 @@
#ifndef _IDPF_VIRTCHNL_H_
#define _IDPF_VIRTCHNL_H_
#include "virtchnl2.h"
#include <linux/net/intel/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;
@ -96,8 +16,6 @@ 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);
@ -124,9 +42,35 @@ 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);
void idpf_recv_event_msg(struct libie_ctlq_ctx *ctx,
struct libie_ctlq_msg *ctlq_msg);
int idpf_send_mb_msg(struct idpf_adapter *adapter,
struct libie_ctlq_xn_send_params *xn_params,
void *send_buf, size_t send_buf_size);
int idpf_send_mb_msg_kfree(struct idpf_adapter *adapter,
struct libie_ctlq_xn_send_params *xn_params,
void *send_buf, size_t send_buf_size);
void idpf_send_vf_reset_msg(struct idpf_adapter *adapter);
bool idpf_mmio_region_non_static(struct libie_mmio_info *mmio_info,
struct libie_pci_mmio_region *reg);
/**
* idpf_send_mb_msg_stack - send a mailbox message from an on-stack buffer
* @adapter: driver specific private structure
* @xn_params: Xn send parameters to fill
* @ptr: pointer to the on-stack message object to send
*
* Send size is deduced based on the pointer type.
*
* Return: %0 on success, -%errno on failure.
*/
#define idpf_send_mb_msg_stack(adapter, xn_params, ptr) \
({ \
typeof(ptr) __ptr = (ptr); \
\
static_assert(sizeof(*__ptr) <= LIBIE_CP_TX_COPYBREAK); \
idpf_send_mb_msg(adapter, xn_params, __ptr, sizeof(*__ptr)); \
})
struct idpf_queue_ptr {
enum virtchnl2_queue_type type;
@ -208,13 +152,10 @@ int idpf_send_ena_dis_loopback_msg(struct idpf_adapter *adapter, u32 vport_id,
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_send_set_rss_key_msg(struct idpf_adapter *adapter,
struct idpf_rss_data *rss_data, u32 vport_id);
int idpf_send_set_rss_lut_msg(struct idpf_adapter *adapter,
struct idpf_rss_data *rss_data, u32 vport_id);
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);

View File

@ -15,7 +15,6 @@
*/
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 |
@ -24,33 +23,33 @@ int idpf_ptp_get_caps(struct idpf_adapter *adapter)
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),
struct libie_ctlq_xn_send_params xn_params = {
.chnl_opcode = VIRTCHNL2_OP_PTP_GET_CAPS,
.timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC,
};
struct virtchnl2_ptp_cross_time_reg_offsets cross_tstamp_offsets;
struct libie_mmio_info *mmio = &adapter->ctlq_ctx.mmio_info;
struct virtchnl2_ptp_clk_adj_reg_offsets clk_adj_offsets;
struct virtchnl2_ptp_clk_reg_offsets clock_offsets;
struct virtchnl2_ptp_get_caps *recv_ptp_caps_msg;
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;
size_t reply_sz;
int err;
recv_ptp_caps_msg = kzalloc_obj(struct virtchnl2_ptp_get_caps);
if (!recv_ptp_caps_msg)
return -ENOMEM;
err = idpf_send_mb_msg_stack(adapter, &xn_params, &send_ptp_caps_msg);
if (err)
return err;
xn_params.recv_buf.iov_base = recv_ptp_caps_msg;
xn_params.recv_buf.iov_len = sizeof(*recv_ptp_caps_msg);
reply_sz = xn_params.recv_mem.iov_len;
if (reply_sz != sizeof(*recv_ptp_caps_msg)) {
err = -EIO;
goto free_resp;
}
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;
recv_ptp_caps_msg = xn_params.recv_mem.iov_base;
ptp->caps = le32_to_cpu(recv_ptp_caps_msg->caps);
ptp->base_incval = le64_to_cpu(recv_ptp_caps_msg->base_incval);
@ -76,19 +75,20 @@ int idpf_ptp_get_caps(struct idpf_adapter *adapter)
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);
ptp->dev_clk_regs.dev_clk_ns_l =
libie_pci_get_mmio_addr(mmio, 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);
ptp->dev_clk_regs.dev_clk_ns_h =
libie_pci_get_mmio_addr(mmio, 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);
ptp->dev_clk_regs.phy_clk_ns_l =
libie_pci_get_mmio_addr(mmio, 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);
ptp->dev_clk_regs.phy_clk_ns_h =
libie_pci_get_mmio_addr(mmio, 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);
ptp->dev_clk_regs.cmd_sync =
libie_pci_get_mmio_addr(mmio, temp_offset);
cross_tstamp:
access_type = ptp->get_cross_tstamp_access;
@ -98,48 +98,54 @@ int idpf_ptp_get_caps(struct idpf_adapter *adapter)
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);
ptp->dev_clk_regs.sys_time_ns_l =
libie_pci_get_mmio_addr(mmio, 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);
ptp->dev_clk_regs.sys_time_ns_h =
libie_pci_get_mmio_addr(mmio, 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);
ptp->dev_clk_regs.cmd_sync =
libie_pci_get_mmio_addr(mmio, temp_offset);
discipline_clock:
access_type = ptp->adj_dev_clk_time_access;
if (access_type != IDPF_PTP_DIRECT)
return 0;
goto free_resp;
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);
ptp->dev_clk_regs.cmd = libie_pci_get_mmio_addr(mmio, 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);
ptp->dev_clk_regs.incval_l = libie_pci_get_mmio_addr(mmio, 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);
ptp->dev_clk_regs.incval_h = libie_pci_get_mmio_addr(mmio, 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);
ptp->dev_clk_regs.shadj_l = libie_pci_get_mmio_addr(mmio, 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);
ptp->dev_clk_regs.shadj_h = libie_pci_get_mmio_addr(mmio, 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);
ptp->dev_clk_regs.phy_cmd =
libie_pci_get_mmio_addr(mmio, 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);
ptp->dev_clk_regs.phy_incval_l =
libie_pci_get_mmio_addr(mmio, 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);
ptp->dev_clk_regs.phy_incval_h =
libie_pci_get_mmio_addr(mmio, 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);
ptp->dev_clk_regs.phy_shadj_l =
libie_pci_get_mmio_addr(mmio, 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);
ptp->dev_clk_regs.phy_shadj_h =
libie_pci_get_mmio_addr(mmio, temp_offset);
return 0;
free_resp:
libie_ctlq_release_rx_buf(&xn_params.recv_mem);
return err;
}
/**
@ -154,28 +160,34 @@ int idpf_ptp_get_caps(struct idpf_adapter *adapter)
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_resp;
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),
struct libie_ctlq_xn_send_params xn_params = {
.chnl_opcode = VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME,
.timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC,
};
int reply_sz;
size_t reply_sz;
u64 dev_time;
int err;
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;
err = idpf_send_mb_msg_stack(adapter, &xn_params,
&get_dev_clk_time_msg);
if (err)
return err;
dev_time = le64_to_cpu(get_dev_clk_time_msg.dev_time_ns);
reply_sz = xn_params.recv_mem.iov_len;
if (reply_sz != sizeof(*get_dev_clk_time_resp)) {
err = -EIO;
goto free_resp;
}
get_dev_clk_time_resp = xn_params.recv_mem.iov_base;
dev_time = le64_to_cpu(get_dev_clk_time_resp->dev_time_ns);
dev_clk_time->dev_clk_time_ns = dev_time;
return 0;
free_resp:
libie_ctlq_release_rx_buf(&xn_params.recv_mem);
return err;
}
/**
@ -191,27 +203,29 @@ int idpf_ptp_get_dev_clk_time(struct idpf_adapter *adapter,
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),
struct virtchnl2_ptp_get_cross_time cross_time_send, *cross_time_recv;
struct libie_ctlq_xn_send_params xn_params = {
.chnl_opcode = VIRTCHNL2_OP_PTP_GET_CROSS_TIME,
.timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC,
};
int reply_sz;
int err = 0;
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;
err = idpf_send_mb_msg_stack(adapter, &xn_params, &cross_time_send);
if (err)
return err;
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);
if (xn_params.recv_mem.iov_len != sizeof(*cross_time_recv)) {
err = -EIO;
goto free_resp;
}
return 0;
cross_time_recv = xn_params.recv_mem.iov_base;
cross_time->dev_clk_time_ns = le64_to_cpu(cross_time_recv->dev_time_ns);
cross_time->sys_time_ns = le64_to_cpu(cross_time_recv->sys_time_ns);
free_resp:
libie_ctlq_release_rx_buf(&xn_params.recv_mem);
return err;
}
/**
@ -228,23 +242,18 @@ 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),
struct libie_ctlq_xn_send_params xn_params = {
.chnl_opcode = VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME,
.timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC,
};
int reply_sz;
int err;
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;
err = idpf_send_mb_msg_stack(adapter, &xn_params,
&set_dev_clk_time_msg);
if (!err)
libie_ctlq_release_rx_buf(&xn_params.recv_mem);
return 0;
return err;
}
/**
@ -261,23 +270,18 @@ 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),
struct libie_ctlq_xn_send_params xn_params = {
.chnl_opcode = VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME,
.timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC,
};
int reply_sz;
int err;
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;
err = idpf_send_mb_msg_stack(adapter, &xn_params,
&adj_dev_clk_time_msg);
if (!err)
libie_ctlq_release_rx_buf(&xn_params.recv_mem);
return 0;
return err;
}
/**
@ -295,23 +299,18 @@ 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),
struct libie_ctlq_xn_send_params xn_params = {
.chnl_opcode = VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE,
.timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC,
};
int reply_sz;
int err;
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;
err = idpf_send_mb_msg_stack(adapter, &xn_params,
&adj_dev_clk_fine_msg);
if (!err)
libie_ctlq_release_rx_buf(&xn_params.recv_mem);
return 0;
return err;
}
/**
@ -330,18 +329,16 @@ int idpf_ptp_get_vport_tstamps_caps(struct idpf_vport *vport)
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,
struct libie_ctlq_xn_send_params xn_params = {
.chnl_opcode = VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS,
.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;
size_t reply_sz;
u16 num_latches;
int err = 0;
u32 size;
if (!ptp)
@ -353,19 +350,19 @@ int idpf_ptp_get_vport_tstamps_caps(struct idpf_vport *vport)
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;
err = idpf_send_mb_msg_stack(vport->adapter, &xn_params,
&send_tx_tstamp_caps);
if (err)
return err;
rcv_tx_tstamp_caps = xn_params.recv_mem.iov_base;
reply_sz = xn_params.recv_mem.iov_len;
if (reply_sz < sizeof(*rcv_tx_tstamp_caps)) {
err = -EIO;
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) {
@ -420,7 +417,7 @@ int idpf_ptp_get_vport_tstamps_caps(struct idpf_vport *vport)
}
vport->tx_tstamp_caps = tstamp_caps;
kfree(rcv_tx_tstamp_caps);
libie_ctlq_release_rx_buf(&xn_params.recv_mem);
return 0;
@ -433,7 +430,7 @@ int idpf_ptp_get_vport_tstamps_caps(struct idpf_vport *vport)
kfree(tstamp_caps);
get_tstamp_caps_out:
kfree(rcv_tx_tstamp_caps);
libie_ctlq_release_rx_buf(&xn_params.recv_mem);
return err;
}
@ -530,9 +527,9 @@ idpf_ptp_get_tstamp_value(struct idpf_vport *vport,
/**
* 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
* @ctx: adapter pointer
* @mem: address and size of the response
* @status: return value of the request
*
* 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
@ -540,22 +537,26 @@ idpf_ptp_get_tstamp_value(struct idpf_vport *vport,
*
* 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)
static void
idpf_ptp_get_tx_tstamp_async_handler(void *ctx, struct kvec *mem, int status)
{
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 idpf_adapter *adapter = ctx;
struct list_head *head;
u16 num_latches;
u32 vport_id;
int err = 0;
recv_tx_tstamp_msg = ctlq_msg->ctx.indirect.payload->va;
if (status)
return;
recv_tx_tstamp_msg = mem->iov_base;
if (mem->iov_len < sizeof(*recv_tx_tstamp_msg))
return;
vport_id = le32_to_cpu(recv_tx_tstamp_msg->vport_id);
idpf_for_each_vport(adapter, vport) {
@ -569,10 +570,13 @@ idpf_ptp_get_tx_tstamp_async_handler(struct idpf_adapter *adapter,
}
if (!tstamp_vport || !tstamp_vport->tx_tstamp_caps)
return -EINVAL;
return;
tx_tstamp_caps = tstamp_vport->tx_tstamp_caps;
num_latches = le16_to_cpu(recv_tx_tstamp_msg->num_latches);
if (mem->iov_len < struct_size(recv_tx_tstamp_msg, tstamp_latches,
num_latches))
return;
spin_lock_bh(&tx_tstamp_caps->latches_lock);
head = &tx_tstamp_caps->latches_in_use;
@ -583,13 +587,13 @@ idpf_ptp_get_tx_tstamp_async_handler(struct idpf_adapter *adapter,
if (!tstamp_latch.valid)
continue;
if (list_empty(head)) {
err = -ENOBUFS;
if (list_empty(head))
goto unlock;
}
list_for_each_entry_safe(tx_tstamp, tmp, head, list_member) {
if (tstamp_latch.index == tx_tstamp->idx) {
int err;
list_del(&tx_tstamp->list_member);
err = idpf_ptp_get_tstamp_value(tstamp_vport,
&tstamp_latch,
@ -604,8 +608,6 @@ idpf_ptp_get_tx_tstamp_async_handler(struct idpf_adapter *adapter,
unlock:
spin_unlock_bh(&tx_tstamp_caps->latches_lock);
return err;
}
/**
@ -621,15 +623,15 @@ 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,
struct libie_ctlq_xn_send_params xn_params = {
.chnl_opcode = 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,
.resp_cb = idpf_ptp_get_tx_tstamp_async_handler,
.send_ctx = vport->adapter,
};
struct idpf_ptp_tx_tstamp *ptp_tx_tstamp;
int reply_sz, size, msg_size;
struct list_head *head;
int size, msg_size;
bool state_upd;
u16 id = 0;
@ -662,11 +664,7 @@ int idpf_ptp_get_tx_tstamp(struct idpf_vport *vport)
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);
return idpf_send_mb_msg_kfree(vport->adapter, &xn_params,
send_tx_tstamp_msg, msg_size);
}

View File

@ -0,0 +1,14 @@
# SPDX-License-Identifier: GPL-2.0-only
# Copyright (C) 2025 Intel Corporation
config IXD
tristate "Intel(R) Control Plane Function Support"
depends on PCI_MSI
select LIBIE_CP
select LIBIE_PCI
select NET_DEVLINK
help
This driver supports Intel(R) Control Plane PCI Function
of Intel E2100 and later IPUs and FNICs.
It facilitates a centralized control over multiple IDPF PFs/VFs/SFs
exposed by the same card.

View File

@ -0,0 +1,13 @@
# SPDX-License-Identifier: GPL-2.0-only
# Copyright (C) 2025 Intel Corporation
# Intel(R) Control Plane Function Linux Driver
obj-$(CONFIG_IXD) += ixd.o
ixd-y := ixd_main.o
ixd-y += ixd_ctlq.o
ixd-y += ixd_dev.o
ixd-y += ixd_devlink.o
ixd-y += ixd_lib.o
ixd-y += ixd_virtchnl.o

View File

@ -0,0 +1,67 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (C) 2025 Intel Corporation */
#ifndef _IXD_H_
#define _IXD_H_
#include <linux/net/intel/libie/controlq.h>
#define IXD_INIT_TASK_DELAY_JIFFIES msecs_to_jiffies(500)
/**
* struct ixd_adapter - Data structure representing a CPF
* @cp_ctx: Control plane communication context
* @init_task: Delayed initialization after reset
* @init_task.init_work: Delayed initialization work
* @init_task.reset_retries: How many times to check, whether reset is completed
* @init_task.vc_retries: Number of retries to establish mailbox communication
* @init_task.success: init_work completion status
* @mbx_task: Control queue Rx handling
* @xnm: virtchnl transaction manager
* @asq: Send control queue info
* @arq: Receive control queue info
* @vc_ver: Negotiated virtchnl version
* @vc_ver.major: Negotiated major virtchnl version
* @vc_ver.minor: Negotiated minor virtchnl version
* @caps: Negotiated virtchnl capabilities
*/
struct ixd_adapter {
struct libie_ctlq_ctx cp_ctx;
struct {
struct delayed_work init_work;
u8 reset_retries;
u8 vc_retries;
bool success;
} init_task;
struct delayed_work mbx_task;
struct libie_ctlq_xn_manager *xnm;
struct libie_ctlq_info *asq;
struct libie_ctlq_info *arq;
struct {
u32 major;
u32 minor;
} vc_ver;
struct virtchnl2_get_capabilities caps;
};
/**
* ixd_to_dev - Get the corresponding device struct from an adapter
* @adapter: PCI device driver-specific private data
*
* Return: struct device corresponding to the given adapter
*/
static inline struct device *ixd_to_dev(struct ixd_adapter *adapter)
{
return &adapter->cp_ctx.mmio_info.pdev->dev;
}
void ixd_ctlq_reg_init(struct ixd_adapter *adapter,
struct libie_ctlq_reg *ctlq_reg_tx,
struct libie_ctlq_reg *ctlq_reg_rx);
void ixd_trigger_reset(struct ixd_adapter *adapter);
bool ixd_check_reset_complete(struct ixd_adapter *adapter);
void ixd_init_task(struct work_struct *work);
int ixd_init_dflt_mbx(struct ixd_adapter *adapter);
void ixd_deinit_dflt_mbx(struct ixd_adapter *adapter);
#endif /* _IXD_H_ */

View File

@ -0,0 +1,141 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2025 Intel Corporation */
#include "ixd.h"
#include "ixd_ctlq.h"
#include "ixd_virtchnl.h"
#define IXD_CTLQ_RX_TASK_DELAY_JIFFIES msecs_to_jiffies(300)
/**
* ixd_ctlq_clean_sq - Clean the send control queue after sending the message
* @adapter: The adapter that sent the messages
* @force: Clean regardless of send status
*
* Free the libie send resources after sending the message and handling
* the response.
*/
void ixd_ctlq_clean_sq(struct ixd_adapter *adapter, bool force)
{
libie_ctlq_xn_send_clean(adapter->asq, kfree, force);
}
/**
* ixd_ctlq_init_sparams - Initialize control queue send parameters
* @adapter: The adapter with initialized mailbox
* @sparams: Parameters to initialize
* @msg_buf: DMA-mappable pointer to the message being sent
* @msg_size: Message size
*/
static void ixd_ctlq_init_sparams(struct ixd_adapter *adapter,
struct libie_ctlq_xn_send_params *sparams,
void *msg_buf, size_t msg_size)
{
*sparams = (struct libie_ctlq_xn_send_params) {
.rel_tx_buf = kfree,
.xnm = adapter->xnm,
.ctlq = adapter->asq,
.timeout_ms = IXD_CTLQ_TIMEOUT,
.send_buf = (struct kvec) {
.iov_base = msg_buf,
.iov_len = msg_size,
},
};
}
/**
* ixd_ctlq_do_req - Perform a standard virtchnl request
* @adapter: The adapter with initialized mailbox
* @req: virtchnl request description
*
* Return: %0 if a message was sent and received a response
* that was successfully handled by the custom callback,
* negative error otherwise.
*/
int ixd_ctlq_do_req(struct ixd_adapter *adapter, const struct ixd_ctlq_req *req)
{
u8 onstack_send_buff[LIBIE_CP_TX_COPYBREAK] __aligned_largest = {};
struct libie_ctlq_xn_send_params send_params = {};
struct kvec *recv_mem;
void *send_buff;
int err;
send_buff = libie_cp_can_send_onstack(req->send_size) ?
&onstack_send_buff : kzalloc(req->send_size, GFP_KERNEL);
if (!send_buff)
return -ENOMEM;
ixd_ctlq_init_sparams(adapter, &send_params, send_buff,
req->send_size);
send_params.chnl_opcode = req->opcode;
if (req->send_buff_init)
req->send_buff_init(adapter, send_buff, req->ctx);
ixd_ctlq_clean_sq(adapter, false);
err = libie_ctlq_xn_send(&send_params);
if (err)
return err;
recv_mem = &send_params.recv_mem;
if (req->recv_process)
err = req->recv_process(adapter, recv_mem->iov_base,
recv_mem->iov_len, req->ctx);
libie_ctlq_release_rx_buf(recv_mem);
return err;
}
/**
* ixd_ctlq_handle_msg - Default control queue message handler
* @ctx: Control plane communication context
* @msg: Message received
*/
static void ixd_ctlq_handle_msg(struct libie_ctlq_ctx *ctx,
struct libie_ctlq_msg *msg)
{
struct ixd_adapter *adapter = pci_get_drvdata(ctx->mmio_info.pdev);
if (ixd_vc_can_handle_msg(msg))
ixd_vc_recv_event_msg(adapter, msg);
else
dev_dbg_ratelimited(ixd_to_dev(adapter),
"Received an unsupported opcode 0x%x from the CP\n",
msg->chnl_opcode);
libie_ctlq_release_rx_buf(&msg->recv_mem);
}
/**
* ixd_ctlq_recv_mb_msg - Receive a potential message over mailbox periodically
* @adapter: The adapter with initialized mailbox
*/
static void ixd_ctlq_recv_mb_msg(struct ixd_adapter *adapter)
{
struct libie_ctlq_xn_recv_params xn_params = {
.xnm = adapter->xnm,
.ctlq = adapter->arq,
.ctlq_msg_handler = ixd_ctlq_handle_msg,
.budget = LIBIE_CTLQ_MAX_XN_ENTRIES,
};
libie_ctlq_xn_recv(&xn_params);
}
/**
* ixd_ctlq_rx_task - Periodically check for mailbox responses and events
* @work: work handle
*/
void ixd_ctlq_rx_task(struct work_struct *work)
{
struct ixd_adapter *adapter;
adapter = container_of(work, struct ixd_adapter, mbx_task.work);
queue_delayed_work(system_dfl_wq, &adapter->mbx_task,
IXD_CTLQ_RX_TASK_DELAY_JIFFIES);
ixd_ctlq_recv_mb_msg(adapter);
}

View File

@ -0,0 +1,34 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (C) 2025 Intel Corporation */
#ifndef _IXD_CTLQ_H_
#define _IXD_CTLQ_H_
#include <linux/net/intel/virtchnl2.h>
#define IXD_CTLQ_TIMEOUT 2000
/**
* struct ixd_ctlq_req - Standard virtchnl request description
* @opcode: protocol opcode, only virtchnl2 is needed for now
* @send_size: required length of the send buffer
* @send_buff_init: function to initialize the allocated send buffer
* @recv_process: function to handle the CP response
* @ctx: additional context for callbacks
*/
struct ixd_ctlq_req {
enum virtchnl2_op opcode;
size_t send_size;
void (*send_buff_init)(struct ixd_adapter *adapter, void *send_buff,
void *ctx);
int (*recv_process)(struct ixd_adapter *adapter, void *recv_buff,
size_t recv_size, void *ctx);
void *ctx;
};
void ixd_ctlq_clean_sq(struct ixd_adapter *adapter, bool force);
int ixd_ctlq_do_req(struct ixd_adapter *adapter,
const struct ixd_ctlq_req *req);
void ixd_ctlq_rx_task(struct work_struct *work);
#endif /* _IXD_CTLQ_H_ */

View File

@ -0,0 +1,89 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2025 Intel Corporation */
#include "ixd.h"
#include "ixd_lan_regs.h"
/**
* ixd_ctlq_reg_init - Initialize default mailbox registers
* @adapter: PCI device driver-specific private data
* @ctlq_reg_tx: Transmit queue registers info to be filled
* @ctlq_reg_rx: Receive queue registers info to be filled
*/
void ixd_ctlq_reg_init(struct ixd_adapter *adapter,
struct libie_ctlq_reg *ctlq_reg_tx,
struct libie_ctlq_reg *ctlq_reg_rx)
{
struct libie_mmio_info *mmio_info = &adapter->cp_ctx.mmio_info;
*ctlq_reg_tx = (struct libie_ctlq_reg) {
.head = libie_pci_get_mmio_addr(mmio_info, PF_FW_ATQH),
.tail = libie_pci_get_mmio_addr(mmio_info, PF_FW_ATQT),
.len = libie_pci_get_mmio_addr(mmio_info, PF_FW_ATQLEN),
.addr_high = libie_pci_get_mmio_addr(mmio_info, PF_FW_ATQBAH),
.addr_low = libie_pci_get_mmio_addr(mmio_info, PF_FW_ATQBAL),
.len_mask = PF_FW_ATQLEN_ATQLEN_M,
.len_ena_mask = PF_FW_ATQLEN_ATQENABLE_M,
.head_mask = PF_FW_ATQH_ATQH_M,
};
*ctlq_reg_rx = (struct libie_ctlq_reg) {
.head = libie_pci_get_mmio_addr(mmio_info, PF_FW_ARQH),
.tail = libie_pci_get_mmio_addr(mmio_info, PF_FW_ARQT),
.len = libie_pci_get_mmio_addr(mmio_info, PF_FW_ARQLEN),
.addr_high = libie_pci_get_mmio_addr(mmio_info, PF_FW_ARQBAH),
.addr_low = libie_pci_get_mmio_addr(mmio_info, PF_FW_ARQBAL),
.len_mask = PF_FW_ARQLEN_ARQLEN_M,
.len_ena_mask = PF_FW_ARQLEN_ARQENABLE_M,
.head_mask = PF_FW_ARQH_ARQH_M,
};
}
static const struct ixd_reset_reg ixd_reset_reg = {
.rstat = PFGEN_RSTAT,
.rstat_m = PFGEN_RSTAT_PFR_STATE_M,
.rstat_ok_v = 0b01,
.rtrigger = PFGEN_CTRL,
.rtrigger_m = PFGEN_CTRL_PFSWR,
};
/**
* ixd_trigger_reset - Trigger PFR reset
* @adapter: the device with mapped reset register
*/
void ixd_trigger_reset(struct ixd_adapter *adapter)
{
void __iomem *addr;
u32 reg_val;
addr = libie_pci_get_mmio_addr(&adapter->cp_ctx.mmio_info,
ixd_reset_reg.rtrigger);
reg_val = readl(addr);
writel(reg_val | ixd_reset_reg.rtrigger_m, addr);
}
/**
* ixd_check_reset_complete - Check if the PFR reset is completed
* @adapter: CPF being reset
*
* Return: %true if the register read indicates reset has been finished,
* %false otherwise
*/
bool ixd_check_reset_complete(struct ixd_adapter *adapter)
{
u32 reg_val, reset_status;
void __iomem *addr;
addr = libie_pci_get_mmio_addr(&adapter->cp_ctx.mmio_info,
ixd_reset_reg.rstat);
reg_val = readl(addr);
reset_status = reg_val & ixd_reset_reg.rstat_m;
/* 0xFFFFFFFF might be read if the other side hasn't cleared
* the register for us yet.
*/
if (reg_val != GENMASK(31, 0) &&
reset_status == ixd_reset_reg.rstat_ok_v)
return true;
return false;
}

View File

@ -0,0 +1,99 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2025, Intel Corporation. */
#include "ixd.h"
#include "ixd_devlink.h"
#define IXD_DEVLINK_INFO_LEN 128
/**
* ixd_fill_dsn - Get the serial number for the ixd device
* @adapter: adapter to query
* @buf: storage buffer for the info request
*/
static void ixd_fill_dsn(struct ixd_adapter *adapter, char *buf)
{
u8 dsn[8];
/* Copy the DSN into an array in Big Endian format */
put_unaligned_be64(pci_get_dsn(adapter->cp_ctx.mmio_info.pdev), dsn);
snprintf(buf, IXD_DEVLINK_INFO_LEN, "%8phD", dsn);
}
/**
* ixd_fill_device_name - Get the name of the underlying hardware
* @adapter: adapter to query
* @buf: storage buffer for the info request
* @buf_size: size of the storage buffer
*/
static void ixd_fill_device_name(struct ixd_adapter *adapter, char *buf,
size_t buf_size)
{
if (adapter->caps.device_type == cpu_to_le32(VIRTCHNL2_MEV_DEVICE))
snprintf(buf, buf_size, "%s", "MEV");
else
snprintf(buf, buf_size, "%s", "UNKNOWN");
}
/**
* ixd_devlink_info_get - .info_get devlink handler
* @devlink: devlink instance structure
* @req: the devlink info request
* @extack: extended netdev ack structure
*
* Callback for the devlink .info_get operation. Reports information about the
* device.
*
* Return: zero on success or an error code on failure.
*/
static int ixd_devlink_info_get(struct devlink *devlink,
struct devlink_info_req *req,
struct netlink_ext_ack *extack)
{
struct ixd_adapter *adapter = devlink_priv(devlink);
char buf[IXD_DEVLINK_INFO_LEN];
int err;
ixd_fill_dsn(adapter, buf);
err = devlink_info_serial_number_put(req, buf);
if (err)
return err;
ixd_fill_device_name(adapter, buf, IXD_DEVLINK_INFO_LEN);
err = devlink_info_version_fixed_put(req, "device.type", buf);
if (err)
return err;
snprintf(buf, sizeof(buf), "%u.%u",
adapter->vc_ver.major, adapter->vc_ver.minor);
return devlink_info_version_running_put(req,
DEVLINK_INFO_VERSION_GENERIC_FW_MGMT_API,
buf);
}
static const struct devlink_ops ixd_devlink_ops = {
.info_get = ixd_devlink_info_get,
};
/**
* ixd_adapter_alloc - Allocate devlink and return adapter pointer
* @dev: the device to allocate for
*
* Allocate a devlink instance for this device and return the private area as
* the adapter structure.
*
* Return: adapter structure on success, NULL on failure
*/
struct ixd_adapter *ixd_adapter_alloc(struct device *dev)
{
struct devlink *devlink;
devlink = devlink_alloc(&ixd_devlink_ops, sizeof(struct ixd_adapter),
dev);
if (!devlink)
return NULL;
return devlink_priv(devlink);
}

View File

@ -0,0 +1,50 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2025, Intel Corporation. */
#ifndef _IXD_DEVLINK_H_
#define _IXD_DEVLINK_H_
#include <net/devlink.h>
#include "ixd.h"
struct ixd_adapter *ixd_adapter_alloc(struct device *dev);
/**
* ixd_devlink_free - teardown the devlink
* @adapter: the adapter structure to free
*
*/
static inline void ixd_devlink_free(struct ixd_adapter *adapter)
{
struct devlink *devlink = priv_to_devlink(adapter);
devlink_free(devlink);
}
/**
* ixd_devlink_unregister - Unregister devlink for this adapter.
* @adapter: the adapter structure to cleanup
*
* Init task must be completed or cancelled beforehand.
*/
static inline void ixd_devlink_unregister(struct ixd_adapter *adapter)
{
if (!adapter->init_task.success)
return;
devlink_unregister(priv_to_devlink(adapter));
}
/**
* ixd_devlink_register - Register devlink interface for this adapter
* @adapter: pointer to ixd adapter structure to be associated with devlink
*
* Register the devlink instance associated with this adapter
*/
static inline void ixd_devlink_register(struct ixd_adapter *adapter)
{
devlink_register(priv_to_devlink(adapter));
}
#endif /* _IXD_DEVLINK_H_ */

View File

@ -0,0 +1,68 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (C) 2025 Intel Corporation */
#ifndef _IXD_LAN_REGS_H_
#define _IXD_LAN_REGS_H_
/* Control Plane Function PCI ID */
#define IXD_DEV_ID_CPF 0x1efe
/* Control Queue (Mailbox) */
#define PF_FW_MBX_REG_LEN 4096
#define PF_FW_MBX 0x08400000
#define PF_FW_ARQBAL (PF_FW_MBX)
#define PF_FW_ARQBAH (PF_FW_MBX + 0x4)
#define PF_FW_ARQLEN (PF_FW_MBX + 0x8)
#define PF_FW_ARQLEN_ARQLEN_M GENMASK(12, 0)
#define PF_FW_ARQLEN_ARQENABLE_S 31
#define PF_FW_ARQLEN_ARQENABLE_M BIT(PF_FW_ARQLEN_ARQENABLE_S)
#define PF_FW_ARQH_ARQH_M GENMASK(12, 0)
#define PF_FW_ARQH (PF_FW_MBX + 0xC)
#define PF_FW_ARQT (PF_FW_MBX + 0x10)
#define PF_FW_ATQBAL (PF_FW_MBX + 0x14)
#define PF_FW_ATQBAH (PF_FW_MBX + 0x18)
#define PF_FW_ATQLEN (PF_FW_MBX + 0x1C)
#define PF_FW_ATQLEN_ATQLEN_M GENMASK(9, 0)
#define PF_FW_ATQLEN_ATQENABLE_S 31
#define PF_FW_ATQLEN_ATQENABLE_M BIT(PF_FW_ATQLEN_ATQENABLE_S)
#define PF_FW_ATQH_ATQH_M GENMASK(9, 0)
#define PF_FW_ATQH (PF_FW_MBX + 0x20)
#define PF_FW_ATQT (PF_FW_MBX + 0x24)
/* Reset registers */
#define PFGEN_RTRIG_REG_LEN 2048
#define PFGEN_RTRIG 0x08407000 /* Device resets */
#define PFGEN_RSTAT 0x08407008 /* PFR status */
#define PFGEN_RSTAT_PFR_STATE_M GENMASK(1, 0)
#define PFGEN_CTRL 0x0840700C /* PFR trigger */
#define PFGEN_CTRL_PFSWR BIT(0)
/**
* struct ixd_bar_region - BAR region description
* @offset: BAR region offset
* @size: BAR region size
*/
struct ixd_bar_region {
resource_size_t offset;
resource_size_t size;
};
/**
* struct ixd_reset_reg - structure for reset registers
* @rstat: offset of status in register
* @rstat_m: status mask
* @rstat_ok_v: value that indicates PFR completed status
* @rtrigger: offset of reset trigger in register
* @rtrigger_m: reset trigger mask
*/
struct ixd_reset_reg {
u32 rstat;
u32 rstat_m;
u32 rstat_ok_v;
u32 rtrigger;
u32 rtrigger_m;
};
#endif /* _IXD_LAN_REGS_H_ */

View File

@ -0,0 +1,173 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2025 Intel Corporation */
#include "ixd.h"
#include "ixd_ctlq.h"
#include "ixd_devlink.h"
#include "ixd_virtchnl.h"
#define IXD_DFLT_MBX_Q_LEN 64
/**
* ixd_init_ctlq_create_info - Initialize control queue info for creation
* @info: destination
* @type: type of the queue to create
* @ctlq_reg: register assigned to the control queue
*/
static void ixd_init_ctlq_create_info(struct libie_ctlq_create_info *info,
enum libie_ctlq_type type,
const struct libie_ctlq_reg *ctlq_reg)
{
*info = (struct libie_ctlq_create_info) {
.type = type,
.id = -1,
.reg = *ctlq_reg,
.len = IXD_DFLT_MBX_Q_LEN,
};
}
/**
* ixd_init_libie_xn_params - Initialize xn transaction manager creation info
* @params: destination
* @adapter: adapter info struct
* @ctlqs: list of the managed queues to create
* @num_queues: length of the queue list
*/
static void ixd_init_libie_xn_params(struct libie_ctlq_xn_init_params *params,
struct ixd_adapter *adapter,
struct libie_ctlq_create_info *ctlqs,
uint num_queues)
{
*params = (struct libie_ctlq_xn_init_params){
.cctlq_info = ctlqs,
.ctx = &adapter->cp_ctx,
.num_qs = num_queues,
};
}
/**
* ixd_adapter_fill_dflt_ctlqs - Find default control queues and store them
* @adapter: adapter info struct
*/
static void ixd_adapter_fill_dflt_ctlqs(struct ixd_adapter *adapter)
{
adapter->arq = libie_find_ctlq(&adapter->cp_ctx, LIBIE_CTLQ_TYPE_RX,
LIBIE_CTLQ_MBX_ID);
adapter->asq = libie_find_ctlq(&adapter->cp_ctx, LIBIE_CTLQ_TYPE_TX,
LIBIE_CTLQ_MBX_ID);
}
/**
* ixd_deinit_dflt_mbx - Deinitialize default mailbox
* @adapter: adapter info struct
*/
void ixd_deinit_dflt_mbx(struct ixd_adapter *adapter)
{
cancel_delayed_work_sync(&adapter->mbx_task);
if (adapter->xnm)
libie_ctlq_xn_shutdown(adapter->xnm);
if (adapter->asq)
ixd_ctlq_clean_sq(adapter, true);
if (adapter->xnm)
libie_ctlq_xn_deinit(adapter->xnm, &adapter->cp_ctx);
adapter->arq = NULL;
adapter->asq = NULL;
adapter->xnm = NULL;
}
/**
* ixd_init_dflt_mbx - Setup default mailbox parameters and make request
* @adapter: adapter info struct
*
* Return: %0 on success, negative errno code on failure
*/
int ixd_init_dflt_mbx(struct ixd_adapter *adapter)
{
struct libie_ctlq_create_info ctlqs_info[2];
struct libie_ctlq_xn_init_params xn_params;
struct libie_ctlq_reg ctlq_reg_tx;
struct libie_ctlq_reg ctlq_reg_rx;
int err;
ixd_ctlq_reg_init(adapter, &ctlq_reg_tx, &ctlq_reg_rx);
ixd_init_ctlq_create_info(&ctlqs_info[0], LIBIE_CTLQ_TYPE_TX,
&ctlq_reg_tx);
ixd_init_ctlq_create_info(&ctlqs_info[1], LIBIE_CTLQ_TYPE_RX,
&ctlq_reg_rx);
ixd_init_libie_xn_params(&xn_params, adapter, ctlqs_info,
ARRAY_SIZE(ctlqs_info));
err = libie_ctlq_xn_init(&xn_params);
if (err)
return err;
adapter->xnm = xn_params.xnm;
ixd_adapter_fill_dflt_ctlqs(adapter);
if (!adapter->asq || !adapter->arq) {
ixd_deinit_dflt_mbx(adapter);
return -ENOENT;
}
queue_delayed_work(system_dfl_wq, &adapter->mbx_task, 0);
return 0;
}
/**
* ixd_init_task - Initialize after reset
* @work: init work struct
*/
void ixd_init_task(struct work_struct *work)
{
struct ixd_adapter *adapter;
int err;
adapter = container_of(work, struct ixd_adapter,
init_task.init_work.work);
if (!ixd_check_reset_complete(adapter)) {
if (++adapter->init_task.reset_retries < 10)
queue_delayed_work(system_dfl_wq,
&adapter->init_task.init_work,
IXD_INIT_TASK_DELAY_JIFFIES);
else
dev_err(ixd_to_dev(adapter),
"Device reset failed. The driver was unable to contact the device's firmware. Check that the FW is running.\n");
return;
}
adapter->init_task.reset_retries = 0;
err = ixd_init_dflt_mbx(adapter);
if (err) {
dev_err(ixd_to_dev(adapter),
"Failed to initialize the default mailbox: %pe\n",
ERR_PTR(err));
return;
}
err = ixd_vc_dev_init(adapter);
if (!err) {
adapter->init_task.vc_retries = 0;
adapter->init_task.success = true;
ixd_devlink_register(adapter);
return;
}
libie_ctlq_xn_shutdown(adapter->xnm);
ixd_trigger_reset(adapter);
ixd_deinit_dflt_mbx(adapter);
if (++adapter->init_task.vc_retries > 5 ||
(err != -ETIMEDOUT && err != -EAGAIN && err != -EBUSY)) {
dev_err(ixd_to_dev(adapter),
"Failed to establish mailbox communication with the hardware: %pe\n",
ERR_PTR(err));
return;
}
queue_delayed_work(system_dfl_wq, &adapter->init_task.init_work,
IXD_INIT_TASK_DELAY_JIFFIES);
}

View File

@ -0,0 +1,144 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2025 Intel Corporation */
#include "ixd.h"
#include "ixd_ctlq.h"
#include "ixd_lan_regs.h"
#include "ixd_devlink.h"
MODULE_DESCRIPTION("Intel(R) Control Plane Function Device Driver");
MODULE_IMPORT_NS("LIBIE_CP");
MODULE_IMPORT_NS("LIBIE_PCI");
MODULE_LICENSE("GPL");
/**
* ixd_remove - remove a CPF PCI device
* @pdev: PCI device being removed
*/
static void ixd_remove(struct pci_dev *pdev)
{
struct ixd_adapter *adapter = pci_get_drvdata(pdev);
/* Do not mix removal with (re)initialization */
cancel_delayed_work_sync(&adapter->init_task.init_work);
ixd_devlink_unregister(adapter);
/* Leave the device clean on exit */
if (adapter->xnm)
libie_ctlq_xn_shutdown(adapter->xnm);
ixd_trigger_reset(adapter);
ixd_deinit_dflt_mbx(adapter);
libie_pci_unmap_all_mmio_regions(&adapter->cp_ctx.mmio_info);
ixd_devlink_free(adapter);
}
/**
* ixd_shutdown - shut down a CPF PCI device
* @pdev: PCI device being shut down
*/
static void ixd_shutdown(struct pci_dev *pdev)
{
ixd_remove(pdev);
if (system_state == SYSTEM_POWER_OFF)
pci_set_power_state(pdev, PCI_D3hot);
}
/**
* ixd_iomap_regions - iomap PCI BARs
* @adapter: adapter to map memory regions for
*
* Returns: %0 on success, negative on failure
*/
static int ixd_iomap_regions(struct ixd_adapter *adapter)
{
const struct ixd_bar_region regions[] = {
{
.offset = PFGEN_RTRIG,
.size = PFGEN_RTRIG_REG_LEN,
},
{
.offset = PF_FW_MBX,
.size = PF_FW_MBX_REG_LEN,
},
};
for (int i = 0; i < ARRAY_SIZE(regions); i++) {
struct libie_mmio_info *mmio_info = &adapter->cp_ctx.mmio_info;
bool map_ok;
map_ok = libie_pci_map_mmio_region(mmio_info,
regions[i].offset,
regions[i].size);
if (!map_ok) {
dev_err(ixd_to_dev(adapter),
"Failed to map PCI device MMIO region\n");
libie_pci_unmap_all_mmio_regions(mmio_info);
return -EIO;
}
}
return 0;
}
/**
* ixd_probe - probe a CPF PCI device
* @pdev: corresponding PCI device
* @ent: entry in ixd_pci_tbl
*
* Returns: %0 on success, negative errno code on failure
*/
static int ixd_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct ixd_adapter *adapter;
int err;
adapter = ixd_adapter_alloc(&pdev->dev);
if (!adapter)
return -ENOMEM;
adapter->cp_ctx.mmio_info.pdev = pdev;
INIT_LIST_HEAD(&adapter->cp_ctx.mmio_info.mmio_list);
err = libie_pci_init_dev(pdev);
if (err)
goto free_adapter;
pci_set_drvdata(pdev, adapter);
err = ixd_iomap_regions(adapter);
if (err)
goto free_adapter;
INIT_DELAYED_WORK(&adapter->init_task.init_work,
ixd_init_task);
INIT_DELAYED_WORK(&adapter->mbx_task, ixd_ctlq_rx_task);
ixd_trigger_reset(adapter);
queue_delayed_work(system_dfl_wq, &adapter->init_task.init_work,
IXD_INIT_TASK_DELAY_JIFFIES);
return 0;
free_adapter:
ixd_devlink_free(adapter);
return err;
}
static const struct pci_device_id ixd_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, IXD_DEV_ID_CPF) },
{ }
};
MODULE_DEVICE_TABLE(pci, ixd_pci_tbl);
static struct pci_driver ixd_driver = {
.name = KBUILD_MODNAME,
.id_table = ixd_pci_tbl,
.probe = ixd_probe,
.remove = ixd_remove,
.shutdown = ixd_shutdown,
};
module_pci_driver(ixd_driver);

View File

@ -0,0 +1,190 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2025 Intel Corporation */
#include "ixd.h"
#include "ixd_ctlq.h"
#include "ixd_virtchnl.h"
/**
* ixd_vc_recv_event_msg - Handle virtchnl event message
* @adapter: The adapter handling the message
* @ctlq_msg: Message received
*/
void ixd_vc_recv_event_msg(struct ixd_adapter *adapter,
struct libie_ctlq_msg *ctlq_msg)
{
int payload_size = ctlq_msg->data_len;
struct virtchnl2_event *v2e;
if (payload_size < sizeof(*v2e)) {
dev_warn_ratelimited(ixd_to_dev(adapter),
"Failed to receive valid payload for event msg (op 0x%X len %u)\n",
ctlq_msg->chnl_opcode,
payload_size);
return;
}
v2e = (struct virtchnl2_event *)ctlq_msg->recv_mem.iov_base;
dev_dbg(ixd_to_dev(adapter), "Got event 0x%X from the CP\n",
le32_to_cpu(v2e->event));
}
/**
* ixd_vc_can_handle_msg - Decide if an event has to be handled by virtchnl code
* @ctlq_msg: Message received
*
* Return: %true if virtchnl code can handle the event, %false otherwise
*/
bool ixd_vc_can_handle_msg(struct libie_ctlq_msg *ctlq_msg)
{
return ctlq_msg->chnl_opcode == VIRTCHNL2_OP_EVENT;
}
/**
* ixd_handle_caps - Handle VIRTCHNL2_OP_GET_CAPS response
* @adapter: The adapter for which the capabilities are being updated
* @recv_buff: Buffer containing the response
* @recv_size: Response buffer size
* @ctx: unused
*
* Return: %0 if the response format is correct and was handled as expected,
* negative error otherwise.
*/
static int ixd_handle_caps(struct ixd_adapter *adapter, void *recv_buff,
size_t recv_size, void *ctx)
{
if (recv_size < sizeof(adapter->caps))
return -EBADMSG;
adapter->caps = *(typeof(adapter->caps) *)recv_buff;
return 0;
}
/**
* ixd_req_vc_caps - Request and save device capability
* @adapter: The adapter to get the capabilities for
*
* Return: success or error if sending the get capability message fails
*/
static int ixd_req_vc_caps(struct ixd_adapter *adapter)
{
const struct ixd_ctlq_req req = {
.opcode = VIRTCHNL2_OP_GET_CAPS,
.send_size = sizeof(struct virtchnl2_get_capabilities),
.ctx = NULL,
.send_buff_init = NULL,
.recv_process = ixd_handle_caps,
};
return ixd_ctlq_do_req(adapter, &req);
}
/**
* ixd_get_vc_ver - Get version info from adapter
*
* Return: filled in virtchannel2 version info, ready for sending
*/
static struct virtchnl2_version_info ixd_get_vc_ver(void)
{
return (struct virtchnl2_version_info) {
.major = cpu_to_le32(VIRTCHNL2_VERSION_MAJOR_2),
.minor = cpu_to_le32(VIRTCHNL2_VERSION_MINOR_0),
};
}
static void ixd_fill_vc_ver(struct ixd_adapter *adapter, void *send_buff,
void *ctx)
{
*(struct virtchnl2_version_info *)send_buff = ixd_get_vc_ver();
}
/**
* ixd_handle_vc_ver - Handle VIRTCHNL2_OP_VERSION response
* @adapter: The adapter for which the version is being updated
* @recv_buff: Buffer containing the response
* @recv_size: Response buffer size
* @ctx: Unused
*
* Return: %0 if the response format is correct and was handled as expected,
* negative error otherwise.
*/
static int ixd_handle_vc_ver(struct ixd_adapter *adapter, void *recv_buff,
size_t recv_size, void *ctx)
{
struct virtchnl2_version_info need_ver = ixd_get_vc_ver();
struct virtchnl2_version_info *recv_ver;
if (recv_size < sizeof(need_ver))
return -EBADMSG;
recv_ver = recv_buff;
if (le32_to_cpu(need_ver.major) != le32_to_cpu(recv_ver->major) ||
le32_to_cpu(need_ver.minor) != le32_to_cpu(recv_ver->minor))
dev_warn(ixd_to_dev(adapter),
"Virtchnl version does not match (expected %u.%u, received %u.%u)\n",
le32_to_cpu(need_ver.major),
le32_to_cpu(need_ver.minor),
le32_to_cpu(recv_ver->major),
le32_to_cpu(recv_ver->minor));
if (le32_to_cpu(need_ver.major) != le32_to_cpu(recv_ver->major)) {
dev_err(ixd_to_dev(adapter),
"Device initialization failed due to virtchnl major version mismatch\n");
return -EOPNOTSUPP;
}
adapter->vc_ver.major = le32_to_cpu(recv_ver->major);
adapter->vc_ver.minor = le32_to_cpu(recv_ver->minor);
return 0;
}
/**
* ixd_req_vc_version - Request and save Virtchannel2 version
* @adapter: The adapter to get the version for
*
* Return: success or error if sending fails or the response was not as expected
*/
static int ixd_req_vc_version(struct ixd_adapter *adapter)
{
const struct ixd_ctlq_req req = {
.opcode = VIRTCHNL2_OP_VERSION,
.send_size = sizeof(struct virtchnl2_version_info),
.ctx = NULL,
.send_buff_init = ixd_fill_vc_ver,
.recv_process = ixd_handle_vc_ver,
};
return ixd_ctlq_do_req(adapter, &req);
}
/**
* ixd_vc_dev_init - virtchnl device core initialization
* @adapter: device information
*
* Return: %0 on success or error if any step of the initialization fails
*/
int ixd_vc_dev_init(struct ixd_adapter *adapter)
{
int err;
err = ixd_req_vc_version(adapter);
if (err) {
dev_warn(ixd_to_dev(adapter),
"Getting virtchnl version failed, error=%pe\n",
ERR_PTR(err));
return err;
}
err = ixd_req_vc_caps(adapter);
if (err) {
dev_warn(ixd_to_dev(adapter),
"Getting virtchnl capabilities failed, error=%pe\n",
ERR_PTR(err));
return err;
}
return err;
}

View File

@ -0,0 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (C) 2025 Intel Corporation */
#ifndef _IXD_VIRTCHNL_H_
#define _IXD_VIRTCHNL_H_
int ixd_vc_dev_init(struct ixd_adapter *adapter);
bool ixd_vc_can_handle_msg(struct libie_ctlq_msg *ctlq_msg);
void ixd_vc_recv_event_msg(struct ixd_adapter *adapter,
struct libie_ctlq_msg *ctlq_msg);
#endif /* _IXD_VIRTCHNL_H_ */

View File

@ -145,25 +145,29 @@ static bool libeth_rx_page_pool_params_zc(struct libeth_fq *fq,
/**
* libeth_rx_fq_create - create a PP with the default libeth settings
* @fq: buffer queue struct to fill
* @napi: &napi_struct covering this PP (no usage outside its poll loops)
* @napi_dev: &napi_struct for NAPI (data) queues, &device for others
*
* Return: %0 on success, -%errno on failure.
*/
int libeth_rx_fq_create(struct libeth_fq *fq, struct napi_struct *napi)
int libeth_rx_fq_create(struct libeth_fq *fq, void *napi_dev)
{
struct napi_struct *napi = fq->no_napi ? NULL : napi_dev;
struct page_pool_params pp = {
.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV,
.order = LIBETH_RX_PAGE_ORDER,
.pool_size = fq->count,
.nid = fq->nid,
.dev = napi->dev->dev.parent,
.netdev = napi->dev,
.dev = napi ? napi->dev->dev.parent : napi_dev,
.netdev = napi ? napi->dev : NULL,
.napi = napi,
};
struct libeth_fqe *fqes;
struct page_pool *pool;
int ret;
if (!pp.netdev && fq->type == LIBETH_FQE_MTU)
return -EINVAL;
pp.dma_dir = fq->xdp ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE;
if (!fq->hsplit)

View File

@ -15,6 +15,14 @@ config LIBIE_ADMINQ
Helper functions used by Intel Ethernet drivers for administration
queue command interface (aka adminq).
config LIBIE_CP
tristate
select LIBETH
select LIBIE_PCI
help
Common helper routines to communicate with the device Control Plane
using virtchnl2 or related mailbox protocols.
config LIBIE_FWLOG
tristate
select LIBIE_ADMINQ
@ -23,3 +31,9 @@ config LIBIE_FWLOG
for it. Firmware logging is using admin queue interface to communicate
with the device. Debugfs is a user interface used to config logging
and dump all collected logs.
config LIBIE_PCI
tristate
help
Helper functions for management of PCI resources belonging
to networking devices.

View File

@ -9,6 +9,14 @@ obj-$(CONFIG_LIBIE_ADMINQ) += libie_adminq.o
libie_adminq-y := adminq.o
obj-$(CONFIG_LIBIE_CP) += libie_cp.o
libie_cp-y := controlq.o
obj-$(CONFIG_LIBIE_FWLOG) += libie_fwlog.o
libie_fwlog-y := fwlog.o
obj-$(CONFIG_LIBIE_PCI) += libie_pci.o
libie_pci-y := pci.o

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,229 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2025 Intel Corporation */
#include <linux/net/intel/libie/pci.h>
/**
* libie_find_mmio_region - find MMIO region containing a range
* @mmio_list: list that contains MMIO region info
* @offset: range start offset
* @size: range size
* @bar_idx: BAR index containing the range to search
*
* MMIO regions mappings are traversed from oldest to newest.
*
* Return: pointer to a MMIO region overlapping with the range in any way or
* NULL if no such region is mapped.
*/
static struct libie_pci_mmio_region *
libie_find_mmio_region(const struct list_head *mmio_list,
resource_size_t offset, resource_size_t size,
int bar_idx)
{
resource_size_t end_offset = offset + size;
struct libie_pci_mmio_region *mr;
list_for_each_entry(mr, mmio_list, list) {
resource_size_t mr_end = mr->offset + mr->size;
resource_size_t mr_start = mr->offset;
if (mr->bar_idx != bar_idx)
continue;
if (offset < mr_end && end_offset > mr_start)
return mr;
}
return NULL;
}
/**
* __libie_pci_get_mmio_addr - get the MMIO virtual address
* @mmio_info: contains list of MMIO regions
* @offset: register offset to find
* @num_args: number of additional arguments present
* @...: optional BAR index (0 by default)
*
* This function finds the virtual address of a register offset by iterating
* through the non-linear MMIO regions that are mapped by the driver.
*
* The list is traversed oldest to newest, so accessing an older mapping
* via this function while deleting a newer one is allowed.
*
* Return: valid MMIO virtual address or NULL.
*/
void __iomem *__libie_pci_get_mmio_addr(struct libie_mmio_info *mmio_info,
resource_size_t offset,
int num_args, ...)
{
struct libie_pci_mmio_region *mr;
int bar_idx = 0;
va_list args;
if (num_args) {
va_start(args, num_args);
bar_idx = va_arg(args, int);
va_end(args);
}
list_for_each_entry(mr, &mmio_info->mmio_list, list)
if (bar_idx == mr->bar_idx && offset >= mr->offset &&
offset < mr->offset + mr->size) {
offset -= mr->offset;
return mr->addr + offset;
}
WARN_ONCE(true, "Access to an unmapped MMIO region (BAR%d, offset %pa)",
bar_idx, &offset);
return NULL;
}
EXPORT_SYMBOL_NS_GPL(__libie_pci_get_mmio_addr, "LIBIE_PCI");
/**
* __libie_pci_map_mmio_region - map PCI device MMIO region
* @mmio_info: struct to store the mapped MMIO region
* @offset: MMIO region start offset
* @size: MMIO region size
* @num_args: number of additional arguments present
* @...: optional BAR index (0 by default)
*
* Return: true if the requested address range is accessible through
* new or existing mapping, false otherwise.
*/
bool __libie_pci_map_mmio_region(struct libie_mmio_info *mmio_info,
resource_size_t offset,
resource_size_t size, int num_args, ...)
{
struct pci_dev *pdev = mmio_info->pdev;
struct libie_pci_mmio_region *mr;
resource_size_t end_offset;
void __iomem *va;
int bar_idx = 0;
va_list args;
if (num_args) {
va_start(args, num_args);
bar_idx = va_arg(args, int);
va_end(args);
}
if (bar_idx >= PCI_STD_NUM_BARS || bar_idx < 0 ||
!pci_resource_is_mem(pdev, bar_idx))
return false;
/* pci_iomap_range() silently maps less
* if the requested length is too big
*/
if (!size || check_add_overflow(offset, size, &end_offset) ||
end_offset > pci_resource_len(pdev, bar_idx))
return false;
mr = libie_find_mmio_region(&mmio_info->mmio_list, offset, size,
bar_idx);
if (mr) {
pci_warn(pdev,
"Mapping of BAR%u (offset=%llu, size=%llu) intersecting region (offset=%llu, size=%llu) already exists\n",
bar_idx, (unsigned long long)mr->offset,
(unsigned long long)mr->size,
(unsigned long long)offset, (unsigned long long)size);
return mr->offset <= offset &&
mr->offset + mr->size >= end_offset;
}
va = pci_iomap_range(mmio_info->pdev, bar_idx, offset, size);
if (!va) {
pci_err(pdev, "Failed to map BAR%u region\n", bar_idx);
return false;
}
mr = kvzalloc_obj(*mr);
if (!mr) {
pci_iounmap(pdev, va);
return false;
}
mr->addr = va;
mr->offset = offset;
mr->size = size;
mr->bar_idx = bar_idx;
list_add_tail(&mr->list, &mmio_info->mmio_list);
return true;
}
EXPORT_SYMBOL_NS_GPL(__libie_pci_map_mmio_region, "LIBIE_PCI");
/**
* libie_pci_unmap_fltr_regs - unmap selected PCI device MMIO regions
* @mmio_info: contains list of MMIO regions to unmap
* @fltr: returns true, if region is to be unmapped
*/
void libie_pci_unmap_fltr_regs(struct libie_mmio_info *mmio_info,
bool (*fltr)(struct libie_mmio_info *mmio_info,
struct libie_pci_mmio_region *reg))
{
struct libie_pci_mmio_region *mr, *tmp;
list_for_each_entry_safe(mr, tmp, &mmio_info->mmio_list, list) {
if (!fltr(mmio_info, mr))
continue;
list_del(&mr->list);
pci_iounmap(mmio_info->pdev, mr->addr);
kvfree(mr);
}
}
EXPORT_SYMBOL_NS_GPL(libie_pci_unmap_fltr_regs, "LIBIE_PCI");
/**
* libie_pci_unmap_all_mmio_regions - unmap all PCI device MMIO regions
* @mmio_info: contains list of MMIO regions to unmap
*/
void libie_pci_unmap_all_mmio_regions(struct libie_mmio_info *mmio_info)
{
struct libie_pci_mmio_region *mr, *tmp;
list_for_each_entry_safe(mr, tmp, &mmio_info->mmio_list, list) {
list_del(&mr->list);
pci_iounmap(mmio_info->pdev, mr->addr);
kvfree(mr);
}
}
EXPORT_SYMBOL_NS_GPL(libie_pci_unmap_all_mmio_regions, "LIBIE_PCI");
/**
* libie_pci_init_dev - enable and configure the device
* @pdev: PCI device information
*
* Enable the device, request memory regions, set 64-bit DMA mask
* and coherent DMA mask, and enable bus-mastering
*
* Return: %0 on success, -%errno on failure.
*/
int libie_pci_init_dev(struct pci_dev *pdev)
{
int err;
err = pcim_enable_device(pdev);
if (err)
return err;
for (int bar = 0; bar < PCI_STD_NUM_BARS; bar++)
if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM) {
err = pcim_request_region(pdev, bar, pci_name(pdev));
if (err)
return err;
}
err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
if (err)
return err;
pci_set_master(pdev);
return 0;
}
EXPORT_SYMBOL_NS_GPL(libie_pci_init_dev, "LIBIE_PCI");
MODULE_DESCRIPTION("Common Ethernet PCI library");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,436 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (C) 2025 Intel Corporation */
#ifndef __LIBIE_CONTROLQ_H
#define __LIBIE_CONTROLQ_H
#include <linux/dmapool.h>
#include <net/libeth/rx.h>
#include <linux/net/intel/libie/pci.h>
#include <linux/net/intel/virtchnl2.h>
/* Default mailbox control queue */
#define LIBIE_CTLQ_MBX_ID -1
#define LIBIE_CTLQ_MAX_BUF_LEN SZ_4K
/**
* enum libie_ctlq_type - control queue type
* @LIBIE_CTLQ_TYPE_TX: basic Tx control queue
* @LIBIE_CTLQ_TYPE_RX: basic Rx control queue
*/
enum libie_ctlq_type {
LIBIE_CTLQ_TYPE_TX = 0,
LIBIE_CTLQ_TYPE_RX = 1,
};
/* Opcode used to send controlq message to the control plane */
#define LIBIE_CTLQ_SEND_MSG_TO_CP 0x801
#define LIBIE_CTLQ_SEND_MSG_TO_PEER 0x804
#define LIBIE_CP_TX_COPYBREAK 128
/**
* struct libie_ctlq_ctx - contains controlq info and MMIO region info
* @mmio_info: MMIO region info structure
* @ctlqs: list that stores all the control queues
* @ctlqs_lock: lock for control queue list
*/
struct libie_ctlq_ctx {
struct libie_mmio_info mmio_info;
struct list_head ctlqs;
spinlock_t ctlqs_lock; /* protects the ctlqs list */
};
/**
* struct libie_ctlq_reg - structure representing virtual addresses of the
* controlq registers and masks
* @head: controlq head register address
* @tail: controlq tail register address
* @len: register address to write controlq length and enable bit
* @addr_high: register address to write the upper 32b of ring physical address
* @addr_low: register address to write the lower 32b of ring physical address
* @len_mask: mask to read the controlq length
* @len_ena_mask: mask to write the controlq enable bit
* @head_mask: mask to read the head value
*/
struct libie_ctlq_reg {
void __iomem *head;
void __iomem *tail;
void __iomem *len;
void __iomem *addr_high;
void __iomem *addr_low;
u32 len_mask;
u32 len_ena_mask;
u32 head_mask;
};
/**
* struct libie_cp_dma_mem - structure for DMA memory
* @va: virtual address
* @pa: physical address
* @size: memory size
* @direction: memory to device or device to memory
*/
struct libie_cp_dma_mem {
void *va;
dma_addr_t pa;
size_t size;
int direction;
};
/**
* struct libie_ctlq_msg - control queue message data
* @flags: refer to 'Flags sub-structure' definitions
* @opcode: infrastructure message opcode
* @data_len: size of the payload
* @func_id: queue id for mailbox selection, 0 for default mailbox (Tx)
* @hw_retval: execution status from the HW (Rx)
* @chnl_opcode: virtchnl message opcode
* @chnl_retval: virtchnl return value
* @param0: indirect message raw parameter0
* @sw_cookie: used to verify the response of the sent virtchnl message
* @virt_flags: virtchnl capability flags
* @addr_param: additional parameters in place of the address, given no buffer
* @recv_mem: virtual address and size of the buffer that contains
* the indirect response
* @send_mem: physical and virtual address of the DMA buffer,
* used for sending
*/
struct libie_ctlq_msg {
u16 flags;
u16 opcode;
u16 data_len;
union {
u16 func_id;
u16 hw_retval;
};
u32 chnl_opcode;
u32 chnl_retval;
u32 param0;
u16 sw_cookie;
u16 virt_flags;
u64 addr_param;
union {
struct kvec recv_mem;
struct libie_cp_dma_mem send_mem;
};
};
/**
* struct libie_ctlq_create_info - control queue create information
* @type: control queue type (Rx or Tx)
* @id: queue offset passed as input, -1 for default mailbox
* @reg: registers accessed by control queue
* @len: controlq length
*/
struct libie_ctlq_create_info {
enum libie_ctlq_type type;
int id;
struct libie_ctlq_reg reg;
u16 len;
};
/**
* struct libie_ctlq_info - control queue information
* @list: used to add a controlq to the list of queues in libie_ctlq_ctx
* @type: control queue type
* @qid: queue identifier
* @lock: control queue lock
* @ring_mem: descriptor ring DMA memory
* @descs: array of descriptors
* @rx_fqes: array of controlq Rx buffers
* @tx_msg: Tx messages sent to hardware
* @reg: registers used by control queue
* @dev: device that owns this control queue
* @pp: page pool for controlq Rx buffers
* @truesize: size to allocate per buffer
* @next_to_clean: next descriptor to be cleaned
* @next_to_use: next available slot to send buffer (Tx queue)
* @next_to_post: next available slot to post buffers to (Rx queue)
* @ring_len: length of the descriptor ring
*/
struct libie_ctlq_info {
struct list_head list;
enum libie_ctlq_type type;
int qid;
spinlock_t lock; /* for concurrent processing */
struct libie_cp_dma_mem ring_mem;
struct libie_ctlq_desc *descs;
union {
struct libeth_fqe *rx_fqes;
struct libie_ctlq_msg **tx_msg;
};
struct libie_ctlq_reg reg;
struct device *dev;
struct page_pool *pp;
u32 truesize;
u32 next_to_clean;
union {
u32 next_to_use;
u32 next_to_post;
};
u32 ring_len;
};
#define LIBIE_CTLQ_MBX_ATQ_LEN GENMASK(9, 0)
/* libie controlq descriptor qword0 details */
/* 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 |
*/
#define LIBIE_CTLQ_DESC_FLAG_DD BIT(0)
#define LIBIE_CTLQ_DESC_FLAG_CMP BIT(1)
#define LIBIE_CTLQ_DESC_FLAG_ERR BIT(2)
#define LIBIE_CTLQ_DESC_FLAG_FTYPE_VM BIT(6)
#define LIBIE_CTLQ_DESC_FLAG_FTYPE_PF BIT(7)
#define LIBIE_CTLQ_DESC_FLAG_FTYPE GENMASK(7, 6)
#define LIBIE_CTLQ_DESC_FLAG_RD BIT(10)
#define LIBIE_CTLQ_DESC_FLAG_VFC BIT(11)
#define LIBIE_CTLQ_DESC_FLAG_BUF BIT(12)
#define LIBIE_CTLQ_DESC_FLAG_HOST_ID GENMASK(15, 13)
#define LIBIE_CTLQ_DESC_FLAGS GENMASK(15, 0)
#define LIBIE_CTLQ_DESC_INFRA_OPCODE GENMASK_ULL(31, 16)
#define LIBIE_CTLQ_DESC_DATA_LEN GENMASK_ULL(47, 32)
#define LIBIE_CTLQ_DESC_HW_RETVAL GENMASK_ULL(63, 48)
#define LIBIE_CTLQ_DESC_PFID_VFID GENMASK_ULL(63, 48)
/* libie controlq descriptor qword1 details */
#define LIBIE_CTLQ_DESC_VIRTCHNL_OPCODE GENMASK(27, 0)
#define LIBIE_CTLQ_DESC_VIRTCHNL_DESC_TYPE GENMASK_ULL(31, 28)
#define LIBIE_CTLQ_DESC_VIRTCHNL_MSG_RET_VAL GENMASK_ULL(63, 32)
/* libie controlq descriptor qword2 details */
#define LIBIE_CTLQ_DESC_MSG_PARAM0 GENMASK_ULL(31, 0)
#define LIBIE_CTLQ_DESC_SW_COOKIE GENMASK_ULL(47, 32)
#define LIBIE_CTLQ_DESC_VIRTCHNL_FLAGS GENMASK_ULL(63, 48)
/* libie controlq descriptor qword3 details */
#define LIBIE_CTLQ_DESC_DATA_ADDR_HIGH GENMASK_ULL(31, 0)
#define LIBIE_CTLQ_DESC_DATA_ADDR_LOW GENMASK_ULL(63, 32)
/**
* struct libie_ctlq_desc - control queue descriptor format
* @qword0: flags, message opcode, data length etc
* @qword1: virtchnl opcode, descriptor type and return value
* @qword2: indirect message parameters
* @qword3: indirect message buffer address
*/
struct libie_ctlq_desc {
__le64 qword0;
__le64 qword1;
__le64 qword2;
__le64 qword3;
};
/**
* struct libie_ctlq_clean_params - cleaning parameters for Tx messages
* @rel_dma_mem: non-sleeping callback to put the DMA buffer after send
* @rel_ctx: additional context for release callback
* @ctlq: control queue information
* @num_msgs: number of messages to be cleaned
* @force: clean even if DD is not yet set, use only for final cleanup
*/
struct libie_ctlq_clean_params {
void (*rel_dma_mem)(const void *ctx, struct libie_cp_dma_mem *dma_mem);
const void *rel_ctx;
struct libie_ctlq_info *ctlq;
u16 num_msgs;
bool force;
};
/**
* libie_ctlq_release_rx_buf - Release Rx buffer for a specific control queue
* @rx_buf: Rx buffer to be freed
*
* Driver uses this function to post back the Rx buffer after the usage.
*/
static inline void libie_ctlq_release_rx_buf(struct kvec *rx_buf)
{
netmem_ref netmem;
if (!rx_buf->iov_base)
return;
netmem = virt_to_netmem(rx_buf->iov_base);
page_pool_put_full_netmem(netmem_get_pp(netmem), netmem, false);
}
int libie_ctlq_init(struct libie_ctlq_ctx *ctx,
const struct libie_ctlq_create_info *qinfo, u32 numq);
void libie_ctlq_deinit(struct libie_ctlq_ctx *ctx);
struct libie_ctlq_info *libie_find_ctlq(struct libie_ctlq_ctx *ctx,
enum libie_ctlq_type type,
int id);
u32 libie_ctlq_send_desc_avail(const struct libie_ctlq_info *ctlq);
void libie_ctlq_send(struct libie_ctlq_info *ctlq, u32 num_q_msg);
u32 libie_ctlq_send_clean(const struct libie_ctlq_clean_params *params);
u32 libie_ctlq_recv(struct libie_ctlq_info *ctlq, struct libie_ctlq_msg *msg,
u32 num_q_msg);
int libie_ctlq_post_rx_buffs(struct libie_ctlq_info *ctlq);
/* Only 8 bits are available in descriptor for Xn index */
#define LIBIE_CTLQ_MAX_XN_ENTRIES 256
#define LIBIE_CTLQ_XN_COOKIE_M GENMASK(15, 8)
#define LIBIE_CTLQ_XN_INDEX_M GENMASK(7, 0)
/**
* enum libie_ctlq_xn_state - Transaction state of a virtchnl message
* @LIBIE_CTLQ_XN_IDLE: transaction is available to use
* @LIBIE_CTLQ_XN_WAITING: waiting for transaction to complete
* @LIBIE_CTLQ_XN_COMPLETED_SUCCESS: transaction completed with success
* @LIBIE_CTLQ_XN_COMPLETED_FAILED: transaction completed with failure
* @LIBIE_CTLQ_XN_ASYNC: asynchronous virtchnl message transaction type
* @LIBIE_CTLQ_XN_SHUTDOWN: transaction cannot be used anymore
*/
enum libie_ctlq_xn_state {
LIBIE_CTLQ_XN_IDLE = 0,
LIBIE_CTLQ_XN_WAITING,
LIBIE_CTLQ_XN_COMPLETED_SUCCESS,
LIBIE_CTLQ_XN_COMPLETED_FAILED,
LIBIE_CTLQ_XN_ASYNC,
LIBIE_CTLQ_XN_SHUTDOWN,
};
/**
* struct libie_ctlq_xn - structure representing a virtchnl transaction entry
* @resp_cb: non-sleeping callback to handle the response to an async message
* @xn_lock: lock to protect the transaction entry state
* @cmd_completion_event: wait until reply is received or xn is terminated
* @small_dma_mem: DMA memory for copying small send buffers from stack,
* is recycled when response is received or on timeout
* @send_dma_mem: DMA memory of send buffer
* @recv_mem: receive buffer
* @send_ctx: context for callback function
* @timeout_ms: Xn transaction timeout in msecs
* @timestamp: timestamp to record the Xn send
* @tx_msg: control queue Tx message slot to track small DMA usage
* @virtchnl_opcode: virtchnl command opcode used for Xn transaction
* @state: transaction state of a virtchnl message
* @cookie: unique message identifier, incremented every time the slot is used
* @index: index of the transaction entry
*/
struct libie_ctlq_xn {
void (*resp_cb)(void *ctx, struct kvec *mem, int status);
spinlock_t xn_lock; /* protects state */
struct completion cmd_completion_event;
struct libie_cp_dma_mem small_dma_mem;
struct libie_cp_dma_mem send_dma_mem;
struct kvec recv_mem;
void *send_ctx;
u64 timeout_ms;
ktime_t timestamp;
struct libie_ctlq_msg *tx_msg;
u32 virtchnl_opcode;
enum libie_ctlq_xn_state state;
u8 cookie;
u8 index;
};
/**
* struct libie_ctlq_xn_manager - structure representing the array of virtchnl
* transaction entries
* @ctx: pointer to controlq context structure
* @free_xns_bm_lock: lock to protect the free Xn entries bit map
* @free_xns_bm: bitmap that represents the free Xn entries
* @ring: array of Xn entries
* @small_buff_pool: DMA pool for small send buffers
* @can_destroy: completion, triggered by the last released transaction
* @shutdown: shutdown process has been started, no new transactions allowed
*/
struct libie_ctlq_xn_manager {
struct libie_ctlq_ctx *ctx;
spinlock_t free_xns_bm_lock; /* get/check entries */
DECLARE_BITMAP(free_xns_bm, LIBIE_CTLQ_MAX_XN_ENTRIES);
struct libie_ctlq_xn ring[LIBIE_CTLQ_MAX_XN_ENTRIES];
struct dma_pool *small_buff_pool;
struct completion can_destroy;
bool shutdown;
};
/**
* struct libie_ctlq_xn_send_params - structure representing send Xn entry
* @resp_cb: non-sleeping callback to handle the response to an async message
* @rel_tx_buf: non-sleeping callback for freeing the send buffer
* @xnm: Xn manager to process Xn entries
* @ctlq: send control queue information
* @ctlq_msg: control queue message information
* @send_buf: buffer that carries outgoing message data, buffers larger than
* LIBIE_CP_TX_COPYBREAK bytes will always be consumed
* @recv_mem: receive buffer
* @send_ctx: context for callback function
* @timeout_ms: virtchnl transaction timeout in msecs
* @chnl_opcode: virtchnl message opcode
*/
struct libie_ctlq_xn_send_params {
void (*resp_cb)(void *ctx, struct kvec *mem, int status);
void (*rel_tx_buf)(const void *buf_va);
struct libie_ctlq_xn_manager *xnm;
struct libie_ctlq_info *ctlq;
struct libie_ctlq_msg *ctlq_msg;
struct kvec send_buf;
struct kvec recv_mem;
void *send_ctx;
u64 timeout_ms;
u32 chnl_opcode;
};
/**
* libie_cp_can_send_onstack - can a message be sent using a stack variable
* @size: ctlq data buffer size
*
* Return: %true if the message size is small enough for caller to pass
* an on-stack buffer, %false if kmalloc is needed
*/
static inline bool libie_cp_can_send_onstack(u32 size)
{
return size <= LIBIE_CP_TX_COPYBREAK;
}
/**
* struct libie_ctlq_xn_recv_params - request to receive xn responses
* @ctlq_msg_handler: handler for Rx messages with no matching xn (mandatory)
* @xnm: Xn manager to process Xn entries
* @ctlq: control queue information
* @budget: maximum number of messages to process
*/
struct libie_ctlq_xn_recv_params {
void (*ctlq_msg_handler)(struct libie_ctlq_ctx *ctx,
struct libie_ctlq_msg *msg);
struct libie_ctlq_xn_manager *xnm;
struct libie_ctlq_info *ctlq;
u32 budget;
};
/**
* struct libie_ctlq_xn_init_params - xn transaction manager parameters
* @cctlq_info: control queue information
* @ctx: pointer to controlq context structure
* @xnm: Xn manager to process Xn entries
* @num_qs: number of control queues to be initialized
*/
struct libie_ctlq_xn_init_params {
struct libie_ctlq_create_info *cctlq_info;
struct libie_ctlq_ctx *ctx;
struct libie_ctlq_xn_manager *xnm;
u32 num_qs;
};
int libie_ctlq_xn_init(struct libie_ctlq_xn_init_params *params);
void libie_ctlq_xn_deinit(struct libie_ctlq_xn_manager *xnm,
struct libie_ctlq_ctx *ctx);
void libie_ctlq_xn_shutdown(struct libie_ctlq_xn_manager *xnm);
int libie_ctlq_xn_send(struct libie_ctlq_xn_send_params *params);
u32 libie_ctlq_xn_recv(struct libie_ctlq_xn_recv_params *params);
u32 libie_ctlq_xn_send_clean(struct libie_ctlq_info *ctlq,
void (*rel_tx_buf)(const void *buf_va),
bool force);
#endif /* __LIBIE_CONTROLQ_H */

View File

@ -0,0 +1,56 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (C) 2025 Intel Corporation */
#ifndef __LIBIE_PCI_H
#define __LIBIE_PCI_H
#include <linux/pci.h>
/**
* struct libie_pci_mmio_region - structure for MMIO region info
* @list: used to add a MMIO region to the list of MMIO regions in
* libie_mmio_info
* @addr: virtual address of MMIO region start
* @offset: start offset of the MMIO region
* @size: size of the MMIO region
* @bar_idx: BAR index to which the MMIO region belongs to
*/
struct libie_pci_mmio_region {
struct list_head list;
void __iomem *addr;
resource_size_t offset;
resource_size_t size;
u16 bar_idx;
};
/**
* struct libie_mmio_info - contains list of MMIO regions
* @pdev: PCI device pointer
* @mmio_list: list of MMIO regions
*/
struct libie_mmio_info {
struct pci_dev *pdev;
struct list_head mmio_list;
};
#define libie_pci_map_mmio_region(mmio_info, offset, size, ...) \
__libie_pci_map_mmio_region(mmio_info, offset, size, \
COUNT_ARGS(__VA_ARGS__), ##__VA_ARGS__)
#define libie_pci_get_mmio_addr(mmio_info, offset, ...) \
__libie_pci_get_mmio_addr(mmio_info, offset, \
COUNT_ARGS(__VA_ARGS__), ##__VA_ARGS__)
bool __libie_pci_map_mmio_region(struct libie_mmio_info *mmio_info,
resource_size_t offset, resource_size_t size,
int num_args, ...);
void __iomem *__libie_pci_get_mmio_addr(struct libie_mmio_info *mmio_info,
resource_size_t offset,
int num_args, ...);
void libie_pci_unmap_all_mmio_regions(struct libie_mmio_info *mmio_info);
void libie_pci_unmap_fltr_regs(struct libie_mmio_info *mmio_info,
bool (*fltr)(struct libie_mmio_info *mmio_info,
struct libie_pci_mmio_region *reg));
int libie_pci_init_dev(struct pci_dev *pdev);
#endif /* __LIBIE_PCI_H */

View File

@ -69,6 +69,7 @@ enum libeth_fqe_type {
* @type: type of the buffers this queue has
* @hsplit: flag whether header split is enabled
* @xdp: flag indicating whether XDP is enabled
* @no_napi: the queue is not a data queue and does not have NAPI
* @buf_len: HW-writeable length per each buffer
* @nid: ID of the closest NUMA node with memory
*/
@ -85,12 +86,13 @@ struct libeth_fq {
enum libeth_fqe_type type:2;
bool hsplit:1;
bool xdp:1;
bool no_napi:1;
u32 buf_len;
int nid;
};
int libeth_rx_fq_create(struct libeth_fq *fq, struct napi_struct *napi);
int libeth_rx_fq_create(struct libeth_fq *fq, void *napi_dev);
void libeth_rx_fq_destroy(struct libeth_fq *fq);
/**