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);
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;
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
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);
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);
{
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;
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;
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++) {
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))
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;
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;
if (error)
goto out;
+ ntb->limits.max_mw = NTB_MAX_NUM_MW;
+
error = ntb_map_pci_bars(ntb);
if (error)
goto out;
* slot, from which they will never be called back.
*/
ntb->db_cb[num_vectors - 1].reserved = true;
+ ntb->max_cbs--;
return (0);
}
} 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);
{
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++) {
{
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 *
* 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.
{
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);
}
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);
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);
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
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);
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);
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);
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)) {
struct ntb_softc;
-#define NTB_NUM_MW 2
+#define NTB_MAX_NUM_MW 2
enum ntb_link_event {
NTB_LINK_DOWN = 0,
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);