[PATCH 04/14] net: ionic: Export the APIs from net driver to support device commands

Abhijit Gangurde
2025-04-23T10:30:52Z

RDMA driver needs to establish admin queues to support admin operations.
Export the APIs to send device commands, allocate interrupts for
event queues and get doorbell page information.

Reviewed-by: Shannon Nelson <shannon.nelson@amd.com>
Co-developed-by: Allen Hubbe <allen.hubbe@amd.com>
Signed-off-by: Allen Hubbe <allen.hubbe@amd.com>
Signed-off-by: Abhijit Gangurde <abhijit.gangurde@amd.com>
---
drivers/net/ethernet/pensando/ionic/ionic.h | 8 +-
.../net/ethernet/pensando/ionic/ionic_api.c | 93 ++++++++++++++++++
.../net/ethernet/pensando/ionic/ionic_api.h | 94 +++++++++++++++++++
.../net/ethernet/pensando/ionic/ionic_bus.h | 1 +
.../ethernet/pensando/ionic/ionic_bus_pci.c | 6 ++
.../net/ethernet/pensando/ionic/ionic_lif.c | 41 ++++++--
.../net/ethernet/pensando/ionic/ionic_lif.h | 5 +
.../net/ethernet/pensando/ionic/ionic_main.c | 2 +-
8 files changed, 237 insertions(+), 13 deletions(-)

diff --git a/drivers/net/ethernet/pensando/ionic/ionic.h b/drivers/net/ethernet/pensando/ionic/ionic.h
index 013e1ce72d0d..5abdaf2fa3a6 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic.h
@@ -65,12 +65,6 @@ struct ionic {
int watchdog_period;
};

-struct ionic_admin_ctx {
- struct completion work;
- union ionic_adminq_cmd cmd;
- 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,
const int err, const bool do_msg);
@@ -97,4 +91,6 @@ int ionic_port_reset(struct ionic *ionic);

bool ionic_doorbell_wa(struct ionic *ionic);

+int ionic_error_to_errno(enum ionic_status_code code);
+
#endif /* _IONIC_H_ */
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_api.c b/drivers/net/ethernet/pensando/ionic/ionic_api.c
index 8c39e6183ad4..201053cf4ea8 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_api.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_api.c
@@ -4,6 +4,7 @@
#include <linux/kernel.h>

#include "ionic.h"
+#include "ionic_bus.h"
#include "ionic_lif.h"

struct net_device *ionic_api_get_netdev_from_handle(void *handle)
@@ -31,6 +32,31 @@ const union ionic_lif_identity *ionic_api_get_identity(void *handle,
}
EXPORT_SYMBOL_NS(ionic_api_get_identity, "NET_IONIC");

