Commit bc610777 authored by Alexander Duyck's avatar Alexander Duyck Committed by Jakub Kicinski
Browse files

eth: fbnic: Allocate a netdevice and napi vectors with queues



Allocate a netdev and figure out basics like how many queues
we need, MAC address, MTU bounds. Kick off a service task
to do various periodic things like health checking.
The service task only runs when device is open.

We have four levels of objects here:
 - ring - A HW ring with head / tail pointers,
 - triad - Two submission and one completion ring,
 - NAPI - NAPI, with one IRQ and any number of Rx and Tx triads,
 - Netdev - The ultimate container of the rings and napi vectors.

The "triad" is the only less-than-usual construct. On Rx we have
two "free buffer" submission rings, one for packet headers and
one for packet data. On Tx we have separate rings for XDP Tx
and normal Tx. So we ended up with ring triplets in both
directions.

We keep NAPIs on a local list, even though core already maintains a list.
Later on having a separate list will matter for live reconfig.
We introduce the list already, the churn would not be worth it.

Signed-off-by: default avatarAlexander Duyck <alexanderduyck@fb.com>
Link: https://patch.msgid.link/172079938358.1778861.11681469974633489463.stgit@ahduyck-xeon-server.home.arpa


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent da3cde08
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -11,5 +11,7 @@ fbnic-y := fbnic_devlink.o \
	   fbnic_fw.o \
	   fbnic_irq.o \
	   fbnic_mac.o \
	   fbnic_netdev.o \
	   fbnic_pci.o \
	   fbnic_tlv.o
	   fbnic_tlv.o \
	   fbnic_txrx.o
+18 −0
Original line number Diff line number Diff line
@@ -4,7 +4,10 @@
#ifndef _FBNIC_H_
#define _FBNIC_H_

#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/types.h>
#include <linux/workqueue.h>

#include "fbnic_csr.h"
#include "fbnic_fw.h"
@@ -12,6 +15,7 @@

struct fbnic_dev {
	struct device *dev;
	struct net_device *netdev;

