Commit f3f409a9 authored by David S. Miller's avatar David S. Miller

Merge branch 'ionic-ptp'

Shannon Nelson says:

====================
ionic: add PTP and hw clock support

This patchset adds support for accessing the DSC hardware clock and
for offloading PTP timestamping.

Tx packet timestamping happens through a separate Tx queue set up with
expanded completion descriptors that can report the timestamp.

Rx timestamping can happen either on all queues, or on a separate
timestamping queue when specific filtering is requested.  Again, the
timestamps are reported with the expanded completion descriptors.

The timestamping offload ability is advertised but not enabled until an
OS service asks for it.  At that time the driver's queues are reconfigured
to use the different completion descriptors and the private processing
queues as needed.

Reading the raw clock value comes through a new pair of values in the
device info registers in BAR0.  These high and low values are interpreted
with help from new clock mask, mult, and shift values in the device
identity information.

First we add the ability to detect new queue features, then the handling
of the new descriptor sizes.  After adding the new interface structures,
we start adding the support code, saving the advertising to the stack
for last.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents c2bcb4cf afeefec6
...@@ -6,3 +6,4 @@ obj-$(CONFIG_IONIC) := ionic.o ...@@ -6,3 +6,4 @@ obj-$(CONFIG_IONIC) := ionic.o
ionic-y := ionic_main.o ionic_bus_pci.o ionic_devlink.o ionic_dev.o \ ionic-y := ionic_main.o ionic_bus_pci.o ionic_devlink.o ionic_dev.o \
ionic_debugfs.o ionic_lif.o ionic_rx_filter.o ionic_ethtool.o \ ionic_debugfs.o ionic_lif.o ionic_rx_filter.o ionic_ethtool.o \
ionic_txrx.o ionic_stats.o ionic_fw.o ionic_txrx.o ionic_stats.o ionic_fw.o
ionic-$(CONFIG_PTP_1588_CLOCK) += ionic_phc.o
...@@ -20,6 +20,10 @@ struct ionic_lif; ...@@ -20,6 +20,10 @@ struct ionic_lif;
#define DEVCMD_TIMEOUT 10 #define DEVCMD_TIMEOUT 10
#define IONIC_PHC_UPDATE_NS 10000000000 /* 10s in nanoseconds */
#define NORMAL_PPB 1000000000 /* one billion parts per billion */
#define SCALED_PPM (1000000ull << 16) /* 2^16 million parts per 2^16 million */
struct ionic_vf { struct ionic_vf {
u16 index; u16 index;
u8 macaddr[6]; u8 macaddr[6];
...@@ -64,6 +68,8 @@ struct ionic_admin_ctx { ...@@ -64,6 +68,8 @@ struct ionic_admin_ctx {
union ionic_adminq_comp comp; union ionic_adminq_comp comp;
}; };
int ionic_adminq_post(struct ionic_lif *lif, struct ionic_admin_ctx *ctx);
int ionic_adminq_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx, int err);
int ionic_adminq_post_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx); int ionic_adminq_post_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx);
int ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_wait); int ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_wait);
int ionic_set_dma_mask(struct ionic *ionic); int ionic_set_dma_mask(struct ionic *ionic);
......
...@@ -79,6 +79,8 @@ int ionic_dev_setup(struct ionic *ionic) ...@@ -79,6 +79,8 @@ int ionic_dev_setup(struct ionic *ionic)
idev->intr_status = bar->vaddr + IONIC_BAR0_INTR_STATUS_OFFSET; idev->intr_status = bar->vaddr + IONIC_BAR0_INTR_STATUS_OFFSET;
idev->intr_ctrl = bar->vaddr + IONIC_BAR0_INTR_CTRL_OFFSET; idev->intr_ctrl = bar->vaddr + IONIC_BAR0_INTR_CTRL_OFFSET;
idev->hwstamp_regs = &idev->dev_info_regs->hwstamp;
sig = ioread32(&idev->dev_info_regs->signature); sig = ioread32(&idev->dev_info_regs->signature);
if (sig != IONIC_DEV_INFO_SIGNATURE) { if (sig != IONIC_DEV_INFO_SIGNATURE) {
dev_err(dev, "Incompatible firmware signature %x", sig); dev_err(dev, "Incompatible firmware signature %x", sig);
......
...@@ -59,6 +59,7 @@ static_assert(sizeof(struct ionic_dev_getattr_cmd) == 64); ...@@ -59,6 +59,7 @@ static_assert(sizeof(struct ionic_dev_getattr_cmd) == 64);
static_assert(sizeof(struct ionic_dev_getattr_comp) == 16); static_assert(sizeof(struct ionic_dev_getattr_comp) == 16);
static_assert(sizeof(struct ionic_dev_setattr_cmd) == 64); static_assert(sizeof(struct ionic_dev_setattr_cmd) == 64);
static_assert(sizeof(struct ionic_dev_setattr_comp) == 16); static_assert(sizeof(struct ionic_dev_setattr_comp) == 16);
static_assert(sizeof(struct ionic_lif_setphc_cmd) == 64);
/* Port commands */ /* Port commands */
static_assert(sizeof(struct ionic_port_identify_cmd) == 64); static_assert(sizeof(struct ionic_port_identify_cmd) == 64);
...@@ -135,6 +136,7 @@ struct ionic_devinfo { ...@@ -135,6 +136,7 @@ struct ionic_devinfo {
struct ionic_dev { struct ionic_dev {
union ionic_dev_info_regs __iomem *dev_info_regs; union ionic_dev_info_regs __iomem *dev_info_regs;
union ionic_dev_cmd_regs __iomem *dev_cmd_regs; union ionic_dev_cmd_regs __iomem *dev_cmd_regs;
struct ionic_hwstamp_regs __iomem *hwstamp_regs;
atomic_long_t last_check_time; atomic_long_t last_check_time;
unsigned long last_hb_time; unsigned long last_hb_time;
...@@ -218,6 +220,7 @@ struct ionic_queue { ...@@ -218,6 +220,7 @@ struct ionic_queue {
unsigned int index; unsigned int index;
unsigned int num_descs; unsigned int num_descs;
unsigned int max_sg_elems; unsigned int max_sg_elems;
u64 features;
u64 dbell_count; u64 dbell_count;
u64 stop; u64 stop;
u64 wake; u64 wake;
......
...@@ -849,6 +849,98 @@ static int ionic_get_module_eeprom(struct net_device *netdev, ...@@ -849,6 +849,98 @@ static int ionic_get_module_eeprom(struct net_device *netdev,
return 0; return 0;
} }
static int ionic_get_ts_info(struct net_device *netdev,
struct ethtool_ts_info *info)
{
struct ionic_lif *lif = netdev_priv(netdev);
struct ionic *ionic = lif->ionic;
__le64 mask;
if (!lif->phc || !lif->phc->ptp)
return ethtool_op_get_ts_info(netdev, info);
info->phc_index = ptp_clock_index(lif->phc->ptp);
info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE |
SOF_TIMESTAMPING_RX_SOFTWARE |
SOF_TIMESTAMPING_SOFTWARE |
SOF_TIMESTAMPING_TX_HARDWARE |
SOF_TIMESTAMPING_RX_HARDWARE |
SOF_TIMESTAMPING_RAW_HARDWARE;
/* tx modes */
info->tx_types = BIT(HWTSTAMP_TX_OFF) |
BIT(HWTSTAMP_TX_ON);
mask = cpu_to_le64(BIT_ULL(IONIC_TXSTAMP_ONESTEP_SYNC));
if (ionic->ident.lif.eth.hwstamp_tx_modes & mask)
info->tx_types |= BIT(HWTSTAMP_TX_ONESTEP_SYNC);
mask = cpu_to_le64(BIT_ULL(IONIC_TXSTAMP_ONESTEP_P2P));
if (ionic->ident.lif.eth.hwstamp_tx_modes & mask)
info->tx_types |= BIT(HWTSTAMP_TX_ONESTEP_P2P);
/* rx filters */
info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) |
BIT(HWTSTAMP_FILTER_ALL);
mask = cpu_to_le64(IONIC_PKT_CLS_NTP_ALL);
if ((ionic->ident.lif.eth.hwstamp_rx_filters & mask) == mask)
info->rx_filters |= HWTSTAMP_FILTER_NTP_ALL;
mask = cpu_to_le64(IONIC_PKT_CLS_PTP1_SYNC);
if ((ionic->ident.lif.eth.hwstamp_rx_filters & mask) == mask)
info->rx_filters |= HWTSTAMP_FILTER_PTP_V1_L4_SYNC;
mask = cpu_to_le64(IONIC_PKT_CLS_PTP1_DREQ);
if ((ionic->ident.lif.eth.hwstamp_rx_filters & mask) == mask)
info->rx_filters |= HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ;
mask = cpu_to_le64(IONIC_PKT_CLS_PTP1_ALL);
if ((ionic->ident.lif.eth.hwstamp_rx_filters & mask) == mask)
info->rx_filters |= HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
mask = cpu_to_le64(IONIC_PKT_CLS_PTP2_L4_SYNC);
if ((ionic->ident.lif.eth.hwstamp_rx_filters & mask) == mask)
info->rx_filters |= HWTSTAMP_FILTER_PTP_V2_L4_SYNC;
mask = cpu_to_le64(IONIC_PKT_CLS_PTP2_L4_DREQ);
if ((ionic->ident.lif.eth.hwstamp_rx_filters & mask) == mask)
info->rx_filters |= HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ;
mask = cpu_to_le64(IONIC_PKT_CLS_PTP2_L4_ALL);
if ((ionic->ident.lif.eth.hwstamp_rx_filters & mask) == mask)
info->rx_filters |= HWTSTAMP_FILTER_PTP_V2_L4_EVENT;
mask = cpu_to_le64(IONIC_PKT_CLS_PTP2_L2_SYNC);
if ((ionic->ident.lif.eth.hwstamp_rx_filters & mask) == mask)
info->rx_filters |= HWTSTAMP_FILTER_PTP_V2_L2_SYNC;
mask = cpu_to_le64(IONIC_PKT_CLS_PTP2_L2_DREQ);
if ((ionic->ident.lif.eth.hwstamp_rx_filters & mask) == mask)
info->rx_filters |= HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ;
mask = cpu_to_le64(IONIC_PKT_CLS_PTP2_L2_ALL);
if ((ionic->ident.lif.eth.hwstamp_rx_filters & mask) == mask)
info->rx_filters |= HWTSTAMP_FILTER_PTP_V2_L2_EVENT;
mask = cpu_to_le64(IONIC_PKT_CLS_PTP2_SYNC);
if ((ionic->ident.lif.eth.hwstamp_rx_filters & mask) == mask)
info->rx_filters |= HWTSTAMP_FILTER_PTP_V2_SYNC;
mask = cpu_to_le64(IONIC_PKT_CLS_PTP2_DREQ);
if ((ionic->ident.lif.eth.hwstamp_rx_filters & mask) == mask)
info->rx_filters |= HWTSTAMP_FILTER_PTP_V2_DELAY_REQ;
mask = cpu_to_le64(IONIC_PKT_CLS_PTP2_ALL);
if ((ionic->ident.lif.eth.hwstamp_rx_filters & mask) == mask)
info->rx_filters |= HWTSTAMP_FILTER_PTP_V2_EVENT;
return 0;
}
static int ionic_nway_reset(struct net_device *netdev) static int ionic_nway_reset(struct net_device *netdev)
{ {
struct ionic_lif *lif = netdev_priv(netdev); struct ionic_lif *lif = netdev_priv(netdev);
...@@ -906,6 +998,7 @@ static const struct ethtool_ops ionic_ethtool_ops = { ...@@ -906,6 +998,7 @@ static const struct ethtool_ops ionic_ethtool_ops = {
.set_pauseparam = ionic_set_pauseparam, .set_pauseparam = ionic_set_pauseparam,
.get_fecparam = ionic_get_fecparam, .get_fecparam = ionic_get_fecparam,
.set_fecparam = ionic_set_fecparam, .set_fecparam = ionic_set_fecparam,
.get_ts_info = ionic_get_ts_info,
.nway_reset = ionic_nway_reset, .nway_reset = ionic_nway_reset,
}; };
......
...@@ -4,6 +4,9 @@ ...@@ -4,6 +4,9 @@
#ifndef _IONIC_LIF_H_ #ifndef _IONIC_LIF_H_
#define _IONIC_LIF_H_ #define _IONIC_LIF_H_
#include <linux/ptp_clock_kernel.h>
#include <linux/timecounter.h>
#include <uapi/linux/net_tstamp.h>
#include <linux/dim.h> #include <linux/dim.h>
#include <linux/pci.h> #include <linux/pci.h>
#include "ionic_rx_filter.h" #include "ionic_rx_filter.h"
...@@ -36,6 +39,8 @@ struct ionic_tx_stats { ...@@ -36,6 +39,8 @@ struct ionic_tx_stats {
u64 crc32_csum; u64 crc32_csum;
u64 sg_cntr[IONIC_MAX_NUM_SG_CNTR]; u64 sg_cntr[IONIC_MAX_NUM_SG_CNTR];
u64 dma_map_err; u64 dma_map_err;
u64 hwstamp_valid;
u64 hwstamp_invalid;
}; };
struct ionic_rx_stats { struct ionic_rx_stats {
...@@ -49,6 +54,8 @@ struct ionic_rx_stats { ...@@ -49,6 +54,8 @@ struct ionic_rx_stats {
u64 csum_error; u64 csum_error;
u64 dma_map_err; u64 dma_map_err;
u64 alloc_err; u64 alloc_err;
u64 hwstamp_valid;
u64 hwstamp_invalid;
}; };
#define IONIC_QCQ_F_INITED BIT(0) #define IONIC_QCQ_F_INITED BIT(0)
...@@ -125,6 +132,10 @@ struct ionic_lif_sw_stats { ...@@ -125,6 +132,10 @@ struct ionic_lif_sw_stats {
u64 rx_csum_none; u64 rx_csum_none;
u64 rx_csum_complete; u64 rx_csum_complete;
u64 rx_csum_error; u64 rx_csum_error;
u64 tx_hwstamp_valid;
u64 tx_hwstamp_invalid;
u64 rx_hwstamp_valid;
u64 rx_hwstamp_invalid;
u64 hw_tx_dropped; u64 hw_tx_dropped;
u64 hw_rx_dropped; u64 hw_rx_dropped;
u64 hw_rx_over_errors; u64 hw_rx_over_errors;
...@@ -158,6 +169,8 @@ struct ionic_qtype_info { ...@@ -158,6 +169,8 @@ struct ionic_qtype_info {
u16 sg_desc_stride; u16 sg_desc_stride;
}; };
struct ionic_phc;
#define IONIC_LIF_NAME_MAX_SZ 32 #define IONIC_LIF_NAME_MAX_SZ 32
struct ionic_lif { struct ionic_lif {
struct net_device *netdev; struct net_device *netdev;
...@@ -170,8 +183,10 @@ struct ionic_lif { ...@@ -170,8 +183,10 @@ struct ionic_lif {
struct ionic_qcq *adminqcq; struct ionic_qcq *adminqcq;
struct ionic_qcq *notifyqcq; struct ionic_qcq *notifyqcq;
struct ionic_qcq **txqcqs; struct ionic_qcq **txqcqs;
struct ionic_qcq *hwstamp_txq;
struct ionic_tx_stats *txqstats; struct ionic_tx_stats *txqstats;
struct ionic_qcq **rxqcqs; struct ionic_qcq **rxqcqs;
struct ionic_qcq *hwstamp_rxq;
struct ionic_rx_stats *rxqstats; struct ionic_rx_stats *rxqstats;
struct ionic_deferred deferred; struct ionic_deferred deferred;
struct work_struct tx_timeout_work; struct work_struct tx_timeout_work;
...@@ -183,6 +198,7 @@ struct ionic_lif { ...@@ -183,6 +198,7 @@ struct ionic_lif {
unsigned int ntxq_descs; unsigned int ntxq_descs;
unsigned int nrxq_descs; unsigned int nrxq_descs;
u32 rx_copybreak; u32 rx_copybreak;
u64 rxq_features;
unsigned int rx_mode; unsigned int rx_mode;
u64 hw_features; u64 hw_features;
bool registered; bool registered;
...@@ -213,14 +229,35 @@ struct ionic_lif { ...@@ -213,14 +229,35 @@ struct ionic_lif {
unsigned long *dbid_inuse; unsigned long *dbid_inuse;
unsigned int dbid_count; unsigned int dbid_count;
struct ionic_phc *phc;
struct dentry *dentry; struct dentry *dentry;
}; };
struct ionic_phc {
spinlock_t lock; /* lock for cc and tc */
struct cyclecounter cc;
struct timecounter tc;
struct mutex config_lock; /* lock for ts_config */
struct hwtstamp_config ts_config;
u64 ts_config_rx_filt;
u32 ts_config_tx_mode;
u32 init_cc_mult;
long aux_work_delay;
struct ptp_clock_info ptp_info;
struct ptp_clock *ptp;
struct ionic_lif *lif;
};
struct ionic_queue_params { struct ionic_queue_params {
unsigned int nxqs; unsigned int nxqs;
unsigned int ntxq_descs; unsigned int ntxq_descs;
unsigned int nrxq_descs; unsigned int nrxq_descs;
unsigned int intr_split; unsigned int intr_split;
u64 rxq_features;
}; };
static inline void ionic_init_queue_params(struct ionic_lif *lif, static inline void ionic_init_queue_params(struct ionic_lif *lif,
...@@ -230,6 +267,7 @@ static inline void ionic_init_queue_params(struct ionic_lif *lif, ...@@ -230,6 +267,7 @@ static inline void ionic_init_queue_params(struct ionic_lif *lif,
qparam->ntxq_descs = lif->ntxq_descs; qparam->ntxq_descs = lif->ntxq_descs;
qparam->nrxq_descs = lif->nrxq_descs; qparam->nrxq_descs = lif->nrxq_descs;
qparam->intr_split = test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state); qparam->intr_split = test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state);
qparam->rxq_features = lif->rxq_features;
} }
static inline u32 ionic_coal_usec_to_hw(struct ionic *ionic, u32 usecs) static inline u32 ionic_coal_usec_to_hw(struct ionic *ionic, u32 usecs)
...@@ -262,6 +300,43 @@ void ionic_lif_unregister(struct ionic_lif *lif); ...@@ -262,6 +300,43 @@ void ionic_lif_unregister(struct ionic_lif *lif);
int ionic_lif_identify(struct ionic *ionic, u8 lif_type, int ionic_lif_identify(struct ionic *ionic, u8 lif_type,
union ionic_lif_identity *lif_ident); union ionic_lif_identity *lif_ident);
int ionic_lif_size(struct ionic *ionic); int ionic_lif_size(struct ionic *ionic);
#if IS_ENABLED(CONFIG_PTP_1588_CLOCK)
int ionic_lif_hwstamp_set(struct ionic_lif *lif, struct ifreq *ifr);
int ionic_lif_hwstamp_get(struct ionic_lif *lif, struct ifreq *ifr);
ktime_t ionic_lif_phc_ktime(struct ionic_lif *lif, u64 counter);
void ionic_lif_register_phc(struct ionic_lif *lif);
void ionic_lif_unregister_phc(struct ionic_lif *lif);
void ionic_lif_alloc_phc(struct ionic_lif *lif);
void ionic_lif_free_phc(struct ionic_lif *lif);
#else
static inline int ionic_lif_hwstamp_set(struct ionic_lif *lif, struct ifreq *ifr)
{
return -EOPNOTSUPP;
}
static inline int ionic_lif_hwstamp_get(struct ionic_lif *lif, struct ifreq *ifr)
{
return -EOPNOTSUPP;
}
static inline ktime_t ionic_lif_phc_ktime(struct ionic_lif *lif, u64 counter)
{
return ns_to_ktime(0);
}
static inline void ionic_lif_register_phc(struct ionic_lif *lif) {}
static inline void ionic_lif_unregister_phc(struct ionic_lif *lif) {}
static inline void ionic_lif_alloc_phc(struct ionic_lif *lif) {}
static inline void ionic_lif_free_phc(struct ionic_lif *lif) {}
#endif
int ionic_lif_create_hwstamp_txq(struct ionic_lif *lif);
int ionic_lif_create_hwstamp_rxq(struct ionic_lif *lif);
int ionic_lif_config_hwstamp_rxq_all(struct ionic_lif *lif, bool rx_all);
int ionic_lif_set_hwstamp_txmode(struct ionic_lif *lif, u16 txstamp_mode);
int ionic_lif_set_hwstamp_rxfilt(struct ionic_lif *lif, u64 pkt_class);
int ionic_lif_rss_config(struct ionic_lif *lif, u16 types, int ionic_lif_rss_config(struct ionic_lif *lif, u16 types,
const u8 *key, const u32 *indir); const u8 *key, const u32 *indir);
int ionic_reconfigure_queues(struct ionic_lif *lif, int ionic_reconfigure_queues(struct ionic_lif *lif,
......
...@@ -148,6 +148,8 @@ static const char *ionic_opcode_to_str(enum ionic_cmd_opcode opcode) ...@@ -148,6 +148,8 @@ static const char *ionic_opcode_to_str(enum ionic_cmd_opcode opcode)
return "IONIC_CMD_LIF_SETATTR"; return "IONIC_CMD_LIF_SETATTR";
case IONIC_CMD_LIF_GETATTR: case IONIC_CMD_LIF_GETATTR:
return "IONIC_CMD_LIF_GETATTR"; return "IONIC_CMD_LIF_GETATTR";
case IONIC_CMD_LIF_SETPHC:
return "IONIC_CMD_LIF_SETPHC";
case IONIC_CMD_RX_MODE_SET: case IONIC_CMD_RX_MODE_SET:
return "IONIC_CMD_RX_MODE_SET"; return "IONIC_CMD_RX_MODE_SET";
case IONIC_CMD_RX_FILTER_ADD: case IONIC_CMD_RX_FILTER_ADD:
...@@ -256,7 +258,7 @@ static void ionic_adminq_cb(struct ionic_queue *q, ...@@ -256,7 +258,7 @@ static void ionic_adminq_cb(struct ionic_queue *q,
complete_all(&ctx->work); complete_all(&ctx->work);
} }
static int ionic_adminq_post(struct ionic_lif *lif, struct ionic_admin_ctx *ctx) int ionic_adminq_post(struct ionic_lif *lif, struct ionic_admin_ctx *ctx)
{ {
struct ionic_desc_info *desc_info; struct ionic_desc_info *desc_info;
unsigned long irqflags; unsigned long irqflags;
...@@ -295,14 +297,12 @@ static int ionic_adminq_post(struct ionic_lif *lif, struct ionic_admin_ctx *ctx) ...@@ -295,14 +297,12 @@ static int ionic_adminq_post(struct ionic_lif *lif, struct ionic_admin_ctx *ctx)
return err; return err;
} }
int ionic_adminq_post_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx) int ionic_adminq_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx, int err)
{ {
struct net_device *netdev = lif->netdev; struct net_device *netdev = lif->netdev;
unsigned long remaining; unsigned long remaining;
const char *name; const char *name;
int err;
err = ionic_adminq_post(lif, ctx);
if (err) { if (err) {
if (!test_bit(IONIC_LIF_F_FW_RESET, lif->state)) { if (!test_bit(IONIC_LIF_F_FW_RESET, lif->state)) {
name = ionic_opcode_to_str(ctx->cmd.cmd.opcode); name = ionic_opcode_to_str(ctx->cmd.cmd.opcode);
...@@ -317,6 +317,15 @@ int ionic_adminq_post_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx) ...@@ -317,6 +317,15 @@ int ionic_adminq_post_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx)
return ionic_adminq_check_err(lif, ctx, (remaining == 0)); return ionic_adminq_check_err(lif, ctx, (remaining == 0));
} }
int ionic_adminq_post_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx)
{
int err;
err = ionic_adminq_post(lif, ctx);
return ionic_adminq_wait(lif, ctx, err);
}
static void ionic_dev_cmd_clean(struct ionic *ionic) static void ionic_dev_cmd_clean(struct ionic *ionic)
{ {
union __iomem ionic_dev_cmd_regs *regs = ionic->idev.dev_cmd_regs; union __iomem ionic_dev_cmd_regs *regs = ionic->idev.dev_cmd_regs;
......
This diff is collapsed.
...@@ -140,6 +140,9 @@ int ionic_rx_filter_save(struct ionic_lif *lif, u32 flow_id, u16 rxq_index, ...@@ -140,6 +140,9 @@ int ionic_rx_filter_save(struct ionic_lif *lif, u32 flow_id, u16 rxq_index,
case IONIC_RX_FILTER_MATCH_MAC_VLAN: case IONIC_RX_FILTER_MATCH_MAC_VLAN:
key = le16_to_cpu(ac->mac_vlan.vlan); key = le16_to_cpu(ac->mac_vlan.vlan);
break; break;
case IONIC_RX_FILTER_STEER_PKTCLASS:
key = 0;
break;
default: default:
return -EINVAL; return -EINVAL;
} }
...@@ -210,3 +213,21 @@ struct ionic_rx_filter *ionic_rx_filter_by_addr(struct ionic_lif *lif, ...@@ -210,3 +213,21 @@ struct ionic_rx_filter *ionic_rx_filter_by_addr(struct ionic_lif *lif,
return NULL; return NULL;
} }
struct ionic_rx_filter *ionic_rx_filter_rxsteer(struct ionic_lif *lif)
{
struct ionic_rx_filter *f;
struct hlist_head *head;
unsigned int key;
key = hash_32(0, IONIC_RX_FILTER_HASH_BITS);
head = &lif->rx_filters.by_hash[key];
hlist_for_each_entry(f, head, by_hash) {
if (le16_to_cpu(f->cmd.match) != IONIC_RX_FILTER_STEER_PKTCLASS)
continue;
return f;
}
return NULL;
}
...@@ -31,5 +31,6 @@ int ionic_rx_filter_save(struct ionic_lif *lif, u32 flow_id, u16 rxq_index, ...@@ -31,5 +31,6 @@ int ionic_rx_filter_save(struct ionic_lif *lif, u32 flow_id, u16 rxq_index,
u32 hash, struct ionic_admin_ctx *ctx); u32 hash, struct ionic_admin_ctx *ctx);
struct ionic_rx_filter *ionic_rx_filter_by_vlan(struct ionic_lif *lif, u16 vid); struct ionic_rx_filter *ionic_rx_filter_by_vlan(struct ionic_lif *lif, u16 vid);
struct ionic_rx_filter *ionic_rx_filter_by_addr(struct ionic_lif *lif, const u8 *addr); struct ionic_rx_filter *ionic_rx_filter_by_addr(struct ionic_lif *lif, const u8 *addr);
struct ionic_rx_filter *ionic_rx_filter_rxsteer(struct ionic_lif *lif);
#endif /* _IONIC_RX_FILTER_H_ */ #endif /* _IONIC_RX_FILTER_H_ */
...@@ -130,6 +130,8 @@ static const struct ionic_stat_desc ionic_tx_stats_desc[] = { ...@@ -130,6 +130,8 @@ static const struct ionic_stat_desc ionic_tx_stats_desc[] = {
IONIC_TX_STAT_DESC(frags), IONIC_TX_STAT_DESC(frags),
IONIC_TX_STAT_DESC(tso), IONIC_TX_STAT_DESC(tso),
IONIC_TX_STAT_DESC(tso_bytes), IONIC_TX_STAT_DESC(tso_bytes),
IONIC_TX_STAT_DESC(hwstamp_valid),
IONIC_TX_STAT_DESC(hwstamp_invalid),
IONIC_TX_STAT_DESC(csum_none), IONIC_TX_STAT_DESC(csum_none),
IONIC_TX_STAT_DESC(csum), IONIC_TX_STAT_DESC(csum),
IONIC_TX_STAT_DESC(vlan_inserted), IONIC_TX_STAT_DESC(vlan_inserted),
...@@ -143,6 +145,8 @@ static const struct ionic_stat_desc ionic_rx_stats_desc[] = { ...@@ -143,6 +145,8 @@ static const struct ionic_stat_desc ionic_rx_stats_desc[] = {
IONIC_RX_STAT_DESC(csum_none), IONIC_RX_STAT_DESC(csum_none),
IONIC_RX_STAT_DESC(csum_complete), IONIC_RX_STAT_DESC(csum_complete),
IONIC_RX_STAT_DESC(csum_error), IONIC_RX_STAT_DESC(csum_error),
IONIC_RX_STAT_DESC(hwstamp_valid),
IONIC_RX_STAT_DESC(hwstamp_invalid),
IONIC_RX_STAT_DESC(dropped), IONIC_RX_STAT_DESC(dropped),
IONIC_RX_STAT_DESC(vlan_stripped), IONIC_RX_STAT_DESC(vlan_stripped),
}; };
...@@ -188,6 +192,8 @@ static void ionic_add_lif_txq_stats(struct ionic_lif *lif, int q_num, ...@@ -188,6 +192,8 @@ static void ionic_add_lif_txq_stats(struct ionic_lif *lif, int q_num,
stats->tx_tso_bytes += txstats->tso_bytes; stats->tx_tso_bytes += txstats->tso_bytes;
stats->tx_csum_none += txstats->csum_none; stats->tx_csum_none += txstats->csum_none;
stats->tx_csum += txstats->csum; stats->tx_csum += txstats->csum;
stats->tx_hwstamp_valid += txstats->hwstamp_valid;
stats->tx_hwstamp_invalid += txstats->hwstamp_invalid;
} }
static void ionic_add_lif_rxq_stats(struct ionic_lif *lif, int q_num, static void ionic_add_lif_rxq_stats(struct ionic_lif *lif, int q_num,
...@@ -200,6 +206,8 @@ static void ionic_add_lif_rxq_stats(struct ionic_lif *lif, int q_num, ...@@ -200,6 +206,8 @@ static void ionic_add_lif_rxq_stats(struct ionic_lif *lif, int q_num,
stats->rx_csum_none += rxstats->csum_none; stats->rx_csum_none += rxstats->csum_none;
stats->rx_csum_complete += rxstats->csum_complete; stats->rx_csum_complete += rxstats->csum_complete;
stats->rx_csum_error += rxstats->csum_error; stats->rx_csum_error += rxstats->csum_error;
stats->rx_hwstamp_valid += rxstats->hwstamp_valid;
stats->rx_hwstamp_invalid += rxstats->hwstamp_invalid;
} }
static void ionic_get_lif_stats(struct ionic_lif *lif, static void ionic_get_lif_stats(struct ionic_lif *lif,
...@@ -215,6 +223,12 @@ static void ionic_get_lif_stats(struct ionic_lif *lif, ...@@ -215,6 +223,12 @@ static void ionic_get_lif_stats(struct ionic_lif *lif,
ionic_add_lif_rxq_stats(lif, q_num, stats); ionic_add_lif_rxq_stats(lif, q_num, stats);
} }
if (lif->hwstamp_txq)
ionic_add_lif_txq_stats(lif, lif->hwstamp_txq->q.index, stats);
if (lif->hwstamp_rxq)
ionic_add_lif_rxq_stats(lif, lif->hwstamp_rxq->q.index, stats);
ionic_get_stats64(lif->netdev, &ns); ionic_get_stats64(lif->netdev, &ns);
stats->hw_tx_dropped = ns.tx_dropped; stats->hw_tx_dropped = ns.tx_dropped;
stats->hw_rx_dropped = ns.rx_dropped; stats->hw_rx_dropped = ns.rx_dropped;
...@@ -227,14 +241,18 @@ static u64 ionic_sw_stats_get_count(struct ionic_lif *lif) ...@@ -227,14 +241,18 @@ static u64 ionic_sw_stats_get_count(struct ionic_lif *lif)
{ {
u64 total = 0, tx_queues = MAX_Q(lif), rx_queues = MAX_Q(lif); u64 total = 0, tx_queues = MAX_Q(lif), rx_queues = MAX_Q(lif);
/* lif stats */ if (lif->hwstamp_txq)
tx_queues += 1;
if (lif->hwstamp_rxq)
rx_queues += 1;
total += IONIC_NUM_LIF_STATS; total += IONIC_NUM_LIF_STATS;
total += IONIC_NUM_PORT_STATS;
total += tx_queues * IONIC_NUM_TX_STATS; total += tx_queues * IONIC_NUM_TX_STATS;
total += rx_queues * IONIC_NUM_RX_STATS; total += rx_queues * IONIC_NUM_RX_STATS;
/* port stats */
total += IONIC_NUM_PORT_STATS;
if (test_bit(IONIC_LIF_F_UP, lif->state) && if (test_bit(IONIC_LIF_F_UP, lif->state) &&
test_bit(IONIC_LIF_F_SW_DEBUG_STATS, lif->state)) { test_bit(IONIC_LIF_F_SW_DEBUG_STATS, lif->state)) {
/* tx debug stats */ /* tx debug stats */
...@@ -318,8 +336,14 @@ static void ionic_sw_stats_get_strings(struct ionic_lif *lif, u8 **buf) ...@@ -318,8 +336,14 @@ static void ionic_sw_stats_get_strings(struct ionic_lif *lif, u8 **buf)
for (q_num = 0; q_num < MAX_Q(lif); q_num++) for (q_num = 0; q_num < MAX_Q(lif); q_num++)
ionic_sw_stats_get_tx_strings(lif, buf, q_num); ionic_sw_stats_get_tx_strings(lif, buf, q_num);
if (lif->hwstamp_txq)
ionic_sw_stats_get_tx_strings(lif, buf, lif->hwstamp_txq->q.index);
for (q_num = 0; q_num < MAX_Q(lif); q_num++) for (q_num = 0; q_num < MAX_Q(lif); q_num++)
ionic_sw_stats_get_rx_strings(lif, buf, q_num); ionic_sw_stats_get_rx_strings(lif, buf, q_num);
if (lif->hwstamp_rxq)
ionic_sw_stats_get_rx_strings(lif, buf, lif->hwstamp_rxq->q.index);
} }
static void ionic_sw_stats_get_txq_values(struct ionic_lif *lif, u64 **buf, static void ionic_sw_stats_get_txq_values(struct ionic_lif *lif, u64 **buf,
...@@ -434,8 +458,14 @@ static void ionic_sw_stats_get_values(struct ionic_lif *lif, u64 **buf) ...@@ -434,8 +458,14 @@ static void ionic_sw_stats_get_values(struct ionic_lif *lif, u64 **buf)
for (q_num = 0; q_num < MAX_Q(lif); q_num++) for (q_num = 0; q_num < MAX_Q(lif); q_num++)
ionic_sw_stats_get_txq_values(lif, buf, q_num); ionic_sw_stats_get_txq_values(lif, buf, q_num);
if (lif->hwstamp_txq)
ionic_sw_stats_get_txq_values(lif, buf, lif->hwstamp_txq->q.index);
for (q_num = 0; q_num < MAX_Q(lif); q_num++) for (q_num = 0; q_num < MAX_Q(lif); q_num++)
ionic_sw_stats_get_rxq_values(lif, buf, q_num); ionic_sw_stats_get_rxq_values(lif, buf, q_num);
if (lif->hwstamp_rxq)
ionic_sw_stats_get_rxq_values(lif, buf, lif->hwstamp_rxq->q.index);
} }
const struct ionic_stats_group_intf ionic_stats_groups[] = { const struct ionic_stats_group_intf ionic_stats_groups[] = {
......
...@@ -11,8 +11,6 @@ ...@@ -11,8 +11,6 @@
#include "ionic_txrx.h" #include "ionic_txrx.h"
static bool ionic_tx_service(struct ionic_cq *cq, struct ionic_cq_info *cq_info);
static inline void ionic_txq_post(struct ionic_queue *q, bool ring_dbell, static inline void ionic_txq_post(struct ionic_queue *q, bool ring_dbell,
ionic_desc_cb cb_func, void *cb_arg) ionic_desc_cb cb_func, void *cb_arg)
{ {
...@@ -229,12 +227,14 @@ static void ionic_rx_clean(struct ionic_queue *q, ...@@ -229,12 +227,14 @@ static void ionic_rx_clean(struct ionic_queue *q,
struct ionic_cq_info *cq_info, struct ionic_cq_info *cq_info,
void *cb_arg) void *cb_arg)
{ {
struct ionic_rxq_comp *comp = cq_info->rxcq;
struct net_device *netdev = q->lif->netdev; struct net_device *netdev = q->lif->netdev;
struct ionic_qcq *qcq = q_to_qcq(q); struct ionic_qcq *qcq = q_to_qcq(q);
struct ionic_rx_stats *stats; struct ionic_rx_stats *stats;
struct ionic_rxq_comp *comp;
struct sk_buff *skb; struct sk_buff *skb;
comp = cq_info->cq_desc + qcq->cq.desc_size - sizeof(*comp);
stats = q_to_rx_stats(q); stats = q_to_rx_stats(q);
if (comp->status) { if (comp->status) {
...@@ -296,17 +296,39 @@ static void ionic_rx_clean(struct ionic_queue *q, ...@@ -296,17 +296,39 @@ static void ionic_rx_clean(struct ionic_queue *q,
stats->vlan_stripped++; stats->vlan_stripped++;
} }
if (unlikely(q->features & IONIC_RXQ_F_HWSTAMP)) {
__le64 *cq_desc_hwstamp;
u64 hwstamp;
cq_desc_hwstamp =
cq_info->cq_desc +
qcq->cq.desc_size -
sizeof(struct ionic_rxq_comp) -
IONIC_HWSTAMP_CQ_NEGOFFSET;
hwstamp = le64_to_cpu(*cq_desc_hwstamp);
if (hwstamp != IONIC_HWSTAMP_INVALID) {
skb_hwtstamps(skb)->hwtstamp = ionic_lif_phc_ktime(q->lif, hwstamp);
stats->hwstamp_valid++;
} else {
stats->hwstamp_invalid++;
}
}
if (le16_to_cpu(comp->len) <= q->lif->rx_copybreak) if (le16_to_cpu(comp->len) <= q->lif->rx_copybreak)
napi_gro_receive(&qcq->napi, skb); napi_gro_receive(&qcq->napi, skb);
else else
napi_gro_frags(&qcq->napi); napi_gro_frags(&qcq->napi);
} }
static bool ionic_rx_service(struct ionic_cq *cq, struct ionic_cq_info *cq_info) bool ionic_rx_service(struct ionic_cq *cq, struct ionic_cq_info *cq_info)
{ {
struct ionic_rxq_comp *comp = cq_info->rxcq;
struct ionic_queue *q = cq->bound_q; struct ionic_queue *q = cq->bound_q;
struct ionic_desc_info *desc_info; struct ionic_desc_info *desc_info;
struct ionic_rxq_comp *comp;
comp = cq_info->cq_desc + cq->desc_size - sizeof(*comp);
if (!color_match(comp->pkt_type_color, cq->done_color)) if (!color_match(comp->pkt_type_color, cq->done_color))
return false; return false;
...@@ -661,9 +683,11 @@ static void ionic_tx_clean(struct ionic_queue *q, ...@@ -661,9 +683,11 @@ static void ionic_tx_clean(struct ionic_queue *q,
{ {
struct ionic_buf_info *buf_info = desc_info->bufs; struct ionic_buf_info *buf_info = desc_info->bufs;
struct ionic_tx_stats *stats = q_to_tx_stats(q); struct ionic_tx_stats *stats = q_to_tx_stats(q);
struct ionic_qcq *qcq = q_to_qcq(q);
struct sk_buff *skb = cb_arg;
struct device *dev = q->dev; struct device *dev = q->dev;
u16 queue_index;
unsigned int i; unsigned int i;
u16 qi;
if (desc_info->nbufs) { if (desc_info->nbufs) {
dma_unmap_single(dev, (dma_addr_t)buf_info->dma_addr, dma_unmap_single(dev, (dma_addr_t)buf_info->dma_addr,
...@@ -674,13 +698,39 @@ static void ionic_tx_clean(struct ionic_queue *q, ...@@ -674,13 +698,39 @@ static void ionic_tx_clean(struct ionic_queue *q,
buf_info->len, DMA_TO_DEVICE); buf_info->len, DMA_TO_DEVICE);
} }
if (cb_arg) { if (!skb)
struct sk_buff *skb = cb_arg; return;
queue_index = skb_get_queue_mapping(skb); qi = skb_get_queue_mapping(skb);
if (unlikely(__netif_subqueue_stopped(q->lif->netdev,
queue_index))) { if (unlikely(q->features & IONIC_TXQ_F_HWSTAMP)) {
netif_wake_subqueue(q->lif->netdev, queue_index); if (cq_info) {
struct skb_shared_hwtstamps hwts = {};
__le64 *cq_desc_hwstamp;
u64 hwstamp;
cq_desc_hwstamp =
cq_info->cq_desc +
qcq->cq.desc_size -
sizeof(struct ionic_txq_comp) -
IONIC_HWSTAMP_CQ_NEGOFFSET;
hwstamp = le64_to_cpu(*cq_desc_hwstamp);
if (hwstamp != IONIC_HWSTAMP_INVALID) {
hwts.hwtstamp = ionic_lif_phc_ktime(q->lif, hwstamp);
skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
skb_tstamp_tx(skb, &hwts);
stats->hwstamp_valid++;
} else {
stats->hwstamp_invalid++;
}
}
} else if (unlikely(__netif_subqueue_stopped(q->lif->netdev, qi))) {
netif_wake_subqueue(q->lif->netdev, qi);
q->wake++; q->wake++;
} }
...@@ -688,18 +738,19 @@ static void ionic_tx_clean(struct ionic_queue *q, ...@@ -688,18 +738,19 @@ static void ionic_tx_clean(struct ionic_queue *q,
stats->clean++; stats->clean++;
dev_consume_skb_any(skb); dev_consume_skb_any(skb);
}
} }
static bool ionic_tx_service(struct ionic_cq *cq, struct ionic_cq_info *cq_info) bool ionic_tx_service(struct ionic_cq *cq, struct ionic_cq_info *cq_info)
{ {
struct ionic_txq_comp *comp = cq_info->txcq;
struct ionic_queue *q = cq->bound_q; struct ionic_queue *q = cq->bound_q;
struct ionic_desc_info *desc_info; struct ionic_desc_info *desc_info;
struct ionic_txq_comp *comp;
int bytes = 0; int bytes = 0;
int pkts = 0; int pkts = 0;
u16 index; u16 index;
comp = cq_info->cq_desc + cq->desc_size - sizeof(*comp);
if (!color_match(comp->color, cq->done_color)) if (!color_match(comp->color, cq->done_color))
return false; return false;
...@@ -720,7 +771,7 @@ static bool ionic_tx_service(struct ionic_cq *cq, struct ionic_cq_info *cq_info) ...@@ -720,7 +771,7 @@ static bool ionic_tx_service(struct ionic_cq *cq, struct ionic_cq_info *cq_info)
desc_info->cb_arg = NULL; desc_info->cb_arg = NULL;
} while (index != le16_to_cpu(comp->comp_index)); } while (index != le16_to_cpu(comp->comp_index));
if (pkts && bytes) if (pkts && bytes && !unlikely(q->features & IONIC_TXQ_F_HWSTAMP))
netdev_tx_completed_queue(q_to_ndq(q), pkts, bytes); netdev_tx_completed_queue(q_to_ndq(q), pkts, bytes);
return true; return true;
...@@ -758,7 +809,7 @@ void ionic_tx_empty(struct ionic_queue *q) ...@@ -758,7 +809,7 @@ void ionic_tx_empty(struct ionic_queue *q)
desc_info->cb_arg = NULL; desc_info->cb_arg = NULL;
} }
if (pkts && bytes) if (pkts && bytes && !unlikely(q->features & IONIC_TXQ_F_HWSTAMP))
netdev_tx_completed_queue(q_to_ndq(q), pkts, bytes); netdev_tx_completed_queue(q_to_ndq(q), pkts, bytes);
} }
...@@ -832,6 +883,7 @@ static void ionic_tx_tso_post(struct ionic_queue *q, struct ionic_txq_desc *desc ...@@ -832,6 +883,7 @@ static void ionic_tx_tso_post(struct ionic_queue *q, struct ionic_txq_desc *desc
if (start) { if (start) {
skb_tx_timestamp(skb); skb_tx_timestamp(skb);
if (!unlikely(q->features & IONIC_TXQ_F_HWSTAMP))
netdev_tx_sent_queue(q_to_ndq(q), skb->len); netdev_tx_sent_queue(q_to_ndq(q), skb->len);
ionic_txq_post(q, false, ionic_tx_clean, skb); ionic_txq_post(q, false, ionic_tx_clean, skb);
} else { } else {
...@@ -1079,6 +1131,7 @@ static int ionic_tx(struct ionic_queue *q, struct sk_buff *skb) ...@@ -1079,6 +1131,7 @@ static int ionic_tx(struct ionic_queue *q, struct sk_buff *skb)
stats->pkts++; stats->pkts++;
stats->bytes += skb->len; stats->bytes += skb->len;
if (!unlikely(q->features & IONIC_TXQ_F_HWSTAMP))
netdev_tx_sent_queue(q_to_ndq(q), skb->len); netdev_tx_sent_queue(q_to_ndq(q), skb->len);
ionic_txq_post(q, !netdev_xmit_more(), ionic_tx_clean, skb); ionic_txq_post(q, !netdev_xmit_more(), ionic_tx_clean, skb);
...@@ -1131,6 +1184,41 @@ static int ionic_maybe_stop_tx(struct ionic_queue *q, int ndescs) ...@@ -1131,6 +1184,41 @@ static int ionic_maybe_stop_tx(struct ionic_queue *q, int ndescs)
return stopped; return stopped;
} }
static netdev_tx_t ionic_start_hwstamp_xmit(struct sk_buff *skb,
struct net_device *netdev)
{
struct ionic_lif *lif = netdev_priv(netdev);
struct ionic_queue *q = &lif->hwstamp_txq->q;
int err, ndescs;
/* Does not stop/start txq, because we post to a separate tx queue
* for timestamping, and if a packet can't be posted immediately to
* the timestamping queue, it is dropped.
*/
ndescs = ionic_tx_descs_needed(q, skb);
if (unlikely(ndescs < 0))
goto err_out_drop;
if (unlikely(!ionic_q_has_space(q, ndescs)))
goto err_out_drop;
if (skb_is_gso(skb))
err = ionic_tx_tso(q, skb);
else
err = ionic_tx(q, skb);
if (err)
goto err_out_drop;
return NETDEV_TX_OK;
err_out_drop:
q->drop++;
dev_kfree_skb(skb);
return NETDEV_TX_OK;
}
netdev_tx_t ionic_start_xmit(struct sk_buff *skb, struct net_device *netdev) netdev_tx_t ionic_start_xmit(struct sk_buff *skb, struct net_device *netdev)
{ {
u16 queue_index = skb_get_queue_mapping(skb); u16 queue_index = skb_get_queue_mapping(skb);
...@@ -1144,6 +1232,10 @@ netdev_tx_t ionic_start_xmit(struct sk_buff *skb, struct net_device *netdev) ...@@ -1144,6 +1232,10 @@ netdev_tx_t ionic_start_xmit(struct sk_buff *skb, struct net_device *netdev)
return NETDEV_TX_OK; return NETDEV_TX_OK;
} }
if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP))
if (lif->hwstamp_txq)
return ionic_start_hwstamp_xmit(skb, netdev);
if (unlikely(queue_index >= lif->nxqs)) if (unlikely(queue_index >= lif->nxqs))
queue_index = 0; queue_index = 0;
q = &lif->txqcqs[queue_index]->q; q = &lif->txqcqs[queue_index]->q;
......
...@@ -14,4 +14,7 @@ int ionic_tx_napi(struct napi_struct *napi, int budget); ...@@ -14,4 +14,7 @@ int ionic_tx_napi(struct napi_struct *napi, int budget);
int ionic_txrx_napi(struct napi_struct *napi, int budget); int ionic_txrx_napi(struct napi_struct *napi, int budget);
netdev_tx_t ionic_start_xmit(struct sk_buff *skb, struct net_device *netdev); netdev_tx_t ionic_start_xmit(struct sk_buff *skb, struct net_device *netdev);
bool ionic_rx_service(struct ionic_cq *cq, struct ionic_cq_info *cq_info);
bool ionic_tx_service(struct ionic_cq *cq, struct ionic_cq_info *cq_info);
#endif /* _IONIC_TXRX_H_ */ #endif /* _IONIC_TXRX_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment