Once more refactor KPI between ntb_transport(4) and if_ntb(4)..

New design allows to attach multiple consumers to ntb_transport(4) instance.
Previous design obtained from Linux theoretically allowed that, but was not
practically usable (Linux also has only one consumer driver now).
This commit is contained in:
Alexander Motin 2016-07-29 17:15:41 +00:00
parent 793172ea88
commit 6bd57d14ed
5 changed files with 153 additions and 93 deletions

View File

@ -25,7 +25,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd July 10, 2016
.Dd July 29, 2016
.Dt IF_NTB 4
.Os
.Sh NAME
@ -49,7 +49,7 @@ The following tunables are settable from the
.Bl -ohang
.It Va hw.if_ntb.num_queues
Number of transport queues to use per interface.
Default is 1.
Default is unlimited.
.El
.Sh DESCRIPTION
The
@ -84,3 +84,6 @@ Later improvements were done by
.An Conrad E. Meyer Aq Mt cem@FreeBSD.org
and
.An Alexander Motin Aq Mt mav@FreeBSD.org .
.Sh BUGS
Linux supports only one queue per interface, so manual configuration
may be required for compatibility.

View File

@ -25,7 +25,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd July 10, 2016
.Dd July 29, 2016
.Dt NTB_TRANSPORT 4
.Os
.Sh NAME
@ -44,10 +44,15 @@ The following tunables are settable from the
.It Va hw.ntb_transport.debug_level
Driver debug level.
The default value is 0, higher means more verbose.
.It Va hw.ntb_transport.max_num_clients
Number of bidirectional queues to setup.
The default value is 0, that means one queue per available memory window.
Maximal number is limited by number of doorbells.
.It Va hint.ntb_transport. Ns Ar X Ns Va .config
Configures queues allocation for consumer devices, separated by commas.
Each device can be configured as: "<name>[:<queues>]", where:
.Va name
is a name of the driver which should attach the device (empty means any),
.Va queues
is a number of queues to allocate (empty means automatic),
The default configuration is empty string, which means single device
with one queue per memory window allowing any driver attachment.
.El
.Sh DESCRIPTION
The

View File

@ -76,7 +76,7 @@ __FBSDID("$FreeBSD$");
static SYSCTL_NODE(_hw, OID_AUTO, if_ntb, CTLFLAG_RW, 0, "if_ntb");
static unsigned g_if_ntb_num_queues = 1;
static unsigned g_if_ntb_num_queues = UINT_MAX;
SYSCTL_UINT(_hw_if_ntb, OID_AUTO, num_queues, CTLFLAG_RWTUN,
&g_if_ntb_num_queues, 0, "Number of queues per interface");
@ -144,7 +144,8 @@ ntb_net_attach(device_t dev)
if_initname(ifp, device_get_name(dev), device_get_unit(dev));
if_setdev(ifp, dev);
sc->num_queues = g_if_ntb_num_queues;
sc->num_queues = min(g_if_ntb_num_queues,
ntb_transport_queue_count(dev));
sc->queues = malloc(sc->num_queues * sizeof(struct ntb_net_queue),
M_DEVBUF, M_WAITOK | M_ZERO);
sc->mtu = INT_MAX;
@ -152,8 +153,7 @@ ntb_net_attach(device_t dev)
q = &sc->queues[i];
q->sc = sc;
q->ifp = ifp;
q->qp = ntb_transport_create_queue(q,
device_get_parent(dev), &handlers);
q->qp = ntb_transport_create_queue(dev, i, &handlers, q);
if (q->qp == NULL)
break;
sc->mtu = imin(sc->mtu, ntb_transport_max_size(q->qp));
@ -167,6 +167,7 @@ ntb_net_attach(device_t dev)
callout_init(&q->queue_full, 1);
}
sc->num_queues = i;
device_printf(dev, "%d queue(s)\n", sc->num_queues);
if_setinitfn(ifp, ntb_net_init);
if_setsoftc(ifp, sc);

View File

@ -43,7 +43,6 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/bitset.h>
#include <sys/bus.h>
#include <sys/ktr.h>
#include <sys/limits.h>
@ -64,13 +63,6 @@ __FBSDID("$FreeBSD$");
#include "ntb.h"
#include "ntb_transport.h"
#define QP_SETSIZE 64
BITSET_DEFINE(_qpset, QP_SETSIZE);
#define test_bit(pos, addr) BIT_ISSET(QP_SETSIZE, (pos), (addr))
#define set_bit(pos, addr) BIT_SET(QP_SETSIZE, (pos), (addr))
#define clear_bit(pos, addr) BIT_CLR(QP_SETSIZE, (pos), (addr))
#define ffs_bit(addr) BIT_FFS(QP_SETSIZE, (addr))
#define KTR_NTB KTR_SPARE3
#define NTB_TRANSPORT_VERSION 4
@ -94,12 +86,6 @@ SYSCTL_UQUAD(_hw_ntb_transport, OID_AUTO, max_mw_size, CTLFLAG_RDTUN, &max_mw_si
"If enabled (non-zero), limit the size of large memory windows. "
"Both sides of the NTB MUST set the same value here.");
static unsigned max_num_clients;
SYSCTL_UINT(_hw_ntb_transport, 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.");
static unsigned enable_xeon_watchdog;
SYSCTL_UINT(_hw_ntb_transport, OID_AUTO, enable_xeon_watchdog, CTLFLAG_RDTUN,
&enable_xeon_watchdog, 0, "If non-zero, write a register every second to "
@ -200,14 +186,21 @@ struct ntb_transport_mw {
bus_addr_t dma_addr;
};
struct ntb_transport_child {
device_t dev;
int qpoff;
int qpcnt;
struct ntb_transport_child *next;
};
struct ntb_transport_ctx {
device_t dev;
struct ntb_transport_child *child;
struct ntb_transport_mw *mw_vec;
struct ntb_transport_qp *qp_vec;
struct _qpset qp_bitmap;
struct _qpset qp_bitmap_free;
unsigned mw_count;
unsigned qp_count;
uint64_t qp_bitmap;
volatile bool link_is_up;
struct callout link_work;
struct callout link_watchdog;
@ -242,7 +235,6 @@ enum {
NTBT_MW0_SZ_LOW,
NTBT_MW1_SZ_HIGH,
NTBT_MW1_SZ_LOW,
NTBT_MAX_SPAD,
/*
* Some NTB-using hardware have a watchdog to work around NTB hangs; if
@ -332,13 +324,44 @@ static int
ntb_transport_attach(device_t dev)
{
struct ntb_transport_ctx *nt = device_get_softc(dev);
struct ntb_transport_child **cpp = &nt->child;
struct ntb_transport_child *nc;
struct ntb_transport_mw *mw;
uint64_t qp_bitmap;
int rc;
unsigned i;
uint64_t db_bitmap;
int rc, i, db_count, spad_count, qp, qpu, qpo, qpt;
char cfg[128] = "";
char buf[32];
char *n, *np, *c, *name;
nt->dev = dev;
nt->mw_count = ntb_mw_count(dev);
spad_count = ntb_spad_count(dev);
db_bitmap = ntb_db_valid_mask(dev);
db_count = flsll(db_bitmap);
KASSERT(db_bitmap == (1 << db_count) - 1,
("Doorbells are not sequential (%jx).\n", db_bitmap));
device_printf(dev, "%d memory windows, %d scratchpads, "
"%d doorbells\n", nt->mw_count, spad_count, db_count);
if (nt->mw_count == 0) {
device_printf(dev, "At least 1 memory window required.\n");
return (ENXIO);
}
if (spad_count < 6) {
device_printf(dev, "At least 6 scratchpads required.\n");
return (ENXIO);
}
if (spad_count < 4 + 2 * nt->mw_count) {
nt->mw_count = (spad_count - 4) / 2;
device_printf(dev, "Scratchpads enough only for %d "
"memory windows.\n", nt->mw_count);
}
if (db_bitmap == 0) {
device_printf(dev, "At least one doorbell required.\n");
return (ENXIO);
}
nt->mw_vec = malloc(nt->mw_count * sizeof(*nt->mw_vec), M_NTB_T,
M_WAITOK | M_ZERO);
for (i = 0; i < nt->mw_count; i++) {
@ -360,25 +383,59 @@ ntb_transport_attach(device_t dev)
ntb_printf(0, "Unable to set mw%d caching\n", i);
}
qp_bitmap = ntb_db_valid_mask(dev);
nt->qp_count = flsll(qp_bitmap);
KASSERT(nt->qp_count != 0, ("bogus db bitmap"));
nt->qp_count -= 1;
qpu = 0;
qpo = imin(db_count, nt->mw_count);
qpt = db_count;
if (max_num_clients != 0 && max_num_clients < nt->qp_count)
nt->qp_count = max_num_clients;
else if (nt->mw_count < nt->qp_count)
nt->qp_count = nt->mw_count;
KASSERT(nt->qp_count <= QP_SETSIZE, ("invalid qp_count"));
snprintf(buf, sizeof(buf), "hint.%s.%d.config", device_get_name(dev),
device_get_unit(dev));
TUNABLE_STR_FETCH(buf, cfg, sizeof(cfg));
n = cfg;
i = 0;
while ((c = strsep(&n, ",")) != NULL) {
np = c;
name = strsep(&np, ":");
if (name != NULL && name[0] == 0)
name = NULL;
qp = (np && np[0] != 0) ? strtol(np, NULL, 10) : qpo - qpu;
if (qp <= 0)
qp = 1;
if (qp > qpt - qpu) {
device_printf(dev, "Not enough resources for config\n");
break;
}
nc = malloc(sizeof(*nc), M_DEVBUF, M_WAITOK | M_ZERO);
nc->qpoff = qpu;
nc->qpcnt = qp;
nc->dev = device_add_child(dev, name, -1);
if (nc->dev == NULL) {
device_printf(dev, "Can not add child.\n");
break;
}
device_set_ivars(nc->dev, nc);
*cpp = nc;
cpp = &nc->next;
if (bootverbose) {
device_printf(dev, "%d \"%s\": queues %d",
i, name, qpu);
if (qp > 1)
printf("-%d", qpu + qp - 1);
printf("\n");
}
qpu += qp;
i++;
}
nt->qp_count = qpu;
nt->qp_vec = malloc(nt->qp_count * sizeof(*nt->qp_vec), M_NTB_T,
M_WAITOK | M_ZERO);
for (i = 0; i < nt->qp_count; i++) {
set_bit(i, &nt->qp_bitmap);
set_bit(i, &nt->qp_bitmap_free);
for (i = 0; i < nt->qp_count; i++)
ntb_transport_init_queue(nt, i);
}
callout_init(&nt->link_work, 0);
callout_init(&nt->link_watchdog, 0);
@ -394,10 +451,7 @@ ntb_transport_attach(device_t dev)
if (enable_xeon_watchdog != 0)
callout_reset(&nt->link_watchdog, 0, xeon_link_watchdog_hb, nt);
/* Attach children to this transport */
device_add_child(dev, NULL, -1);
bus_generic_attach(dev);
return (0);
err:
@ -410,25 +464,25 @@ static int
ntb_transport_detach(device_t dev)
{
struct ntb_transport_ctx *nt = device_get_softc(dev);
struct _qpset qp_bitmap_alloc;
uint8_t i;
struct ntb_transport_child **cpp = &nt->child;
struct ntb_transport_child *nc;
int error = 0, i;
/* Detach & delete all children */
device_delete_children(dev);
while ((nc = *cpp) != NULL) {
*cpp = (*cpp)->next;
error = device_delete_child(dev, nc->dev);
if (error)
break;
free(nc, M_DEVBUF);
}
KASSERT(nt->qp_bitmap == 0,
("Some queues not freed on detach (%jx)", nt->qp_bitmap));
ntb_transport_link_cleanup(nt);
taskqueue_drain(taskqueue_swi, &nt->link_cleanup);
callout_drain(&nt->link_work);
callout_drain(&nt->link_watchdog);
BIT_COPY(QP_SETSIZE, &nt->qp_bitmap, &qp_bitmap_alloc);
BIT_NAND(QP_SETSIZE, &qp_bitmap_alloc, &nt->qp_bitmap_free);
/* Verify that all the QPs are freed */
for (i = 0; i < nt->qp_count; i++)
if (test_bit(i, &qp_bitmap_alloc))
ntb_transport_free_queue(&nt->qp_vec[i]);
ntb_link_disable(dev);
ntb_clear_ctx(dev);
@ -440,6 +494,14 @@ ntb_transport_detach(device_t dev)
return (0);
}
int
ntb_transport_queue_count(device_t dev)
{
struct ntb_transport_child *nc = device_get_ivars(dev);
return (nc->qpcnt);
}
static void
ntb_transport_init_queue(struct ntb_transport_ctx *nt, unsigned int qp_num)
{
@ -507,6 +569,7 @@ ntb_transport_init_queue(struct ntb_transport_ctx *nt, unsigned int qp_num)
void
ntb_transport_free_queue(struct ntb_transport_qp *qp)
{
struct ntb_transport_ctx *nt = qp->transport;
struct ntb_queue_entry *entry;
if (qp == NULL)
@ -532,7 +595,7 @@ ntb_transport_free_queue(struct ntb_transport_qp *qp)
while ((entry = ntb_list_rm(&qp->ntb_tx_free_q_lock, &qp->tx_free_q)))
free(entry, M_NTB_T);
set_bit(qp->qp_num, &qp->transport->qp_bitmap_free);
nt->qp_bitmap &= ~(1 << qp->qp_num);
}
/**
@ -550,24 +613,20 @@ ntb_transport_free_queue(struct ntb_transport_qp *qp)
* RETURNS: pointer to newly created ntb_queue, NULL on error.
*/
struct ntb_transport_qp *
ntb_transport_create_queue(void *data, device_t dev,
const struct ntb_queue_handlers *handlers)
ntb_transport_create_queue(device_t dev, int q,
const struct ntb_queue_handlers *handlers, void *data)
{
struct ntb_transport_ctx *nt = device_get_softc(dev);
struct ntb_transport_child *nc = device_get_ivars(dev);
struct ntb_transport_ctx *nt = device_get_softc(device_get_parent(dev));
struct ntb_queue_entry *entry;
struct ntb_transport_qp *qp;
unsigned int free_queue;
int i;
free_queue = ffs_bit(&nt->qp_bitmap_free);
if (free_queue == 0)
if (q < 0 || q >= nc->qpcnt)
return (NULL);
/* decrement free_queue to make it zero based */
free_queue--;
qp = &nt->qp_vec[free_queue];
clear_bit(qp->qp_num, &nt->qp_bitmap_free);
qp = &nt->qp_vec[nc->qpoff + q];
nt->qp_bitmap |= (1 << qp->qp_num);
qp->cb_data = data;
qp->rx_handler = handlers->rx_handler;
qp->tx_handler = handlers->tx_handler;
@ -944,24 +1003,19 @@ ntb_transport_doorbell_callback(void *data, uint32_t vector)
{
struct ntb_transport_ctx *nt = data;
struct ntb_transport_qp *qp;
struct _qpset db_bits;
uint64_t vec_mask;
unsigned qp_num;
BIT_COPY(QP_SETSIZE, &nt->qp_bitmap, &db_bits);
BIT_NAND(QP_SETSIZE, &db_bits, &nt->qp_bitmap_free);
vec_mask = ntb_db_vector_mask(nt->dev, vector);
vec_mask &= nt->qp_bitmap;
if ((vec_mask & (vec_mask - 1)) != 0)
vec_mask &= ntb_db_read(nt->dev);
while (vec_mask != 0) {
qp_num = ffsll(vec_mask) - 1;
if (test_bit(qp_num, &db_bits)) {
qp = &nt->qp_vec[qp_num];
if (qp->link_is_up)
taskqueue_enqueue(qp->rxc_tq, &qp->rxc_db_work);
}
qp = &nt->qp_vec[qp_num];
if (qp->link_is_up)
taskqueue_enqueue(qp->rxc_tq, &qp->rxc_db_work);
vec_mask &= ~(1ull << qp_num);
}
@ -1219,19 +1273,16 @@ static void
ntb_transport_link_cleanup(struct ntb_transport_ctx *nt)
{
struct ntb_transport_qp *qp;
struct _qpset qp_bitmap_alloc;
unsigned i;
BIT_COPY(QP_SETSIZE, &nt->qp_bitmap, &qp_bitmap_alloc);
BIT_NAND(QP_SETSIZE, &qp_bitmap_alloc, &nt->qp_bitmap_free);
int i;
/* Pass along the info to any clients */
for (i = 0; i < nt->qp_count; i++)
if (test_bit(i, &qp_bitmap_alloc)) {
for (i = 0; i < nt->qp_count; i++) {
if ((nt->qp_bitmap & (1 << i)) != 0) {
qp = &nt->qp_vec[i];
ntb_qp_link_cleanup(qp);
callout_drain(&qp->link_work);
}
}
if (!nt->link_is_up)
callout_drain(&nt->link_work);
@ -1241,8 +1292,7 @@ ntb_transport_link_cleanup(struct ntb_transport_ctx *nt)
* goes down, blast them now to give them a sane value the next
* time they are accessed
*/
for (i = 0; i < NTBT_MAX_SPAD; i++)
ntb_spad_write(nt->dev, i, 0);
ntb_spad_clear(nt->dev);
}
static void

View File

@ -43,12 +43,13 @@ struct ntb_queue_handlers {
void (*event_handler)(void *data, enum ntb_link_event status);
};
int ntb_transport_queue_count(device_t dev);
struct ntb_transport_qp *
ntb_transport_create_queue(device_t dev, int q,
const struct ntb_queue_handlers *handlers, void *data);
void ntb_transport_free_queue(struct ntb_transport_qp *qp);
unsigned char ntb_transport_qp_num(struct ntb_transport_qp *qp);
unsigned int ntb_transport_max_size(struct ntb_transport_qp *qp);
struct ntb_transport_qp *
ntb_transport_create_queue(void *data, device_t dev,
const struct ntb_queue_handlers *handlers);
void ntb_transport_free_queue(struct ntb_transport_qp *qp);
int ntb_transport_rx_enqueue(struct ntb_transport_qp *qp, void *cb, void *data,
unsigned int len);
int ntb_transport_tx_enqueue(struct ntb_transport_qp *qp, void *cb, void *data,