NTB: Add variable number MW, DB CB support code

This is a follow-up to r289208: "Xeon Errata Workaround."

Add logic to support a variable number of memory windows and doorbell
callbacks.  This was added to the Linux driver in the "Xeon Errata
Workaround" commit, but I skipped it because it didn't look neccessary
at the time.  It is needed for future Haswell split-BAR support, so
bring it in now.

A new tunable was added for if_ntb, 'hw.ntb.max_num_clients'.  By
default, it is set to zero -- infer the number of clients from the
number of memory windows available from the hardware.  Any other
positive value can specify a different number of clients, limited by the
number of doorbell callbacks available (4 under MSI-X, or 15 (Xeon) or
34 (SoC) under legacy INTx).

Obtained from:	Linux (Dual BSD/GPL driver)
Sponsored by:	EMC / Isilon Storage Division
This commit is contained in:
cem 2015-10-15 23:45:43 +00:00
parent 8076e3efdf
commit a2d3d01d7a
3 changed files with 86 additions and 39 deletions

View File

@ -80,11 +80,11 @@ __FBSDID("$FreeBSD$");
static unsigned int transport_mtu = 0x4000 + ETHER_HDR_LEN + ETHER_CRC_LEN;
/*
* This is an oversimplification to work around Xeon Errata. The second client
* may be usable for unidirectional traffic.
*/
static unsigned int max_num_clients = 1;
static unsigned int max_num_clients;
SYSCTL_UINT(_hw_ntb, OID_AUTO, max_num_clients, CTLFLAG_RDTUN,
&max_num_clients, 0, "Maximum number of NTB transport clients. "
"0 (default) - use all available NTB memory windows; "
"positive integer N - Limit to N memory windows.");
STAILQ_HEAD(ntb_queue_list, ntb_queue_entry);
@ -174,7 +174,7 @@ struct ntb_transport_mw {
struct ntb_netdev {
struct ntb_softc *ntb;
struct ifnet *ifp;
struct ntb_transport_mw mw[NTB_NUM_MW];
struct ntb_transport_mw mw[NTB_MAX_NUM_MW];
struct ntb_transport_qp *qps;
uint64_t max_qps;
uint64_t qp_bitmap;
@ -220,7 +220,7 @@ enum {
IF_NTB_MAX_SPAD,
};
#define QP_TO_MW(qp) ((qp) % NTB_NUM_MW)
#define QP_TO_MW(ntb, qp) ((qp) % ntb_get_max_mw(ntb))
#define NTB_QP_DEF_NUM_ENTRIES 100
#define NTB_LINK_DOWN_TIMEOUT 10
@ -492,7 +492,11 @@ ntb_transport_init(struct ntb_softc *ntb)
struct ntb_netdev *nt = &net_softc;
int rc, i;
nt->max_qps = max_num_clients;
if (max_num_clients == 0)
nt->max_qps = MIN(ntb_get_max_cbs(ntb), ntb_get_max_mw(ntb));
else
nt->max_qps = MIN(ntb_get_max_cbs(ntb), max_num_clients);
ntb_register_transport(ntb, nt);
mtx_init(&nt->tx_lock, "ntb transport tx", NULL, MTX_DEF);
mtx_init(&nt->rx_lock, "ntb transport rx", NULL, MTX_DEF);
@ -544,7 +548,7 @@ ntb_transport_free(void *transport)
ntb_unregister_event_callback(ntb);
for (i = 0; i < NTB_NUM_MW; i++)
for (i = 0; i < NTB_MAX_NUM_MW; i++)
ntb_free_mw(nt, i);
free(nt->qps, M_NTB_IF);
@ -556,7 +560,10 @@ ntb_transport_init_queue(struct ntb_netdev *nt, unsigned int qp_num)
{
struct ntb_transport_qp *qp;
unsigned int num_qps_mw, tx_size;
uint8_t mw_num = QP_TO_MW(qp_num);
uint8_t mw_num, mw_max;
mw_max = ntb_get_max_mw(nt->ntb);
mw_num = QP_TO_MW(nt->ntb, qp_num);
qp = &nt->qps[qp_num];
qp->qp_num = qp_num;
@ -566,15 +573,15 @@ ntb_transport_init_queue(struct ntb_netdev *nt, unsigned int qp_num)
qp->client_ready = NTB_LINK_DOWN;
qp->event_handler = NULL;
if (nt->max_qps % NTB_NUM_MW && mw_num + 1 < nt->max_qps / NTB_NUM_MW)
num_qps_mw = nt->max_qps / NTB_NUM_MW + 1;
if (nt->max_qps % mw_max && mw_num + 1 < nt->max_qps / mw_max)
num_qps_mw = nt->max_qps / mw_max + 1;
else
num_qps_mw = nt->max_qps / NTB_NUM_MW;
num_qps_mw = nt->max_qps / mw_max;
tx_size = (unsigned int) ntb_get_mw_size(qp->ntb, mw_num) / num_qps_mw;
qp->rx_info = (struct ntb_rx_info *)
((char *)ntb_get_mw_vbase(qp->ntb, mw_num) +
(qp_num / NTB_NUM_MW * tx_size));
(qp_num / mw_max * tx_size));
tx_size -= sizeof(struct ntb_rx_info);
qp->tx_mw = qp->rx_info + 1;
@ -1048,10 +1055,7 @@ ntb_transport_link_work(void *arg)
uint32_t val, i, num_mw;
int rc;
if (ntb_has_feature(ntb, NTB_REGS_THRU_MW))
num_mw = NTB_NUM_MW - 1;
else
num_mw = NTB_NUM_MW;
num_mw = ntb_get_max_mw(ntb);
/* send the local info, in the opposite order of the way we read it */
for (i = 0; i < num_mw; i++) {
@ -1136,7 +1140,7 @@ ntb_transport_link_work(void *arg)
return;
free_mws:
for (i = 0; i < NTB_NUM_MW; i++)
for (i = 0; i < NTB_MAX_NUM_MW; i++)
ntb_free_mw(nt, i);
out:
if (ntb_query_link_status(ntb))
@ -1207,17 +1211,20 @@ ntb_transport_setup_qp_mw(struct ntb_netdev *nt, unsigned int qp_num)
struct ntb_transport_qp *qp = &nt->qps[qp_num];
void *offset;
unsigned int rx_size, num_qps_mw;
uint8_t mw_num = QP_TO_MW(qp_num);
uint8_t mw_num, mw_max;
unsigned int i;
if (nt->max_qps % NTB_NUM_MW && mw_num + 1 < nt->max_qps / NTB_NUM_MW)
num_qps_mw = nt->max_qps / NTB_NUM_MW + 1;
mw_max = ntb_get_max_mw(nt->ntb);
mw_num = QP_TO_MW(nt->ntb, qp_num);
if (nt->max_qps % mw_max && mw_num + 1 < nt->max_qps / mw_max)
num_qps_mw = nt->max_qps / mw_max + 1;
else
num_qps_mw = nt->max_qps / NTB_NUM_MW;
num_qps_mw = nt->max_qps / mw_max;
rx_size = (unsigned int) nt->mw[mw_num].size / num_qps_mw;
qp->remote_rx_info = (void *)((uint8_t *)nt->mw[mw_num].virt_addr +
(qp_num / NTB_NUM_MW * rx_size));
(qp_num / mw_max * rx_size));
rx_size -= sizeof(struct ntb_rx_info);
qp->rx_buff = qp->remote_rx_info + 1;

View File

@ -127,9 +127,11 @@ struct ntb_softc {
void *ntb_transport;
ntb_event_callback event_cb;
struct ntb_db_cb *db_cb;
struct ntb_db_cb *db_cb;
uint8_t max_cbs;
struct {
uint8_t max_mw;
uint8_t max_spads;
uint8_t max_db_bits;
uint8_t msix_cnt;
@ -321,6 +323,8 @@ ntb_attach(device_t device)
if (error)
goto out;
ntb->limits.max_mw = NTB_MAX_NUM_MW;
error = ntb_map_pci_bars(ntb);
if (error)
goto out;
@ -533,6 +537,7 @@ ntb_setup_xeon_msix(struct ntb_softc *ntb, uint32_t num_vectors)
* slot, from which they will never be called back.
*/
ntb->db_cb[num_vectors - 1].reserved = true;
ntb->max_cbs--;
return (0);
}
@ -649,16 +654,32 @@ ntb_setup_interrupts(struct ntb_softc *ntb)
} else
num_vectors = 1;
/*
* If allocating MSI-X interrupts succeeds, limit callbacks to the
* number of MSI-X slots available.
*/
ntb_create_callbacks(ntb, num_vectors);
if (ntb->type == NTB_XEON)
rc = ntb_setup_xeon_msix(ntb, num_vectors);
else
rc = ntb_setup_soc_msix(ntb, num_vectors);
if (rc != 0)
if (rc != 0) {
device_printf(ntb->device,
"Error allocating MSI-X interrupts: %d\n", rc);
/*
* If allocating MSI-X interrupts failed and we're forced to
* use legacy INTx anyway, the only limit on individual
* callbacks is the number of doorbell bits.
*
* CEM: This seems odd to me but matches the behavior of the
* Linux driver ca. September 2013
*/
ntb_free_callbacks(ntb);
ntb_create_callbacks(ntb, ntb->limits.max_db_bits);
}
if (ntb->type == NTB_XEON && rc == ENOSPC)
rc = ntb_setup_legacy_interrupt(ntb);
@ -842,6 +863,7 @@ ntb_create_callbacks(struct ntb_softc *ntb, uint32_t num_vectors)
{
uint32_t i;
ntb->max_cbs = num_vectors;
ntb->db_cb = malloc(num_vectors * sizeof(*ntb->db_cb), M_NTB,
M_ZERO | M_WAITOK);
for (i = 0; i < num_vectors; i++) {
@ -857,10 +879,11 @@ ntb_free_callbacks(struct ntb_softc *ntb)
{
uint8_t i;
for (i = 0; i < ntb->limits.max_db_bits; i++)
for (i = 0; i < ntb->max_cbs; i++)
ntb_unregister_db_callback(ntb, i);
free(ntb->db_cb, M_NTB);
ntb->max_cbs = 0;
}
static struct ntb_hw_info *
@ -989,14 +1012,16 @@ ntb_setup_xeon(struct ntb_softc *ntb)
* This should already be the case based on the driver defaults, but
* write the limit registers first just in case.
*/
if (HAS_FEATURE(NTB_REGS_THRU_MW))
if (HAS_FEATURE(NTB_REGS_THRU_MW)) {
/* Reserve the last MW for mapping remote spad */
ntb->limits.max_mw--;
/*
* Set the Limit register to 4k, the minimum size, to prevent
* an illegal access.
*/
ntb_reg_write(8, XEON_PBAR4LMT_OFFSET,
ntb_get_mw_size(ntb, 1) + 0x1000);
else
} else
/*
* Disable the limit register, just in case it is set to
* something silly.
@ -1432,8 +1457,7 @@ ntb_register_db_callback(struct ntb_softc *ntb, unsigned int idx, void *data,
{
struct ntb_db_cb *db_cb = &ntb->db_cb[idx];
if (idx >= ntb->allocated_interrupts || db_cb->callback ||
db_cb->reserved) {
if (idx >= ntb->max_cbs || db_cb->callback != NULL || db_cb->reserved) {
device_printf(ntb->device, "Invalid Index.\n");
return (EINVAL);
}
@ -1459,7 +1483,7 @@ void
ntb_unregister_db_callback(struct ntb_softc *ntb, unsigned int idx)
{
if (idx >= ntb->allocated_interrupts || !ntb->db_cb[idx].callback)
if (idx >= ntb->max_cbs || ntb->db_cb[idx].callback == NULL)
return;
mask_ldb_interrupt(ntb, idx);
@ -1518,12 +1542,12 @@ ntb_register_transport(struct ntb_softc *ntb, void *transport)
void
ntb_unregister_transport(struct ntb_softc *ntb)
{
int i;
uint8_t i;
if (ntb->ntb_transport == NULL)
return;
for (i = 0; i < ntb->allocated_interrupts; i++)
for (i = 0; i < ntb->max_cbs; i++)
ntb_unregister_db_callback(ntb, i);
ntb_unregister_event_callback(ntb);
@ -1546,6 +1570,20 @@ ntb_get_max_spads(struct ntb_softc *ntb)
return (ntb->limits.max_spads);
}
uint8_t
ntb_get_max_cbs(struct ntb_softc *ntb)
{
return (ntb->max_cbs);
}
uint8_t
ntb_get_max_mw(struct ntb_softc *ntb)
{
return (ntb->limits.max_mw);
}
/**
* ntb_write_local_spad() - write to the secondary scratchpad register
* @ntb: pointer to ntb_softc instance
@ -1658,7 +1696,7 @@ void *
ntb_get_mw_vbase(struct ntb_softc *ntb, unsigned int mw)
{
if (mw >= NTB_NUM_MW)
if (mw >= ntb_get_max_mw(ntb))
return (NULL);
return (ntb->bar_info[NTB_MW_TO_BAR(mw)].vbase);
@ -1668,7 +1706,7 @@ vm_paddr_t
ntb_get_mw_pbase(struct ntb_softc *ntb, unsigned int mw)
{
if (mw >= NTB_NUM_MW)
if (mw >= ntb_get_max_mw(ntb))
return (0);
return (ntb->bar_info[NTB_MW_TO_BAR(mw)].pbase);
@ -1687,7 +1725,7 @@ u_long
ntb_get_mw_size(struct ntb_softc *ntb, unsigned int mw)
{
if (mw >= NTB_NUM_MW)
if (mw >= ntb_get_max_mw(ntb))
return (0);
return (ntb->bar_info[NTB_MW_TO_BAR(mw)].size);
@ -1707,7 +1745,7 @@ void
ntb_set_mw_addr(struct ntb_softc *ntb, unsigned int mw, uint64_t addr)
{
if (mw >= NTB_NUM_MW)
if (mw >= ntb_get_max_mw(ntb))
return;
switch (NTB_MW_TO_BAR(mw)) {

View File

@ -31,7 +31,7 @@
struct ntb_softc;
#define NTB_NUM_MW 2
#define NTB_MAX_NUM_MW 2
enum ntb_link_event {
NTB_LINK_DOWN = 0,
@ -61,6 +61,8 @@ void *ntb_find_transport(struct ntb_softc *ntb);
struct ntb_softc *ntb_register_transport(struct ntb_softc *ntb,
void *transport);
void ntb_unregister_transport(struct ntb_softc *ntb);
uint8_t ntb_get_max_cbs(struct ntb_softc *ntb);
uint8_t ntb_get_max_mw(struct ntb_softc *ntb);
uint8_t ntb_get_max_spads(struct ntb_softc *ntb);
int ntb_write_local_spad(struct ntb_softc *ntb, unsigned int idx, uint32_t val);
int ntb_read_local_spad(struct ntb_softc *ntb, unsigned int idx, uint32_t *val);