+void ionic_api_request_reset(void *handle)
+{
+ struct ionic_lif *lif = handle;
+ struct ionic *ionic;
+ int err;
+
+ union ionic_dev_cmd cmd = {
+ .cmd.opcode = IONIC_CMD_RDMA_RESET_LIF,
+ .cmd.lif_index = cpu_to_le16(lif->index),
+ };
+
+ ionic = lif->ionic;
+
+ mutex_lock(&ionic->dev_cmd_lock);
+
+ ionic_dev_cmd_go(&ionic->idev, &cmd);
+ err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
+
+ mutex_unlock(&ionic->dev_cmd_lock);
+
+ if (err)
+ netdev_warn(lif->netdev, "request_reset: error %d\n", err);
+}
+EXPORT_SYMBOL_NS(ionic_api_request_reset, "NET_IONIC");
+
const struct ionic_devinfo *ionic_api_get_devinfo(void *handle)
{
struct ionic_lif *lif = handle;
@@ -38,3 +64,70 @@ const struct ionic_devinfo *ionic_api_get_devinfo(void *handle)
return &lif->ionic->idev.dev_info;
}
EXPORT_SYMBOL_NS(ionic_api_get_devinfo, "NET_IONIC");
+
+int ionic_api_get_intr(void *handle, int *irq)
+{
+ struct ionic_intr_info intr_obj;
+ struct ionic_lif *lif = handle;
+ int err;
+
+ if (!lif->nrdma_eqs_avail)
+ return -ENOSPC;
+
+ err = ionic_intr_alloc(lif->ionic, &intr_obj);
+ if (err)
+ return err;
+
+ err = ionic_bus_get_irq(lif->ionic, intr_obj.index);
+ if (err < 0) {
+ ionic_intr_free(lif->ionic, intr_obj.index);
+ return err;
+ }
+
+ lif->nrdma_eqs_avail--;
+
+ *irq = err;
+ return intr_obj.index;
+}
+EXPORT_SYMBOL_NS(ionic_api_get_intr, "NET_IONIC");
+
+void ionic_api_put_intr(void *handle, int intr_index)
+{
+ struct ionic_lif *lif = handle;
+
+ ionic_intr_free(lif->ionic, intr_index);
+
+ lif->nrdma_eqs_avail++;
+}
+EXPORT_SYMBOL_NS(ionic_api_put_intr, "NET_IONIC");
+
+void ionic_api_kernel_dbpage(void *handle,
+ struct ionic_intr __iomem **intr_ctrl,
+ u32 *dbid, u64 __iomem **dbpage)
+{
+ struct ionic_lif *lif = handle;
+
+ *intr_ctrl = lif->ionic->idev.intr_ctrl;
+
+ *dbid = lif->kern_pid;
+ *dbpage = lif->kern_dbpage;
+}
+EXPORT_SYMBOL_NS(ionic_api_kernel_dbpage, "NET_IONIC");
+
+int ionic_api_adminq_post(void *handle, struct ionic_admin_ctx *ctx)
+{
+ return ionic_adminq_post(handle, ctx);
+}
+EXPORT_SYMBOL_NS(ionic_api_adminq_post, "NET_IONIC");
+
+int ionic_api_adminq_post_wait(void *handle, struct ionic_admin_ctx *ctx)
+{
+ return ionic_adminq_post_wait(handle, ctx);
+}
+EXPORT_SYMBOL_NS(ionic_api_adminq_post_wait, "NET_IONIC");
+
+int ionic_api_error_to_errno(enum ionic_status_code code)
+{
+ return ionic_error_to_errno(code);
+}
+EXPORT_SYMBOL_NS(ionic_api_error_to_errno, "NET_IONIC");
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_api.h b/drivers/net/ethernet/pensando/ionic/ionic_api.h
index f59391102c62..80606a37ae45 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_api.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic_api.h
@@ -65,4 +65,98 @@ struct net_device *ionic_api_get_netdev_from_handle(void *handle);
*/
const struct ionic_devinfo *ionic_api_get_devinfo(void *handle);