	u32 __iomem *uc_addr0;
	u32 __iomem *uc_addr4;
@@ -19,6 +23,8 @@ struct fbnic_dev {
	unsigned int fw_msix_vector;
	unsigned short num_irqs;

	struct delayed_work service_task;

	struct fbnic_fw_mbx mbx[FBNIC_IPC_MBX_INDICES];
	/* Lock protecting Tx Mailbox queue to prevent possible races */
	spinlock_t fw_tx_lock;
@@ -26,6 +32,9 @@ struct fbnic_dev {
	u64 dsn;
	u32 mps;
	u32 readrq;

	/* Number of TCQs/RCQs available on hardware */
	u16 max_num_queues;
};

/* Reserve entry 0 in the MSI-X "others" array until we have filled all
@@ -81,6 +90,11 @@ void fbnic_fw_wr32(struct fbnic_dev *fbd, u32 reg, u32 val);
#define fw_wr32(_f, _r, _v)	fbnic_fw_wr32(_f, _r, _v)
#define fw_wrfl(_f)		fbnic_fw_rd32(_f, FBNIC_FW_ZERO_REG)

static inline bool fbnic_init_failure(struct fbnic_dev *fbd)
{
	return !fbd->netdev;
}

extern char fbnic_driver_name[];

void fbnic_devlink_free(struct fbnic_dev *fbd);
@@ -91,6 +105,9 @@ void fbnic_devlink_unregister(struct fbnic_dev *fbd);
int fbnic_fw_enable_mbx(struct fbnic_dev *fbd);
void fbnic_fw_disable_mbx(struct fbnic_dev *fbd);

int fbnic_request_irq(struct fbnic_dev *dev, int nr, irq_handler_t handler,
		      unsigned long flags, const char *name, void *data);
void fbnic_free_irq(struct fbnic_dev *dev, int nr, void *data);
void fbnic_free_irqs(struct fbnic_dev *fbd);
int fbnic_alloc_irqs(struct fbnic_dev *fbd);

@@ -99,6 +116,7 @@ enum fbnic_boards {
};

struct fbnic_info {
	unsigned int max_num_queues;
	unsigned int bar_mask;
};

+40 −0
Original line number Diff line number Diff line
@@ -366,7 +366,47 @@ enum {
#define FBNIC_PUL_OB_TLP_HDR_AR_CFG_BME		CSR_BIT(18)
#define FBNIC_CSR_END_PUL_USER	0x31080	/* CSR section delimiter */

/* Queue Registers
 *
 * The queue register offsets are specific for a given queue grouping. So to
 * find the actual register offset it is necessary to combine FBNIC_QUEUE(n)
 * with the register to get the actual register offset like so:
 *   FBNIC_QUEUE_TWQ0_CTL(n) == FBNIC_QUEUE(n) + FBNIC_QUEUE_TWQ0_CTL
 */
#define FBNIC_CSR_START_QUEUE		0x40000	/* CSR section delimiter */
#define FBNIC_QUEUE_STRIDE		0x400		/* 0x1000 */
#define FBNIC_QUEUE(n)\
	(0x40000 + FBNIC_QUEUE_STRIDE * (n))	/* 0x100000 + 4096*n */

#define FBNIC_QUEUE_TWQ0_CTL		0x000		/* 0x000 */
#define FBNIC_QUEUE_TWQ1_CTL		0x001		/* 0x004 */
#define FBNIC_QUEUE_TWQ_CTL_RESET		CSR_BIT(0)
#define FBNIC_QUEUE_TWQ_CTL_ENABLE		CSR_BIT(1)
#define FBNIC_QUEUE_TWQ_CTL_PREFETCH_DISABLE	CSR_BIT(2)
#define FBNIC_QUEUE_TWQ_CTL_TXB_FIFO_SEL_MASK	CSR_GENMASK(30, 29)
enum {
	FBNIC_QUEUE_TWQ_CTL_TXB_SHARED	= 0,
	FBNIC_QUEUE_TWQ_CTL_TXB_EI_DATA	= 1,
	FBNIC_QUEUE_TWQ_CTL_TXB_EI_CTL	= 2,
};

#define FBNIC_QUEUE_TWQ_CTL_AGGR_MODE		CSR_BIT(31)

#define FBNIC_QUEUE_TWQ0_TAIL		0x002		/* 0x008 */
#define FBNIC_QUEUE_TWQ1_TAIL		0x003		/* 0x00c */

/* Tx Completion Queue Registers */
#define FBNIC_QUEUE_TCQ_HEAD		0x081		/* 0x204 */

/* Rx Completion Queue Registers */
#define FBNIC_QUEUE_RCQ_HEAD		0x201		/* 0x804 */

/* Rx Buffer Descriptor Queue Registers */
#define FBNIC_QUEUE_BDQ_HPQ_TAIL	0x241		/* 0x904 */
#define FBNIC_QUEUE_BDQ_PPQ_TAIL	0x242		/* 0x908 */

#define FBNIC_MAX_QUEUES		128
#define FBNIC_CSR_END_QUEUE	(0x40000 + 0x400 * FBNIC_MAX_QUEUES - 1)

/* BAR 4 CSRs */

+25 −1
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@
#include <linux/types.h>

#include "fbnic.h"
#include "fbnic_txrx.h"

static irqreturn_t fbnic_fw_msix_intr(int __always_unused irq, void *data)
{
@@ -80,6 +81,29 @@ void fbnic_fw_disable_mbx(struct fbnic_dev *fbd)
	fbnic_mbx_clean(fbd);
}

int fbnic_request_irq(struct fbnic_dev *fbd, int nr, irq_handler_t handler,
		      unsigned long flags, const char *name, void *data)
{
	struct pci_dev *pdev = to_pci_dev(fbd->dev);
	int irq = pci_irq_vector(pdev, nr);

	if (irq < 0)
		return irq;

	return request_irq(irq, handler, flags, name, data);
}

void fbnic_free_irq(struct fbnic_dev *fbd, int nr, void *data)
{
	struct pci_dev *pdev = to_pci_dev(fbd->dev);
	int irq = pci_irq_vector(pdev, nr);

	if (irq < 0)
		return;

	free_irq(irq, data);
}

void fbnic_free_irqs(struct fbnic_dev *fbd)
{
	struct pci_dev *pdev = to_pci_dev(fbd->dev);
@@ -97,7 +121,7 @@ int fbnic_alloc_irqs(struct fbnic_dev *fbd)
	struct pci_dev *pdev = to_pci_dev(fbd->dev);
	int num_irqs;

	wanted_irqs += 1;
	wanted_irqs += min_t(unsigned int, num_online_cpus(), FBNIC_MAX_RXQS);
	num_irqs = pci_alloc_irq_vectors(pdev, FBNIC_NON_NAPI_VECTORS + 1,
					 wanted_irqs, PCI_IRQ_MSIX);
	if (num_irqs < 0) {
+189 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) Meta Platforms, Inc. and affiliates. */

#include <linux/etherdevice.h>
#include <linux/ipv6.h>
#include <linux/types.h>

#include "fbnic.h"
#include "fbnic_netdev.h"
#include "fbnic_txrx.h"

int __fbnic_open(struct fbnic_net *fbn)
{
	int err;

	err = fbnic_alloc_napi_vectors(fbn);
	if (err)
		return err;

	err = netif_set_real_num_tx_queues(fbn->netdev,
					   fbn->num_tx_queues);
	if (err)
		goto free_resources;

	err = netif_set_real_num_rx_queues(fbn->netdev,
					   fbn->num_rx_queues);
	if (err)
		goto free_resources;

	return 0;
free_resources:
	fbnic_free_napi_vectors(fbn);
	return err;
}

static int fbnic_open(struct net_device *netdev)
{
	struct fbnic_net *fbn = netdev_priv(netdev);
	int err;

	err = __fbnic_open(fbn);
	if (!err)
		fbnic_up(fbn);

	return err;
}

static int fbnic_stop(struct net_device *netdev)
{
	struct fbnic_net *fbn = netdev_priv(netdev);

	fbnic_down(fbn);

	fbnic_free_napi_vectors(fbn);

	return 0;
}

static const struct net_device_ops fbnic_netdev_ops = {
	.ndo_open		= fbnic_open,
	.ndo_stop		= fbnic_stop,
	.ndo_validate_addr	= eth_validate_addr,
	.ndo_start_xmit		= fbnic_xmit_frame,
};

void fbnic_reset_queues(struct fbnic_net *fbn,
			unsigned int tx, unsigned int rx)
{
	struct fbnic_dev *fbd = fbn->fbd;
	unsigned int max_napis;

	max_napis = fbd->num_irqs - FBNIC_NON_NAPI_VECTORS;

	tx = min(tx, max_napis);
	fbn->num_tx_queues = tx;

	rx = min(rx, max_napis);
	fbn->num_rx_queues = rx;

	fbn->num_napi = max(tx, rx);
}

/**
 * fbnic_netdev_free - Free the netdev associate with fbnic
 * @fbd: Driver specific structure to free netdev from
 *
 * Allocate and initialize the netdev and netdev private structure. Bind
 * together the hardware, netdev, and pci data structures.
 **/
void fbnic_netdev_free(struct fbnic_dev *fbd)
{
	free_netdev(fbd->netdev);
	fbd->netdev = NULL;
}

/**
 * fbnic_netdev_alloc - Allocate a netdev and associate with fbnic
 * @fbd: Driver specific structure to associate netdev with
 *
 * Allocate and initialize the netdev and netdev private structure. Bind
 * together the hardware, netdev, and pci data structures.
 *
 *  Return: 0 on success, negative on failure
 **/
struct net_device *fbnic_netdev_alloc(struct fbnic_dev *fbd)
{
	struct net_device *netdev;
	struct fbnic_net *fbn;
	int default_queues;

	netdev = alloc_etherdev_mq(sizeof(*fbn), FBNIC_MAX_RXQS);
	if (!netdev)
		return NULL;

	SET_NETDEV_DEV(netdev, fbd->dev);
	fbd->netdev = netdev;

	netdev->netdev_ops = &fbnic_netdev_ops;

	fbn = netdev_priv(netdev);

	fbn->netdev = netdev;
	fbn->fbd = fbd;
	INIT_LIST_HEAD(&fbn->napis);

	default_queues = netif_get_num_default_rss_queues();
	if (default_queues > fbd->max_num_queues)
		default_queues = fbd->max_num_queues;

	fbnic_reset_queues(fbn, default_queues, default_queues);

	netdev->min_mtu = IPV6_MIN_MTU;
	netdev->max_mtu = FBNIC_MAX_JUMBO_FRAME_SIZE - ETH_HLEN;

	netif_carrier_off(netdev);

	netif_tx_stop_all_queues(netdev);

	return netdev;
}

static int fbnic_dsn_to_mac_addr(u64 dsn, char *addr)
{
	addr[0] = (dsn >> 56) & 0xFF;
	addr[1] = (dsn >> 48) & 0xFF;
	addr[2] = (dsn >> 40) & 0xFF;
	addr[3] = (dsn >> 16) & 0xFF;
	addr[4] = (dsn >> 8) & 0xFF;
	addr[5] = dsn & 0xFF;

	return is_valid_ether_addr(addr) ? 0 : -EINVAL;
}

/**
 * fbnic_netdev_register - Initialize general software structures
 * @netdev: Netdev containing structure to initialize and register
 *
 * Initialize the MAC address for the netdev and register it.
 *
 *  Return: 0 on success, negative on failure
 **/
int fbnic_netdev_register(struct net_device *netdev)
{
	struct fbnic_net *fbn = netdev_priv(netdev);
	struct fbnic_dev *fbd = fbn->fbd;
	u64 dsn = fbd->dsn;
	u8 addr[ETH_ALEN];
	int err;

	err = fbnic_dsn_to_mac_addr(dsn, addr);
	if (!err) {
		ether_addr_copy(netdev->perm_addr, addr);
		eth_hw_addr_set(netdev, addr);
	} else {
		/* A randomly assigned MAC address will cause provisioning
		 * issues so instead just fail to spawn the netdev and
		 * avoid any confusion.
		 */
		dev_err(fbd->dev, "MAC addr %pM invalid\n", addr);
		return err;
	}

	return register_netdev(netdev);
}

void fbnic_netdev_unregister(struct net_device *netdev)
{
	unregister_netdev(netdev);
}
Loading