+/**
+ * ionic_api_request_reset - request reset or disable the device or lif
+ * @handle: Handle to lif
+ *
+ * The reset is triggered asynchronously. It will wait until reset request
+ * completes or times out.
+ */
+void ionic_api_request_reset(void *handle);
+
+/**
+ * ionic_api_get_intr - Reserve a device interrupt index
+ * @handle: Handle to lif
+ * @irq: OS interrupt number returned
+ *
+ * Reserve an interrupt index, and indicate the irq number for that index.
+ *
+ * Return: interrupt index or negative error status
+ */
+int ionic_api_get_intr(void *handle, int *irq);
+
+/**
+ * ionic_api_put_intr - Release a device interrupt index
+ * @handle: Handle to lif
+ * @intr: Interrupt index
+ *
+ * Mark the interrupt index unused so that it can be reserved again.
+ */
+void ionic_api_put_intr(void *handle, int intr);
+
+/**
+ * ionic_api_kernel_dbpage - Get mapped doorbell page for use in kernel space
+ * @handle: Handle to lif
+ * @intr_ctrl: Interrupt control registers
+ * @dbid: Doorbell id for use in kernel space
+ * @dbpage: One ioremapped doorbell page for use in kernel space
+ *
+ * This also provides mapped interrupt control registers.
+ *
+ * The id and page returned here refer to the doorbell page reserved for use in
+ * kernel space for this lif. For user space, use ionic_api_get_dbid to
+ * allocate a doorbell id for exclusive use by a process.
+ */
+void ionic_api_kernel_dbpage(void *handle,
+ struct ionic_intr __iomem **intr_ctrl,
+ u32 *dbid, u64 __iomem **dbpage);
+
+/**
+ * struct ionic_admin_ctx - Admin command context
+ * @work: Work completion wait queue element
+ * @cmd: Admin command (64B) to be copied to the queue
+ * @comp: Admin completion (16B) copied from the queue
+ */
+struct ionic_admin_ctx {
+ struct completion work;
+ union ionic_adminq_cmd cmd;
+ union ionic_adminq_comp comp;
+};
+
+/**
+ * ionic_api_adminq_post - Post an admin command
+ * @handle: Handle to lif
+ * @ctx: API admin command context
+ *
+ * Post the command to an admin queue in the ethernet driver. If this command
+ * succeeds, then the command has been posted, but that does not indicate a
+ * completion. If this command returns success, then the completion callback
+ * will eventually be called.
+ *
+ * Return: zero or negative error status
+ */
+int ionic_api_adminq_post(void *handle, struct ionic_admin_ctx *ctx);
+
+/**
+ * ionic_api_adminq_post_wait - Post an admin command and wait for response
+ * @handle: Handle to lif
+ * @ctx: API admin command context
+ *
+ * Post the command to an admin queue in the ethernet driver. If this command
+ * succeeds, then the command has been posted, but that does not indicate a
+ * completion. If this command returns success, then the completion callback
+ * will eventually be called.
+ *
+ * Return: zero or negative error status
+ */
+int ionic_api_adminq_post_wait(void *handle, struct ionic_admin_ctx *ctx);
+
+/**
+ * ionic_api_error_to_errno - Transform ionic_if errors to os errno
+ * @code: Ionic error number
+ *
+ * Return: Negative OS error number or zero
+ */
+int ionic_api_error_to_errno(enum ionic_status_code code);
+
#endif /* _IONIC_API_H_ */
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_bus.h b/drivers/net/ethernet/pensando/ionic/ionic_bus.h
index 2f4d08c64910..0963b6aabd42 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_bus.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic_bus.h
@@ -12,5 +12,6 @@ int ionic_bus_register_driver(void);
void ionic_bus_unregister_driver(void);
void __iomem *ionic_bus_map_dbpage(struct ionic *ionic, int page_num);
void ionic_bus_unmap_dbpage(struct ionic *ionic, void __iomem *page);
+phys_addr_t ionic_bus_phys_dbpage(struct ionic *ionic, int page_num);

#endif /* _IONIC_BUS_H_ */
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c b/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c
index bb75044dfb82..5fa8840b063f 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c
@@ -109,6 +109,12 @@ void ionic_bus_unmap_dbpage(struct ionic *ionic, void __iomem *page)
iounmap(page);
}

+phys_addr_t ionic_bus_phys_dbpage(struct ionic *ionic, int page_num)
+{
+ return ionic->bars[IONIC_PCI_BAR_DBELL].bus_addr +
+ ((phys_addr_t)page_num << PAGE_SHIFT);
+}
+
static void ionic_vf_dealloc_locked(struct ionic *ionic)
{
struct ionic_vf_setattr_cmd vfc = { .attr = IONIC_VF_ATTR_STATSADDR };
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
index e63ed91879a1..c1ad2b95d2b9 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
@@ -244,14 +244,13 @@ static int ionic_request_irq(struct ionic_lif *lif, struct ionic_qcq *qcq)
0, intr->name, &qcq->napi);
}

-static int ionic_intr_alloc(struct ionic_lif *lif, struct ionic_intr_info *intr)
+int ionic_intr_alloc(struct ionic *ionic, struct ionic_intr_info *intr)
{
- struct ionic *ionic = lif->ionic;
int index;

index = find_first_zero_bit(ionic->intrs, ionic->nintrs);
if (index == ionic->nintrs) {
- netdev_warn(lif->netdev, "%s: no intr, index=%d nintrs=%d\n",
+ netdev_warn(ionic->lif->netdev, "%s: no intr, index=%d nintrs=%d\n",
__func__, index, ionic->nintrs);
return -ENOSPC;
}
@@ -262,7 +261,7 @@ static int ionic_intr_alloc(struct ionic_lif *lif, struct ionic_intr_info *intr)
return 0;
}

-static void ionic_intr_free(struct ionic *ionic, int index)
+void ionic_intr_free(struct ionic *ionic, int index)
{
if (index != IONIC_INTR_INDEX_NOT_ASSIGNED && index < ionic->nintrs)
clear_bit(index, ionic->intrs);
@@ -504,7 +503,7 @@ static int ionic_alloc_qcq_interrupt(struct ionic_lif *lif, struct ionic_qcq *qc
return 0;
}

- err = ionic_intr_alloc(lif, &qcq->intr);
+ err = ionic_intr_alloc(lif->ionic, &qcq->intr);
if (err) {
netdev_warn(lif->netdev, "no intr for %s: %d\n",
qcq->q.name, err);
@@ -3268,6 +3267,7 @@ int ionic_lif_alloc(struct ionic *ionic)
lif->netdev->max_mtu =
le32_to_cpu(lif->identity->eth.max_frame_size) - VLAN_ETH_HLEN;

+ lif->nrdma_eqs_avail = ionic->nrdma_eqs_per_lif;
lif->nrdma_eqs = ionic->nrdma_eqs_per_lif;
lif->nxqs = ionic->ntxqs_per_lif;

@@ -3295,6 +3295,7 @@ int ionic_lif_alloc(struct ionic *ionic)
mutex_init(&lif->queue_lock);
mutex_init(&lif->config_lock);
mutex_init(&lif->adev_lock);
+ mutex_init(&lif->dbid_inuse_lock);

spin_lock_init(&lif->adminq_lock);

@@ -3351,6 +3352,7 @@ int ionic_lif_alloc(struct ionic *ionic)
lif->info = NULL;
lif->info_pa = 0;
err_out_free_mutex:
+ mutex_destroy(&lif->dbid_inuse_lock);
mutex_destroy(&lif->adev_lock);
mutex_destroy(&lif->config_lock);
mutex_destroy(&lif->queue_lock);
@@ -3507,6 +3509,14 @@ static void ionic_lif_handle_fw_up(struct ionic_lif *lif)
dev_err(ionic->dev, "FW Up: LIFs restart failed - err %d\n", err);
}

+static void ionic_lif_dbid_inuse_free(struct ionic_lif *lif)
+{
+ mutex_lock(&lif->dbid_inuse_lock);
+ bitmap_free(lif->dbid_inuse);
+ lif->dbid_inuse = NULL;
+ mutex_unlock(&lif->dbid_inuse_lock);
+}
+
void ionic_lif_free(struct ionic_lif *lif)
{
struct device *dev = lif->ionic->dev;
@@ -3535,10 +3545,12 @@ void ionic_lif_free(struct ionic_lif *lif)
/* unmap doorbell page */
ionic_bus_unmap_dbpage(lif->ionic, lif->kern_dbpage);
lif->kern_dbpage = NULL;
+ ionic_lif_dbid_inuse_free(lif);

mutex_destroy(&lif->config_lock);
mutex_destroy(&lif->queue_lock);
mutex_destroy(&lif->adev_lock);
+ mutex_destroy(&lif->dbid_inuse_lock);

/* free netdev & lif */
ionic_debugfs_del_lif(lif);
@@ -3562,6 +3574,8 @@ void ionic_lif_deinit(struct ionic_lif *lif)
ionic_lif_qcq_deinit(lif, lif->notifyqcq);
ionic_lif_qcq_deinit(lif, lif->adminqcq);

+ ionic_lif_dbid_inuse_free(lif);
+
ionic_lif_reset(lif);
}

@@ -3755,12 +3769,25 @@ int ionic_lif_init(struct ionic_lif *lif)
return -EINVAL;
}

+ mutex_lock(&lif->dbid_inuse_lock);
+ lif->dbid_inuse = bitmap_zalloc(lif->dbid_count, GFP_KERNEL);
+ if (!lif->dbid_inuse) {
+ dev_err(dev, "Failed alloc doorbell id bitmap, aborting\n");
+ mutex_unlock(&lif->dbid_inuse_lock);
+ return -ENOMEM;
+ }
+
+ /* first doorbell id reserved for kernel (dbid aka pid == zero) */
+ set_bit(0, lif->dbid_inuse);
+ mutex_unlock(&lif->dbid_inuse_lock);
lif->kern_pid = 0;
+
dbpage_num = ionic_db_page_num(lif, lif->kern_pid);
lif->kern_dbpage = ionic_bus_map_dbpage(lif->ionic, dbpage_num);
if (!lif->kern_dbpage) {
dev_err(dev, "Cannot map dbpage, aborting\n");
- return -ENOMEM;
+ err = -ENOMEM;
+ goto err_out_free_dbid;
}

err = ionic_lif_adminq_init(lif);
@@ -3807,6 +3834,8 @@ int ionic_lif_init(struct ionic_lif *lif)
ionic_lif_reset(lif);
ionic_bus_unmap_dbpage(lif->ionic, lif->kern_dbpage);
lif->kern_dbpage = NULL;
+err_out_free_dbid:
+ ionic_lif_dbid_inuse_free(lif);

return err;
}
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.h b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
index 333394e477e0..54c8bbe8960a 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
@@ -204,6 +204,7 @@ struct ionic_lif {
unsigned int kern_pid;
u64 __iomem *kern_dbpage;
unsigned int nrdma_eqs;
+ unsigned int nrdma_eqs_avail;
unsigned int nxqs;
unsigned int ntxq_descs;
unsigned int nrxq_descs;
@@ -241,6 +242,8 @@ struct ionic_lif {
u32 tx_coalesce_usecs; /* what the user asked for */
u32 tx_coalesce_hw; /* what the hw is using */
unsigned int dbid_count;
+ struct mutex dbid_inuse_lock; /* lock the dbid bit list */
+ unsigned long *dbid_inuse;

struct ionic_phc *phc;

@@ -402,6 +405,8 @@ int ionic_lif_set_hwstamp_rxfilt(struct ionic_lif *lif, u64 pkt_class);

int ionic_lif_rss_config(struct ionic_lif *lif, u16 types,
const u8 *key, const u32 *indir);
+int ionic_intr_alloc(struct ionic *ionic, struct ionic_intr_info *intr);
+void ionic_intr_free(struct ionic *ionic, int index);
void ionic_lif_rx_mode(struct ionic_lif *lif);
int ionic_reconfigure_queues(struct ionic_lif *lif,
struct ionic_queue_params *qparam);
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_main.c b/drivers/net/ethernet/pensando/ionic/ionic_main.c
index daf1e82cb76b..83e3117a199f 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_main.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_main.c
@@ -72,7 +72,7 @@ static const char *ionic_error_to_str(enum ionic_status_code code)
}
}

-static int ionic_error_to_errno(enum ionic_status_code code)
+int ionic_error_to_errno(enum ionic_status_code code)
{
switch (code) {
case IONIC_RC_SUCCESS:
--
2.34.1