]> xenbits.xensource.com Git - people/julieng/freebsd.git/commitdiff
if_ntb: MFV e26a5843: Move MW/DB management to if_ntb
authorcem <cem@FreeBSD.org>
Sun, 18 Oct 2015 20:20:57 +0000 (20:20 +0000)
committercem <cem@FreeBSD.org>
Sun, 18 Oct 2015 20:20:57 +0000 (20:20 +0000)
This is the last e26a5843 patch.  The general thrust of the rewrite was
to move more responsibility for Memory Window and Doorbell interrupt
management from the ntb_hw driver to if_ntb.

A number of APIs have been added, removed, or replaced.  The old
DB callback mechanism has been excised.  Instead, callers (if_ntb) are
responsible for configuring MWs and handling their interrupts more
directly.

This adds a tunable, hw.ntb.max_mw_size, allowing users to limit the
size of memory windows used by if_ntb (identical to the Linux modparam
of the same name).

Despite attempts to keep mechanical name changes to separate commits,
some have snuck in here.  At least the driver should be much more
similar to the latest Linux one now -- making porting fixes easier.

Authored by: Allen Hubbe
Obtained from: Linux (Dual BSD/GPL driver)
Sponsored by: EMC / Isilon Storage Division

sys/dev/ntb/if_ntb/if_ntb.c
sys/dev/ntb/ntb_hw/ntb_hw.c
sys/dev/ntb/ntb_hw/ntb_hw.h
sys/dev/ntb/ntb_hw/ntb_regs.h

index 3e45a8968ff84fd2d5d716cfde95a50c12a2c33a..6a1fd92e5c885adda77d417fb5919f29babf7694 100644 (file)
@@ -81,12 +81,22 @@ BITSET_DEFINE(_qpset, QP_SETSIZE);
 
 #define KTR_NTB KTR_SPARE3
 
-#define NTB_TRANSPORT_VERSION  3
+#define NTB_TRANSPORT_VERSION  4
 #define NTB_RX_MAX_PKTS                64
 #define        NTB_RXQ_SIZE            300
 
+enum ntb_link_event {
+       NTB_LINK_DOWN = 0,
+       NTB_LINK_UP,
+};
+
 static unsigned int transport_mtu = 0x4000 + ETHER_HDR_LEN + ETHER_CRC_LEN;
 
+static uint64_t max_mw_size;
+SYSCTL_UQUAD(_hw_ntb, OID_AUTO, max_mw_size, CTLFLAG_RDTUN, &max_mw_size, 0,
+    "If enabled (non-zero), limit the size of large memory windows. "
+    "Both sides of the NTB MUST set the same value here.");
+
 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.  "
@@ -99,11 +109,15 @@ struct ntb_queue_entry {
        /* ntb_queue list reference */
        STAILQ_ENTRY(ntb_queue_entry) entry;
 
-       /* info on data to be transfered */
+       /* info on data to be transferred */
        void            *cb_data;
        void            *buf;
-       uint64_t        len;
-       uint64_t        flags;
+       unsigned        len;
+       unsigned        flags;
+
+       struct ntb_transport_qp         *qp;
+       struct ntb_payload_header       *x_hdr;
+       unsigned        index;
 };
 
 struct ntb_rx_info {
@@ -128,6 +142,7 @@ struct ntb_transport_qp {
        struct ntb_queue_list   tx_free_q;
        struct mtx              ntb_tx_free_q_lock;
        void                    *tx_mw;
+       bus_addr_t              tx_mw_phys;
        uint64_t                tx_index;
        uint64_t                tx_max_entry;
        uint64_t                tx_max_frame;
@@ -139,6 +154,7 @@ struct ntb_transport_qp {
        struct mtx              ntb_rx_pend_q_lock;
        struct mtx              ntb_rx_free_q_lock;
        struct task             rx_completion_task;
+       struct task             rxc_db_work;
        void                    *rx_buff;
        uint64_t                rx_index;
        uint64_t                rx_max_entry;
@@ -171,11 +187,18 @@ struct ntb_queue_handlers {
        void (*event_handler)(void *data, enum ntb_link_event status);
 };
 
-
 struct ntb_transport_mw {
+       vm_paddr_t      phys_addr;
+       size_t          phys_size;
+       size_t          xlat_align;
+       size_t          xlat_align_size;
+       /* Tx buff is off vbase / phys_addr */
+       void            *vbase;
        size_t          xlat_size;
+       size_t          buff_size;
+       /* Rx buff is off virt_addr / dma_addr */
        void            *virt_addr;
-       vm_paddr_t      dma_addr;
+       bus_addr_t      dma_addr;
 };
 
 struct ntb_transport_ctx {
@@ -184,15 +207,18 @@ struct ntb_transport_ctx {
        struct ntb_transport_mw mw_vec[NTB_MAX_NUM_MW];
        struct ntb_transport_qp *qp_vec;
        struct _qpset           qp_bitmap;
+       struct _qpset           qp_bitmap_free;
        unsigned                mw_count;
        unsigned                qp_count;
        enum ntb_link_event     link_is_up;
        struct callout          link_work;
-       struct ntb_transport_qp *qp;
        uint64_t                bufsize;
        u_char                  eaddr[ETHER_ADDR_LEN];
        struct mtx              tx_lock;
        struct mtx              rx_lock;
+
+       /* The hardcoded single queuepair in ntb_setup_interface() */
+       struct ntb_transport_qp *qp;
 };
 
 static struct ntb_transport_ctx net_softc;
@@ -244,31 +270,32 @@ static void ntb_net_rx_handler(struct ntb_transport_qp *qp, void *qp_data,
     void *data, int len);
 static void ntb_net_event_handler(void *data, enum ntb_link_event status);
 static int ntb_transport_init(struct ntb_softc *ntb);
-static void ntb_transport_free(void *transport);
+static void ntb_transport_free(struct ntb_transport_ctx *);
 static void ntb_transport_init_queue(struct ntb_transport_ctx *nt,
     unsigned int qp_num);
 static void ntb_transport_free_queue(struct ntb_transport_qp *qp);
-static struct ntb_transport_qp * ntb_transport_create_queue(void *data,
+static struct ntb_transport_qp *ntb_transport_create_queue(void *data,
     struct ntb_softc *pdev, const struct ntb_queue_handlers *handlers);
 static void ntb_transport_link_up(struct ntb_transport_qp *qp);
 static int ntb_transport_tx_enqueue(struct ntb_transport_qp *qp, void *cb,
     void *data, unsigned int len);
 static int ntb_process_tx(struct ntb_transport_qp *qp,
     struct ntb_queue_entry *entry);
-static void ntb_tx_copy_task(struct ntb_transport_qp *qp,
+static void ntb_memcpy_tx(struct ntb_transport_qp *qp,
     struct ntb_queue_entry *entry, void *offset);
 static void ntb_qp_full(void *arg);
-static int ntb_transport_rxc_db(void *arg, int dummy);
+static void ntb_transport_rxc_db(void *arg, int pending);
 static void ntb_rx_pendq_full(void *arg);
 static int ntb_process_rxc(struct ntb_transport_qp *qp);
 static void ntb_rx_copy_task(struct ntb_transport_qp *qp,
     struct ntb_queue_entry *entry, void *offset);
-static void ntb_rx_completion_task(void *arg, int pending);
-static void ntb_transport_event_callback(void *data, enum ntb_hw_event event);
+static void ntb_complete_rxc(void *arg, int pending);
+static void ntb_transport_doorbell_callback(void *data, int vector);
+static void ntb_transport_event_callback(void *data);
 static void ntb_transport_link_work(void *arg);
 static int ntb_set_mw(struct ntb_transport_ctx *, int num_mw, unsigned size);
 static void ntb_free_mw(struct ntb_transport_ctx *nt, int num_mw);
-static void ntb_transport_setup_qp_mw(struct ntb_transport_ctx *nt,
+static int ntb_transport_setup_qp_mw(struct ntb_transport_ctx *nt,
     unsigned int qp_num);
 static void ntb_qp_link_work(void *arg);
 static void ntb_transport_link_cleanup(struct ntb_transport_ctx *nt);
@@ -283,6 +310,11 @@ static struct ntb_queue_entry *ntb_list_rm(struct mtx *lock,
 static void create_random_local_eui48(u_char *eaddr);
 static unsigned int ntb_transport_max_size(struct ntb_transport_qp *qp);
 
+static const struct ntb_ctx_ops ntb_transport_ops = {
+       .link_event = ntb_transport_event_callback,
+       .db_event = ntb_transport_doorbell_callback,
+};
+
 MALLOC_DEFINE(M_NTB_IF, "if_ntb", "ntb network driver");
 
 /* Module setup and teardown */
@@ -320,6 +352,7 @@ ntb_setup_interface(void)
        struct ifnet *ifp;
        struct ntb_queue_handlers handlers = { ntb_net_rx_handler,
            ntb_net_tx_handler, ntb_net_event_handler };
+       int rc;
 
        net_softc.ntb = devclass_get_softc(devclass_find("ntb_hw"), 0);
        if (net_softc.ntb == NULL) {
@@ -327,11 +360,16 @@ ntb_setup_interface(void)
                return (ENXIO);
        }
 
-       ntb_transport_init(net_softc.ntb);
+       rc = ntb_transport_init(net_softc.ntb);
+       if (rc != 0) {
+               printf("ntb: Cannot init transport: %d\n", rc);
+               return (rc);
+       }
 
        ifp = net_softc.ifp = if_alloc(IFT_ETHER);
        if (ifp == NULL) {
-               printf("ntb: cannot allocate ifnet structure\n");
+               ntb_transport_free(&net_softc);
+               printf("ntb: Cannot allocate ifnet structure\n");
                return (ENOMEM);
        }
 
@@ -498,83 +536,107 @@ static int
 ntb_transport_init(struct ntb_softc *ntb)
 {
        struct ntb_transport_ctx *nt = &net_softc;
+       struct ntb_transport_mw *mw;
+       uint64_t qp_bitmap;
        int rc;
-       uint8_t i;
+       unsigned i;
 
        nt->mw_count = ntb_mw_count(ntb);
-       if (max_num_clients == 0)
-               nt->qp_count = MIN(ntb_get_max_cbs(ntb), ntb_mw_count(ntb));
-       else
-               nt->qp_count = MIN(ntb_get_max_cbs(ntb), max_num_clients);
+       for (i = 0; i < nt->mw_count; i++) {
+               mw = &nt->mw_vec[i];
+
+               rc = ntb_mw_get_range(ntb, i, &mw->phys_addr, &mw->vbase,
+                   &mw->phys_size, &mw->xlat_align, &mw->xlat_align_size);
+               if (rc != 0)
+                       goto err;
+
+               mw->buff_size = 0;
+               mw->xlat_size = 0;
+               mw->virt_addr = 0;
+               mw->dma_addr = 0;
+       }
+
+       qp_bitmap = ntb_db_valid_mask(ntb);
+       nt->qp_count = flsll(qp_bitmap);
+       KASSERT(nt->qp_count != 0, ("bogus db bitmap"));
+       nt->qp_count -= 1;
+
+       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"));
 
-       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);
 
        nt->qp_vec = malloc(nt->qp_count * sizeof(*nt->qp_vec), M_NTB_IF,
            M_WAITOK | M_ZERO);
 
-       KASSERT(nt->qp_count <= QP_SETSIZE, ("invalid qp_count"));
-
        for (i = 0; i < nt->qp_count; i++) {
                set_bit(i, &nt->qp_bitmap);
+               set_bit(i, &nt->qp_bitmap_free);
                ntb_transport_init_queue(nt, i);
        }
 
        callout_init(&nt->link_work, 0);
 
-       rc = ntb_register_event_callback(ntb, ntb_transport_event_callback);
+       rc = ntb_set_ctx(ntb, nt, &ntb_transport_ops);
        if (rc != 0)
                goto err;
 
-       if (ntb_link_is_up(ntb)) {
-               if (bootverbose)
-                       device_printf(ntb_get_device(ntb), "link up\n");
-               callout_reset(&nt->link_work, 0, ntb_transport_link_work, nt);
-       }
-
+       nt->link_is_up = false;
+       ntb_link_enable(ntb, NTB_SPEED_AUTO, NTB_WIDTH_AUTO);
+       ntb_link_event(ntb);
        return (0);
 
 err:
        free(nt->qp_vec, M_NTB_IF);
-       ntb_unregister_transport(ntb);
+       nt->qp_vec = NULL;
        return (rc);
 }
 
 static void
-ntb_transport_free(void *transport)
+ntb_transport_free(struct ntb_transport_ctx *nt)
 {
-       struct ntb_transport_ctx *nt = transport;
        struct ntb_softc *ntb = nt->ntb;
+       struct _qpset qp_bitmap_alloc;
        uint8_t i;
 
        ntb_transport_link_cleanup(nt);
 
        callout_drain(&nt->link_work);
 
-       /* verify that all the qps are freed */
+       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, &nt->qp_bitmap))
+               if (test_bit(i, &qp_bitmap_alloc))
                        ntb_transport_free_queue(&nt->qp_vec[i]);
 
-       ntb_unregister_event_callback(ntb);
+       ntb_link_disable(ntb);
+       ntb_clear_ctx(ntb);
 
-       for (i = 0; i < NTB_MAX_NUM_MW; i++)
+       for (i = 0; i < nt->mw_count; i++)
                ntb_free_mw(nt, i);
 
        free(nt->qp_vec, M_NTB_IF);
-       ntb_unregister_transport(ntb);
 }
 
 static void
 ntb_transport_init_queue(struct ntb_transport_ctx *nt, unsigned int qp_num)
 {
+       struct ntb_transport_mw *mw;
        struct ntb_transport_qp *qp;
-       unsigned int num_qps_mw, tx_size;
-       uint8_t mw_num, mw_count;
+       vm_paddr_t mw_base;
+       uint64_t mw_size, qp_offset;
+       size_t tx_size;
+       unsigned num_qps_mw, mw_num, mw_count;
 
-       mw_count = ntb_mw_count(nt->ntb);
+       mw_count = nt->mw_count;
        mw_num = QP_TO_MW(nt, qp_num);
+       mw = &nt->mw_vec[mw_num];
 
        qp = &nt->qp_vec[qp_num];
        qp->qp_num = qp_num;
@@ -589,13 +651,22 @@ ntb_transport_init_queue(struct ntb_transport_ctx *nt, unsigned int qp_num)
        else
                num_qps_mw = nt->qp_count / mw_count;
 
-       tx_size = 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 / mw_count * tx_size));
+       mw_base = mw->phys_addr;
+       mw_size = mw->phys_size;
+
+       tx_size = mw_size / num_qps_mw;
+       qp_offset = tx_size * qp_num / mw_count;
+
+       qp->tx_mw = (char *)mw->vbase + qp_offset;
+       KASSERT(qp->tx_mw != NULL, ("uh oh?"));
+
+       /* XXX Assumes that a vm_paddr_t is equivalent to bus_addr_t */
+       qp->tx_mw_phys = mw_base + qp_offset;
+       KASSERT(qp->tx_mw_phys != 0, ("uh oh?"));
+
        tx_size -= sizeof(struct ntb_rx_info);
+       qp->rx_info = (void *)((char *)qp->tx_mw + tx_size);
 
-       qp->tx_mw = qp->rx_info + 1;
        /* Due to house-keeping, there must be at least 2 buffs */
        qp->tx_max_frame = min(transport_mtu + sizeof(struct ntb_payload_header),
            tx_size / 2);
@@ -608,7 +679,8 @@ ntb_transport_init_queue(struct ntb_transport_ctx *nt, unsigned int qp_num)
        mtx_init(&qp->ntb_rx_pend_q_lock, "ntb rx pend q", NULL, MTX_SPIN);
        mtx_init(&qp->ntb_rx_free_q_lock, "ntb rx free q", NULL, MTX_SPIN);
        mtx_init(&qp->ntb_tx_free_q_lock, "ntb tx free q", NULL, MTX_SPIN);
-       TASK_INIT(&qp->rx_completion_task, 0, ntb_rx_completion_task, qp);
+       TASK_INIT(&qp->rx_completion_task, 0, ntb_complete_rxc, qp);
+       TASK_INIT(&qp->rxc_db_work, 0, ntb_transport_rxc_db, qp);
 
        STAILQ_INIT(&qp->rx_pend_q);
        STAILQ_INIT(&qp->rx_free_q);
@@ -625,7 +697,14 @@ ntb_transport_free_queue(struct ntb_transport_qp *qp)
 
        callout_drain(&qp->link_work);
 
-       ntb_unregister_db_callback(qp->ntb, qp->qp_num);
+       ntb_db_set_mask(qp->ntb, 1ull << qp->qp_num);
+       taskqueue_drain(taskqueue_swi, &qp->rxc_db_work);
+       taskqueue_drain(taskqueue_swi, &qp->rx_completion_task);
+
+       qp->cb_data = NULL;
+       qp->rx_handler = NULL;
+       qp->tx_handler = NULL;
+       qp->event_handler = NULL;
 
        while ((entry = ntb_list_rm(&qp->ntb_rx_free_q_lock, &qp->rx_free_q)))
                free(entry, M_NTB_IF);
@@ -636,7 +715,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_IF);
 
-       set_bit(qp->qp_num, &qp->transport->qp_bitmap);
+       set_bit(qp->qp_num, &qp->transport->qp_bitmap_free);
 }
 
 /**
@@ -654,37 +733,34 @@ ntb_transport_free_queue(struct ntb_transport_qp *qp)
  * RETURNS: pointer to newly created ntb_queue, NULL on error.
  */
 static struct ntb_transport_qp *
-ntb_transport_create_queue(void *data, struct ntb_softc *pdev,
+ntb_transport_create_queue(void *data, struct ntb_softc *ntb,
     const struct ntb_queue_handlers *handlers)
 {
        struct ntb_queue_entry *entry;
        struct ntb_transport_qp *qp;
        struct ntb_transport_ctx *nt;
        unsigned int free_queue;
-       int rc, i;
+       int i;
 
-       nt = ntb_find_transport(pdev);
-       if (nt == NULL)
-               goto err;
+       nt = ntb_get_ctx(ntb, NULL);
+       KASSERT(nt != NULL, ("bogus"));
 
        free_queue = ffs_bit(&nt->qp_bitmap);
        if (free_queue == 0)
-               goto err;
+               return (NULL);
 
        /* decrement free_queue to make it zero based */
        free_queue--;
 
-       clear_bit(free_queue, &nt->qp_bitmap);
-
        qp = &nt->qp_vec[free_queue];
+       clear_bit(1ull << qp->qp_num, &nt->qp_bitmap_free);
        qp->cb_data = data;
        qp->rx_handler = handlers->rx_handler;
        qp->tx_handler = handlers->tx_handler;
        qp->event_handler = handlers->event_handler;
 
        for (i = 0; i < NTB_QP_DEF_NUM_ENTRIES; i++) {
-               entry = malloc(sizeof(struct ntb_queue_entry), M_NTB_IF,
-                   M_WAITOK|M_ZERO);
+               entry = malloc(sizeof(*entry), M_NTB_IF, M_WAITOK | M_ZERO);
                entry->cb_data = nt->ifp;
                entry->buf = NULL;
                entry->len = transport_mtu;
@@ -692,26 +768,13 @@ ntb_transport_create_queue(void *data, struct ntb_softc *pdev,
        }
 
        for (i = 0; i < NTB_QP_DEF_NUM_ENTRIES; i++) {
-               entry = malloc(sizeof(struct ntb_queue_entry), M_NTB_IF,
-                   M_WAITOK|M_ZERO);
+               entry = malloc(sizeof(*entry), M_NTB_IF, M_WAITOK | M_ZERO);
                ntb_list_add(&qp->ntb_tx_free_q_lock, entry, &qp->tx_free_q);
        }
 
-       rc = ntb_register_db_callback(qp->ntb, free_queue, qp,
-                                     ntb_transport_rxc_db);
-       if (rc != 0)
-               goto err1;
-
+       ntb_db_clear(ntb, 1ull << qp->qp_num);
+       ntb_db_clear_mask(ntb, 1ull << qp->qp_num);
        return (qp);
-
-err1:
-       while ((entry = ntb_list_rm(&qp->ntb_tx_free_q_lock, &qp->tx_free_q)))
-               free(entry, M_NTB_IF);
-       while ((entry = ntb_list_rm(&qp->ntb_rx_free_q_lock, &qp->rx_free_q)))
-               free(entry, M_NTB_IF);
-       set_bit(free_queue, &nt->qp_bitmap);
-err:
-       return (NULL);
 }
 
 /**
@@ -813,7 +876,7 @@ ntb_process_tx(struct ntb_transport_qp *qp, struct ntb_queue_entry *entry)
                return (0);
        }
        CTR2(KTR_NTB, "TX: copying entry %p to offset %p", entry, offset);
-       ntb_tx_copy_task(qp, entry, offset);
+       ntb_memcpy_tx(qp, entry, offset);
 
        qp->tx_index++;
        qp->tx_index %= qp->tx_max_entry;
@@ -824,20 +887,31 @@ ntb_process_tx(struct ntb_transport_qp *qp, struct ntb_queue_entry *entry)
 }
 
 static void
-ntb_tx_copy_task(struct ntb_transport_qp *qp, struct ntb_queue_entry *entry,
+ntb_memcpy_tx(struct ntb_transport_qp *qp, struct ntb_queue_entry *entry,
     void *offset)
 {
        struct ntb_payload_header *hdr;
 
-       CTR2(KTR_NTB, "TX: copying %d bytes to offset %p", entry->len, offset);
-       if (entry->buf != NULL)
-               m_copydata((struct mbuf *)entry->buf, 0, entry->len, offset);
-
+       /* This piece is from Linux' ntb_async_tx() */
        hdr = (struct ntb_payload_header *)((char *)offset + qp->tx_max_frame -
            sizeof(struct ntb_payload_header));
+       entry->x_hdr = hdr;
        hdr->len = entry->len; /* TODO: replace with bus_space_write */
        hdr->ver = qp->tx_pkts; /* TODO: replace with bus_space_write */
-       wmb();
+
+       /* This piece is ntb_memcpy_tx() */
+       CTR2(KTR_NTB, "TX: copying %d bytes to offset %p", entry->len, offset);
+       if (entry->buf != NULL) {
+               m_copydata((struct mbuf *)entry->buf, 0, entry->len, offset);
+
+               /*
+                * Ensure that the data is fully copied before setting the
+                * flags
+                */
+               wmb();
+       }
+
+       /* The rest is ntb_tx_copy_callback() */
        /* TODO: replace with bus_space_write */
        hdr->flags = entry->flags | IF_NTB_DESC_DONE_FLAG;
 
@@ -879,8 +953,8 @@ ntb_rx_pendq_full(void *arg)
        ntb_transport_rxc_db(arg, 0);
 }
 
-static int
-ntb_transport_rxc_db(void *arg, int dummy __unused)
+static void
+ntb_transport_rxc_db(void *arg, int pending __unused)
 {
        struct ntb_transport_qp *qp = arg;
        uint64_t i;
@@ -890,9 +964,9 @@ ntb_transport_rxc_db(void *arg, int dummy __unused)
         * Limit the number of packets processed in a single interrupt to
         * provide fairness to others
         */
-       mtx_lock(&qp->transport->rx_lock);
        CTR0(KTR_NTB, "RX: transport_rx");
-       for (i = 0; i < MIN(qp->rx_max_entry, INT_MAX); i++) {
+       mtx_lock(&qp->transport->rx_lock);
+       for (i = 0; i < qp->rx_max_entry; i++) {
                rc = ntb_process_rxc(qp);
                if (rc != 0) {
                        CTR0(KTR_NTB, "RX: process_rxc failed");
@@ -901,7 +975,20 @@ ntb_transport_rxc_db(void *arg, int dummy __unused)
        }
        mtx_unlock(&qp->transport->rx_lock);
 
-       return ((int)i);
+       if (i == qp->rx_max_entry)
+               taskqueue_enqueue(taskqueue_swi, &qp->rxc_db_work);
+       else if ((ntb_db_read(qp->ntb) & (1ull << qp->qp_num)) != 0) {
+               /* If db is set, clear it and read it back to commit clear. */
+               ntb_db_clear(qp->ntb, 1ull << qp->qp_num);
+               (void)ntb_db_read(qp->ntb);
+
+               /*
+                * An interrupt may have arrived between finishing
+                * ntb_process_rxc and clearing the doorbell bit: there might
+                * be some more work to do.
+                */
+               taskqueue_enqueue(taskqueue_swi, &qp->rxc_db_work);
+       }
 }
 
 static int
@@ -918,66 +1005,61 @@ ntb_process_rxc(struct ntb_transport_qp *qp)
                sizeof(struct ntb_payload_header));
 
        CTR1(KTR_NTB, "RX: process_rxc rx_index = %u", qp->rx_index);
-       entry = ntb_list_rm(&qp->ntb_rx_pend_q_lock, &qp->rx_pend_q);
-       if (entry == NULL) {
-               qp->rx_err_no_buf++;
-               CTR0(KTR_NTB, "RX: No entries in rx_pend_q");
-               return (ENOMEM);
-       }
-       callout_stop(&qp->rx_full);
-       CTR1(KTR_NTB, "RX: rx entry %p from rx_pend_q", entry);
-
        if ((hdr->flags & IF_NTB_DESC_DONE_FLAG) == 0) {
-               CTR1(KTR_NTB,
-                   "RX: hdr not done. Returning entry %p to rx_pend_q", entry);
-               ntb_list_add(&qp->ntb_rx_pend_q_lock, entry, &qp->rx_pend_q);
+               CTR0(KTR_NTB, "RX: hdr not done");
                qp->rx_ring_empty++;
                return (EAGAIN);
        }
 
-       if (hdr->ver != (uint32_t) qp->rx_pkts) {
-               CTR3(KTR_NTB,"RX: ver != rx_pkts (%x != %lx). "
-                   "Returning entry %p to rx_pend_q", hdr->ver, qp->rx_pkts,
-                   entry);
-               ntb_list_add(&qp->ntb_rx_pend_q_lock, entry, &qp->rx_pend_q);
+       if ((hdr->flags & IF_NTB_LINK_DOWN_FLAG) != 0) {
+               CTR0(KTR_NTB, "RX: link down");
+               ntb_qp_link_down(qp);
+               hdr->flags = 0;
+               return (EAGAIN);
+       }
+
+       if (hdr->ver != (uint32_t)qp->rx_pkts) {
+               CTR2(KTR_NTB,"RX: ver != rx_pkts (%x != %lx). "
+                   "Returning entry %p to rx_pend_q", hdr->ver, qp->rx_pkts);
                qp->rx_err_ver++;
                return (EIO);
        }
 
-       if ((hdr->flags & IF_NTB_LINK_DOWN_FLAG) != 0) {
-               ntb_qp_link_down(qp);
-               CTR1(KTR_NTB,
-                   "RX: link down. adding entry %p back to rx_pend_q", entry);
-               ntb_list_add(&qp->ntb_rx_pend_q_lock, entry, &qp->rx_pend_q);
-               goto out;
+       entry = ntb_list_rm(&qp->ntb_rx_pend_q_lock, &qp->rx_pend_q);
+       if (entry == NULL) {
+               qp->rx_err_no_buf++;
+               CTR0(KTR_NTB, "RX: No entries in rx_pend_q");
+               return (EAGAIN);
        }
+       callout_stop(&qp->rx_full);
+       CTR1(KTR_NTB, "RX: rx entry %p from rx_pend_q", entry);
 
-       if (hdr->len <= entry->len) {
-               entry->len = hdr->len;
-               ntb_rx_copy_task(qp, entry, offset);
-       } else {
-               CTR1(KTR_NTB,
-                   "RX: len too long. Returning entry %p to rx_pend_q", entry);
-               ntb_list_add(&qp->ntb_rx_pend_q_lock, entry, &qp->rx_pend_q);
+       entry->x_hdr = hdr;
+       entry->index = qp->rx_index;
 
+       if (hdr->len > entry->len) {
+               CTR2(KTR_NTB, "RX: len too long. Wanted %ju got %ju",
+                   (uintmax_t)hdr->len, (uintmax_t)entry->len);
                qp->rx_err_oflow++;
-       }
 
-       qp->rx_bytes += hdr->len;
-       qp->rx_pkts++;
-       CTR1(KTR_NTB, "RX: received %ld rx_pkts", qp->rx_pkts);
+               entry->len = -EIO;
+               entry->flags |= IF_NTB_DESC_DONE_FLAG;
 
+               ntb_list_add(&qp->ntb_rx_free_q_lock, entry, &qp->rx_free_q);
+               taskqueue_enqueue(taskqueue_swi, &qp->rx_completion_task);
+       } else {
+               qp->rx_bytes += hdr->len;
+               qp->rx_pkts++;
 
-out:
-       /* Ensure that the data is globally visible before clearing the flag */
-       wmb();
-       hdr->flags = 0;
-       /* TODO: replace with bus_space_write */
-       qp->rx_info->entry = qp->rx_index;
+               CTR1(KTR_NTB, "RX: received %ld rx_pkts", qp->rx_pkts);
+
+               entry->len = hdr->len;
+
+               ntb_rx_copy_task(qp, entry, offset);
+       }
 
        qp->rx_index++;
        qp->rx_index %= qp->rx_max_entry;
-
        return (0);
 }
 
@@ -995,6 +1077,12 @@ ntb_rx_copy_task(struct ntb_transport_qp *qp, struct ntb_queue_entry *entry,
 
        entry->buf = (void *)m;
 
+       /* Ensure that the data is globally visible before clearing the flag */
+       wmb();
+       entry->x_hdr->flags = 0;
+       /* TODO: replace with bus_space_write */
+       qp->rx_info->entry = qp->rx_index;
+
        CTR2(KTR_NTB,
            "RX: copied entry %p to mbuf %p. Adding entry to rx_free_q", entry,
            m);
@@ -1004,7 +1092,7 @@ ntb_rx_copy_task(struct ntb_transport_qp *qp, struct ntb_queue_entry *entry,
 }
 
 static void
-ntb_rx_completion_task(void *arg, int pending)
+ntb_complete_rxc(void *arg, int pending)
 {
        struct ntb_transport_qp *qp = arg;
        struct mbuf *m;
@@ -1033,25 +1121,52 @@ ntb_rx_completion_task(void *arg, int pending)
        }
 }
 
+static void
+ntb_transport_doorbell_callback(void *data, int 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->ntb, vector);
+       while (vec_mask != 0) {
+               qp_num = ffsl(vec_mask);
+               /* i386 doesn't have ffsll(), fake it */
+               if (qp_num == 0) {
+                       qp_num = ffsl(vec_mask >> 32);
+                       KASSERT(qp_num != 0, ("ffs"));
+                       qp_num += 32;
+               }
+               qp_num--;
+
+               if (test_bit(qp_num, &db_bits)) {
+                       qp = &nt->qp_vec[qp_num];
+                       taskqueue_enqueue(taskqueue_swi, &qp->rxc_db_work);
+               }
+
+               vec_mask &= ~(1ull << qp_num);
+       }
+}
+
 /* Link Event handler */
 static void
-ntb_transport_event_callback(void *data, enum ntb_hw_event event)
+ntb_transport_event_callback(void *data)
 {
        struct ntb_transport_ctx *nt = data;
 
-       switch (event) {
-       case NTB_EVENT_HW_LINK_UP:
+       if (ntb_link_is_up(nt->ntb, NULL, NULL)) {
                if (bootverbose)
                        device_printf(ntb_get_device(nt->ntb), "HW link up\n");
                callout_reset(&nt->link_work, 0, ntb_transport_link_work, nt);
-               break;
-       case NTB_EVENT_HW_LINK_DOWN:
+       } else {
                if (bootverbose)
                        device_printf(ntb_get_device(nt->ntb), "HW link down\n");
                ntb_transport_link_cleanup(nt);
-               break;
-       default:
-               panic("ntb: Unknown NTB event");
        }
 }
 
@@ -1062,72 +1177,48 @@ ntb_transport_link_work(void *arg)
        struct ntb_transport_ctx *nt = arg;
        struct ntb_softc *ntb = nt->ntb;
        struct ntb_transport_qp *qp;
-       uint64_t val64;
-       uint32_t val, i, num_mw;
+       uint64_t val64, size;
+       uint32_t val;
+       unsigned i;
        int rc;
 
-       num_mw = ntb_mw_count(ntb);
-
        /* send the local info, in the opposite order of the way we read it */
-       for (i = 0; i < num_mw; i++) {
-               rc = ntb_peer_spad_write(ntb, IF_NTB_MW0_SZ_HIGH + (i * 2),
-                   (uint64_t)ntb_get_mw_size(ntb, i) >> 32);
-               if (rc != 0)
-                       goto out;
+       for (i = 0; i < nt->mw_count; i++) {
+               size = nt->mw_vec[i].phys_size;
 
-               rc = ntb_peer_spad_write(ntb, IF_NTB_MW0_SZ_LOW + (i * 2),
-                   (uint32_t)ntb_get_mw_size(ntb, i));
-               if (rc != 0)
-                       goto out;
+               if (max_mw_size != 0 && size > max_mw_size)
+                       size = max_mw_size;
+
+               ntb_peer_spad_write(ntb, IF_NTB_MW0_SZ_HIGH + (i * 2),
+                   size >> 32);
+               ntb_peer_spad_write(ntb, IF_NTB_MW0_SZ_LOW + (i * 2), size);
        }
 
-       rc = ntb_peer_spad_write(ntb, IF_NTB_NUM_MWS, num_mw);
-       if (rc != 0)
-               goto out;
+       ntb_peer_spad_write(ntb, IF_NTB_NUM_MWS, nt->mw_count);
 
-       rc = ntb_peer_spad_write(ntb, IF_NTB_NUM_QPS, nt->qp_count);
-       if (rc != 0)
-               goto out;
+       ntb_peer_spad_write(ntb, IF_NTB_NUM_QPS, nt->qp_count);
 
-       rc = ntb_peer_spad_write(ntb, IF_NTB_VERSION, NTB_TRANSPORT_VERSION);
-       if (rc != 0)
-               goto out;
+       ntb_peer_spad_write(ntb, IF_NTB_VERSION, NTB_TRANSPORT_VERSION);
 
        /* Query the remote side for its info */
-       rc = ntb_spad_read(ntb, IF_NTB_VERSION, &val);
-       if (rc != 0)
-               goto out;
-
+       val = 0;
+       ntb_spad_read(ntb, IF_NTB_VERSION, &val);
        if (val != NTB_TRANSPORT_VERSION)
                goto out;
 
-       rc = ntb_spad_read(ntb, IF_NTB_NUM_QPS, &val);
-       if (rc != 0)
-               goto out;
-
+       ntb_spad_read(ntb, IF_NTB_NUM_QPS, &val);
        if (val != nt->qp_count)
                goto out;
 
-       rc = ntb_spad_read(ntb, IF_NTB_NUM_MWS, &val);
-       if (rc != 0)
-               goto out;
-
-       if (val != num_mw)
+       ntb_spad_read(ntb, IF_NTB_NUM_MWS, &val);
+       if (val != nt->mw_count)
                goto out;
 
-       for (i = 0; i < num_mw; i++) {
-               rc = ntb_spad_read(ntb, IF_NTB_MW0_SZ_HIGH + (i * 2),
-                   &val);
-               if (rc != 0)
-                       goto free_mws;
-
+       for (i = 0; i < nt->mw_count; i++) {
+               ntb_spad_read(ntb, IF_NTB_MW0_SZ_HIGH + (i * 2), &val);
                val64 = (uint64_t)val << 32;
 
-               rc = ntb_spad_read(ntb, IF_NTB_MW0_SZ_LOW + (i * 2),
-                   &val);
-               if (rc != 0)
-                       goto free_mws;
-
+               ntb_spad_read(ntb, IF_NTB_MW0_SZ_LOW + (i * 2), &val);
                val64 |= val;
 
                rc = ntb_set_mw(nt, i, val64);
@@ -1151,33 +1242,40 @@ ntb_transport_link_work(void *arg)
        return;
 
 free_mws:
-       for (i = 0; i < NTB_MAX_NUM_MW; i++)
+       for (i = 0; i < nt->mw_count; i++)
                ntb_free_mw(nt, i);
 out:
-       if (ntb_link_is_up(ntb))
+       if (ntb_link_is_up(ntb, NULL, NULL))
                callout_reset(&nt->link_work,
                    NTB_LINK_DOWN_TIMEOUT * hz / 1000, ntb_transport_link_work, nt);
 }
 
 static int
-ntb_set_mw(struct ntb_transport_ctx *nt, int num_mw, unsigned int size)
+ntb_set_mw(struct ntb_transport_ctx *nt, int num_mw, unsigned size)
 {
        struct ntb_transport_mw *mw = &nt->mw_vec[num_mw];
+       unsigned xlat_size, buff_size;
+       int rc;
+
+       xlat_size = roundup(size, mw->xlat_align_size);
+       buff_size = roundup(size, mw->xlat_align);
 
        /* No need to re-setup */
-       if (mw->xlat_size == size)
+       if (mw->xlat_size == xlat_size)
                return (0);
 
-       if (mw->xlat_size != 0)
+       if (mw->buff_size != 0)
                ntb_free_mw(nt, num_mw);
 
-       /* Alloc memory for receiving data.  Must be 4k aligned */
-       mw->xlat_size = size;
+       /* Alloc memory for receiving data.  Must be aligned */
+       mw->xlat_size = xlat_size;
+       mw->buff_size = buff_size;
 
-       mw->virt_addr = contigmalloc(mw->xlat_size, M_NTB_IF, M_ZERO, 0,
-           BUS_SPACE_MAXADDR, mw->xlat_size, 0);
+       mw->virt_addr = contigmalloc(mw->buff_size, M_NTB_IF, M_ZERO, 0,
+           BUS_SPACE_MAXADDR, mw->xlat_align, 0);
        if (mw->virt_addr == NULL) {
                mw->xlat_size = 0;
+               mw->buff_size = 0;
                printf("ntb: Unable to allocate MW buffer of size %zu\n",
                    mw->xlat_size);
                return (ENOMEM);
@@ -1190,7 +1288,7 @@ ntb_set_mw(struct ntb_transport_ctx *nt, int num_mw, unsigned int size)
         * requested.  XXX: This may not be needed -- brought in for parity
         * with the Linux driver.
         */
-       if (mw->dma_addr % size != 0) {
+       if (mw->dma_addr % mw->xlat_align != 0) {
                device_printf(ntb_get_device(nt->ntb),
                    "DMA memory 0x%jx not aligned to BAR size 0x%x\n",
                    (uintmax_t)mw->dma_addr, size);
@@ -1199,7 +1297,13 @@ ntb_set_mw(struct ntb_transport_ctx *nt, int num_mw, unsigned int size)
        }
 
        /* Notify HW the memory location of the receive buffer */
-       ntb_set_mw_addr(nt->ntb, num_mw, mw->dma_addr);
+       rc = ntb_mw_set_trans(nt->ntb, num_mw, mw->dma_addr, mw->xlat_size);
+       if (rc) {
+               device_printf(ntb_get_device(nt->ntb),
+                   "Unable to set mw%d translation", num_mw);
+               ntb_free_mw(nt, num_mw);
+               return (rc);
+       }
 
        return (0);
 }
@@ -1212,33 +1316,41 @@ ntb_free_mw(struct ntb_transport_ctx *nt, int num_mw)
        if (mw->virt_addr == NULL)
                return;
 
+       ntb_mw_clear_trans(nt->ntb, num_mw);
        contigfree(mw->virt_addr, mw->xlat_size, M_NTB_IF);
+       mw->xlat_size = 0;
+       mw->buff_size = 0;
        mw->virt_addr = NULL;
 }
 
-static void
+static int
 ntb_transport_setup_qp_mw(struct ntb_transport_ctx *nt, unsigned int qp_num)
 {
        struct ntb_transport_qp *qp = &nt->qp_vec[qp_num];
+       struct ntb_transport_mw *mw;
        void *offset;
-       unsigned int rx_size, num_qps_mw;
-       uint8_t mw_num, mw_count;
-       unsigned int i;
+       uint64_t i;
+       size_t rx_size;
+       unsigned num_qps_mw, mw_num, mw_count;
 
-       mw_count = ntb_mw_count(nt->ntb);
+       mw_count = nt->mw_count;
        mw_num = QP_TO_MW(nt, qp_num);
+       mw = &nt->mw_vec[mw_num];
+
+       if (mw->virt_addr == NULL)
+               return (ENOMEM);
 
        if (nt->qp_count % mw_count && mw_num + 1 < nt->qp_count / mw_count)
                num_qps_mw = nt->qp_count / mw_count + 1;
        else
                num_qps_mw = nt->qp_count / mw_count;
 
-       rx_size = nt->mw_vec[mw_num].xlat_size / num_qps_mw;
-       qp->remote_rx_info = (void *)((uint8_t *)nt->mw_vec[mw_num].virt_addr +
-           (qp_num / mw_count * rx_size));
+       rx_size = mw->xlat_size / num_qps_mw;
+       qp->rx_buff = (char *)mw->virt_addr + rx_size * qp_num / mw_count;
        rx_size -= sizeof(struct ntb_rx_info);
 
-       qp->rx_buff = qp->remote_rx_info + 1;
+       qp->remote_rx_info = (void*)((char *)qp->rx_buff + rx_size);
+
        /* Due to house-keeping, there must be at least 2 buffs */
        qp->rx_max_frame = min(transport_mtu + sizeof(struct ntb_payload_header),
            rx_size / 2);
@@ -1247,7 +1359,7 @@ ntb_transport_setup_qp_mw(struct ntb_transport_ctx *nt, unsigned int qp_num)
 
        qp->remote_rx_info->entry = qp->rx_max_entry - 1;
 
-       /* setup the hdr offsets with 0's */
+       /* Set up the hdr offsets with 0s */
        for (i = 0; i < qp->rx_max_entry; i++) {
                offset = (void *)((uint8_t *)qp->rx_buff +
                    qp->rx_max_frame * (i + 1) -
@@ -1258,6 +1370,8 @@ ntb_transport_setup_qp_mw(struct ntb_transport_ctx *nt, unsigned int qp_num)
        qp->rx_pkts = 0;
        qp->tx_pkts = 0;
        qp->tx_index = 0;
+
+       return (0);
 }
 
 static void
@@ -1266,46 +1380,51 @@ ntb_qp_link_work(void *arg)
        struct ntb_transport_qp *qp = arg;
        struct ntb_softc *ntb = qp->ntb;
        struct ntb_transport_ctx *nt = qp->transport;
-       int rc, val;
-
+       int val;
 
-       rc = ntb_peer_spad_read(ntb, IF_NTB_QP_LINKS, &val);
-       if (rc != 0)
-               return;
+       ntb_spad_read(ntb, IF_NTB_QP_LINKS, &val);
 
-       rc = ntb_peer_spad_write(ntb, IF_NTB_QP_LINKS, val | 1 << qp->qp_num);
+       ntb_peer_spad_write(ntb, IF_NTB_QP_LINKS, val | (1ull << qp->qp_num));
 
        /* query remote spad for qp ready bits */
-       rc = ntb_spad_read(ntb, IF_NTB_QP_LINKS, &val);
+       ntb_peer_spad_read(ntb, IF_NTB_QP_LINKS, &val);
 
        /* See if the remote side is up */
-       if ((1 << qp->qp_num & val) != 0) {
+       if ((val & (1ull << qp->qp_num)) != 0) {
+               if (bootverbose)
+                       device_printf(ntb_get_device(ntb), "qp link up\n");
                qp->link_is_up = true;
+
                if (qp->event_handler != NULL)
                        qp->event_handler(qp->cb_data, NTB_LINK_UP);
-               if (bootverbose)
-                       device_printf(ntb_get_device(ntb), "qp link up\n");
-       } else if (nt->link_is_up) {
+
+               taskqueue_enqueue(taskqueue_swi, &qp->rxc_db_work);
+       } else if (nt->link_is_up)
                callout_reset(&qp->link_work,
                    NTB_LINK_DOWN_TIMEOUT * hz / 1000, ntb_qp_link_work, qp);
-       }
 }
 
 /* Link down event*/
 static void
 ntb_transport_link_cleanup(struct ntb_transport_ctx *nt)
 {
-       uint8_t i;
+       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);
 
        /* Pass along the info to any clients */
        for (i = 0; i < nt->qp_count; i++)
-               if (!test_bit(i, &nt->qp_bitmap))
-                       ntb_qp_link_down(&nt->qp_vec[i]);
+               if (test_bit(i, &qp_bitmap_alloc)) {
+                       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);
-       else
-               nt->link_is_up = false;
 
        /*
         * The scratchpad registers keep the values if the remote side
@@ -1334,11 +1453,11 @@ ntb_qp_link_cleanup(struct ntb_transport_qp *qp)
                return;
        }
 
+       qp->link_is_up = false;
+
        if (qp->event_handler != NULL)
                qp->event_handler(qp->cb_data, NTB_LINK_DOWN);
 
-       qp->link_is_up = false;
-
        if (nt->link_is_up)
                callout_reset(&qp->link_work,
                    NTB_LINK_DOWN_TIMEOUT * hz / 1000, ntb_qp_link_work, qp);
@@ -1356,25 +1475,22 @@ ntb_qp_link_cleanup(struct ntb_transport_qp *qp)
 static void
 ntb_transport_link_down(struct ntb_transport_qp *qp)
 {
-       int rc, val;
+       uint32_t val;
 
        if (qp == NULL)
                return;
 
        qp->client_ready = false;
 
-       rc = ntb_peer_spad_read(qp->ntb, IF_NTB_QP_LINKS, &val);
-       if (rc != 0)
-               return;
+       ntb_spad_read(qp->ntb, IF_NTB_QP_LINKS, &val);
 
-       rc = ntb_peer_spad_write(qp->ntb, IF_NTB_QP_LINKS,
+       ntb_peer_spad_write(qp->ntb, IF_NTB_QP_LINKS,
           val & ~(1 << qp->qp_num));
 
        if (qp->link_is_up)
                ntb_send_link_down(qp);
        else
                callout_drain(&qp->link_work);
-
 }
 
 static void
index cee2424db8d436a27efcf59cfe2ebdf4a381cfa9..9a3e189b974d087266d5e8be795f4d87aec5e99a 100644 (file)
@@ -103,8 +103,6 @@ struct ntb_pci_bar_info {
        /* Configuration register offsets */
        uint32_t                psz_off;
        uint32_t                ssz_off;
-       uint32_t                sbarbase_off;
-       uint32_t                sbarlmt_off;
        uint32_t                pbarxlat_off;
 };
 
@@ -114,13 +112,9 @@ struct ntb_int_info {
        void            *tag;
 };
 
-struct ntb_db_cb {
-       ntb_db_callback         callback;
-       unsigned int            db_num;
-       void                    *data;
+struct ntb_vec {
        struct ntb_softc        *ntb;
-       struct callout          irq_work;
-       bool                    reserved;
+       uint32_t                num;
 };
 
 struct ntb_reg {
@@ -137,9 +131,18 @@ struct ntb_alt_reg {
 };
 
 struct ntb_xlat_reg {
-       uint64_t        bar0_base;
-       uint64_t        bar2_xlat;
-       uint64_t        bar2_limit;
+       uint32_t        bar0_base;
+       uint32_t        bar2_base;
+       uint32_t        bar4_base;
+       uint32_t        bar5_base;
+
+       uint32_t        bar2_xlat;
+       uint32_t        bar4_xlat;
+       uint32_t        bar5_xlat;
+
+       uint32_t        bar2_limit;
+       uint32_t        bar4_limit;
+       uint32_t        bar5_limit;
 };
 
 struct ntb_b2b_addr {
@@ -162,10 +165,13 @@ struct ntb_softc {
        struct callout          heartbeat_timer;
        struct callout          lr_timer;
 
-       void                    *ntb_transport;
-       ntb_event_callback      event_cb;
-       struct ntb_db_cb        *db_cb;
-       uint8_t                 max_cbs;
+       void                    *ntb_ctx;
+       const struct ntb_ctx_ops *ctx_ops;
+       struct ntb_vec          *msix_vec;
+#define CTX_LOCK(sc)           mtx_lock_spin(&(sc)->ctx_lock)
+#define CTX_UNLOCK(sc)         mtx_unlock_spin(&(sc)->ctx_lock)
+#define CTX_ASSERT(sc,f)       mtx_assert(&(sc)->ctx_lock, (f))
+       struct mtx              ctx_lock;
 
        struct {
                uint32_t ldb;
@@ -178,7 +184,6 @@ struct ntb_softc {
        uint32_t ppd;
        uint8_t conn_type;
        uint8_t dev_type;
-       uint8_t link_status;
        uint8_t link_width;
        uint8_t link_speed;
 
@@ -194,18 +199,18 @@ struct ntb_softc {
        uint8_t                         db_vec_count;
        uint8_t                         db_vec_shift;
 
-       /* Protects local DB mask and (h). */
-#define HW_LOCK(sc)    mtx_lock_spin(&(sc)->db_mask_lock)
-#define HW_UNLOCK(sc)  mtx_unlock_spin(&(sc)->db_mask_lock)
-#define HW_ASSERT(sc,f)        mtx_assert(&(sc)->db_mask_lock, (f))
+       /* Protects local db_mask. */
+#define DB_MASK_LOCK(sc)       mtx_lock_spin(&(sc)->db_mask_lock)
+#define DB_MASK_UNLOCK(sc)     mtx_unlock_spin(&(sc)->db_mask_lock)
+#define DB_MASK_ASSERT(sc,f)   mtx_assert(&(sc)->db_mask_lock, (f))
        struct mtx                      db_mask_lock;
 
-       uint32_t                        ntb_ctl;        /* (h) - SOC only */
-       uint32_t                        lnk_sta;        /* (h) - SOC only */
+       uint32_t                        ntb_ctl;
+       uint32_t                        lnk_sta;
 
        uint64_t                        db_valid_mask;
        uint64_t                        db_link_mask;
-       uint64_t                        db_mask;        /* (h) */
+       uint64_t                        db_mask;
 
        int                             last_ts;        /* ticks @ last irq */
 
@@ -254,6 +259,9 @@ static int ntb_probe(device_t device);
 static int ntb_attach(device_t device);
 static int ntb_detach(device_t device);
 static inline enum ntb_bar ntb_mw_to_bar(struct ntb_softc *, unsigned mw);
+static inline bool bar_is_64bit(struct ntb_softc *, enum ntb_bar);
+static inline void bar_get_xlat_params(struct ntb_softc *, enum ntb_bar,
+    uint32_t *base, uint32_t *xlat, uint32_t *lmt);
 static int ntb_map_pci_bars(struct ntb_softc *ntb);
 static void print_map_success(struct ntb_softc *, struct ntb_pci_bar_info *);
 static int map_mmr_bar(struct ntb_softc *ntb, struct ntb_pci_bar_info *bar);
@@ -266,17 +274,13 @@ static int ntb_setup_legacy_interrupt(struct ntb_softc *ntb);
 static int ntb_setup_msix(struct ntb_softc *ntb, uint32_t num_vectors);
 static void ntb_teardown_interrupts(struct ntb_softc *ntb);
 static inline uint64_t ntb_vec_mask(struct ntb_softc *, uint64_t db_vector);
-static void handle_irq(void *arg);
-static void ntb_handle_legacy_interrupt(void *arg);
-static void ntb_irq_work(void *arg);
-static inline uint64_t ntb_db_read(struct ntb_softc *, uint64_t regoff);
-static inline void ntb_db_write(struct ntb_softc *, uint64_t regoff, uint64_t val);
-static inline void mask_ldb_interrupt(struct ntb_softc *ntb, unsigned int idx);
-static inline void unmask_ldb_interrupt(struct ntb_softc *ntb, unsigned int idx);
-static inline void ntb_db_set_mask(struct ntb_softc *, uint64_t bits);
-static inline void ntb_db_clear_mask(struct ntb_softc *, uint64_t bits);
-static int ntb_create_callbacks(struct ntb_softc *ntb, uint32_t num_vectors);
-static void ntb_free_callbacks(struct ntb_softc *ntb);
+static void ntb_interrupt(struct ntb_softc *, uint32_t vec);
+static void ndev_vec_isr(void *arg);
+static void ndev_irq_isr(void *arg);
+static inline uint64_t db_ioread(struct ntb_softc *, uint64_t regoff);
+static inline void db_iowrite(struct ntb_softc *, uint64_t regoff, uint64_t val);
+static int ntb_create_msix_vec(struct ntb_softc *ntb, uint32_t num_vectors);
+static void ntb_free_msix_vec(struct ntb_softc *ntb);
 static struct ntb_hw_info *ntb_get_device_info(uint32_t device_id);
 static void ntb_detect_max_mw(struct ntb_softc *ntb);
 static int ntb_detect_xeon(struct ntb_softc *ntb);
@@ -293,12 +297,14 @@ static void xeon_set_pbar_xlat(struct ntb_softc *, uint64_t base_addr,
     enum ntb_bar idx);
 static int xeon_setup_b2b_mw(struct ntb_softc *,
     const struct ntb_b2b_addr *addr, const struct ntb_b2b_addr *peer_addr);
+static inline bool link_is_up(struct ntb_softc *ntb);
+static inline bool soc_link_is_err(struct ntb_softc *ntb);
+static inline enum ntb_speed ntb_link_sta_speed(struct ntb_softc *);
+static inline enum ntb_width ntb_link_sta_width(struct ntb_softc *);
 static void soc_link_hb(void *arg);
-static void ntb_handle_link_event(struct ntb_softc *ntb, int link_state);
-static void ntb_link_disable(struct ntb_softc *ntb);
-static void ntb_link_enable(struct ntb_softc *ntb);
+static void ntb_db_event(struct ntb_softc *ntb, uint32_t vec);
 static void recover_soc_link(void *arg);
-static int ntb_poll_link(struct ntb_softc *ntb);
+static bool ntb_poll_link(struct ntb_softc *ntb);
 static void save_bar_parameters(struct ntb_pci_bar_info *bar);
 
 static struct ntb_hw_info pci_ids[] = {
@@ -338,9 +344,15 @@ static const struct ntb_xlat_reg soc_sec_xlat = {
 #if 0
        /* "FIXME" says the Linux driver. */
        .bar0_base = SOC_SBAR0BASE_OFFSET,
+       .bar2_base = SOC_SBAR2BASE_OFFSET,
+       .bar4_base = SOC_SBAR4BASE_OFFSET,
+
        .bar2_limit = SOC_SBAR2LMT_OFFSET,
+       .bar4_limit = SOC_SBAR4LMT_OFFSET,
 #endif
+
        .bar2_xlat = SOC_SBAR2XLAT_OFFSET,
+       .bar4_xlat = SOC_SBAR4XLAT_OFFSET,
 };
 
 static const struct ntb_reg xeon_reg = {
@@ -357,8 +369,17 @@ static const struct ntb_alt_reg xeon_b2b_reg = {
 
 static const struct ntb_xlat_reg xeon_sec_xlat = {
        .bar0_base = XEON_SBAR0BASE_OFFSET,
+       .bar2_base = XEON_SBAR2BASE_OFFSET,
+       .bar4_base = XEON_SBAR4BASE_OFFSET,
+       .bar5_base = XEON_SBAR5BASE_OFFSET,
+
        .bar2_limit = XEON_SBAR2LMT_OFFSET,
+       .bar4_limit = XEON_SBAR4LMT_OFFSET,
+       .bar5_limit = XEON_SBAR5LMT_OFFSET,
+
        .bar2_xlat = XEON_SBAR2XLAT_OFFSET,
+       .bar4_xlat = XEON_SBAR4XLAT_OFFSET,
+       .bar5_xlat = XEON_SBAR5XLAT_OFFSET,
 };
 
 static const struct ntb_b2b_addr xeon_b2b_usd_addr = {
@@ -437,6 +458,7 @@ ntb_attach(device_t device)
        callout_init(&ntb->heartbeat_timer, 1);
        callout_init(&ntb->lr_timer, 1);
        mtx_init(&ntb->db_mask_lock, "ntb hw bits", NULL, MTX_SPIN);
+       mtx_init(&ntb->ctx_lock, "ntb ctx", NULL, MTX_SPIN);
 
        if (ntb->type == NTB_SOC)
                error = ntb_detect_soc(ntb);
@@ -483,6 +505,7 @@ ntb_detach(device_t device)
        ntb_teardown_interrupts(ntb);
 
        mtx_destroy(&ntb->db_mask_lock);
+       mtx_destroy(&ntb->ctx_lock);
 
        /*
         * Redetect total MWs so we unmap properly -- in case we lowered the
@@ -504,10 +527,56 @@ ntb_mw_to_bar(struct ntb_softc *ntb, unsigned mw)
        KASSERT(mw < ntb->mw_count ||
            (mw != B2B_MW_DISABLED && mw == ntb->b2b_mw_idx),
            ("%s: mw:%u > count:%u", __func__, mw, (unsigned)ntb->mw_count));
+       KASSERT(ntb->reg->mw_bar[mw] != 0, ("invalid mw"));
 
        return (ntb->reg->mw_bar[mw]);
 }
 
+static inline bool
+bar_is_64bit(struct ntb_softc *ntb, enum ntb_bar bar)
+{
+       /* XXX This assertion could be stronger. */
+       KASSERT(bar < NTB_MAX_BARS, ("bogus bar"));
+       return (bar < NTB_B2B_BAR_2 || !HAS_FEATURE(NTB_SPLIT_BAR));
+}
+
+static inline void
+bar_get_xlat_params(struct ntb_softc *ntb, enum ntb_bar bar, uint32_t *base,
+    uint32_t *xlat, uint32_t *lmt)
+{
+       uint32_t basev, lmtv, xlatv;
+
+       switch (bar) {
+       case NTB_B2B_BAR_1:
+               basev = ntb->xlat_reg->bar2_base;
+               lmtv = ntb->xlat_reg->bar2_limit;
+               xlatv = ntb->xlat_reg->bar2_xlat;
+               break;
+       case NTB_B2B_BAR_2:
+               basev = ntb->xlat_reg->bar4_base;
+               lmtv = ntb->xlat_reg->bar4_limit;
+               xlatv = ntb->xlat_reg->bar4_xlat;
+               break;
+       case NTB_B2B_BAR_3:
+               basev = ntb->xlat_reg->bar5_base;
+               lmtv = ntb->xlat_reg->bar5_limit;
+               xlatv = ntb->xlat_reg->bar5_xlat;
+               break;
+       default:
+               KASSERT(bar >= NTB_B2B_BAR_1 && bar < NTB_MAX_BARS,
+                   ("bad bar"));
+               basev = lmtv = xlatv = 0;
+               break;
+       }
+
+       if (base != NULL)
+               *base = basev;
+       if (xlat != NULL)
+               *xlat = xlatv;
+       if (lmt != NULL)
+               *lmt = lmtv;
+}
+
 static int
 ntb_map_pci_bars(struct ntb_softc *ntb)
 {
@@ -524,8 +593,6 @@ ntb_map_pci_bars(struct ntb_softc *ntb)
                goto out;
        ntb->bar_info[NTB_B2B_BAR_1].psz_off = XEON_PBAR23SZ_OFFSET;
        ntb->bar_info[NTB_B2B_BAR_1].ssz_off = XEON_SBAR23SZ_OFFSET;
-       ntb->bar_info[NTB_B2B_BAR_1].sbarbase_off = XEON_SBAR2BASE_OFFSET;
-       ntb->bar_info[NTB_B2B_BAR_1].sbarlmt_off = XEON_SBAR2LMT_OFFSET;
        ntb->bar_info[NTB_B2B_BAR_1].pbarxlat_off = XEON_PBAR2XLAT_OFFSET;
 
        ntb->bar_info[NTB_B2B_BAR_2].pci_resource_id = PCIR_BAR(4);
@@ -536,8 +603,6 @@ ntb_map_pci_bars(struct ntb_softc *ntb)
                rc = map_memory_window_bar(ntb, &ntb->bar_info[NTB_B2B_BAR_2]);
        ntb->bar_info[NTB_B2B_BAR_2].psz_off = XEON_PBAR4SZ_OFFSET;
        ntb->bar_info[NTB_B2B_BAR_2].ssz_off = XEON_SBAR4SZ_OFFSET;
-       ntb->bar_info[NTB_B2B_BAR_2].sbarbase_off = XEON_SBAR4BASE_OFFSET;
-       ntb->bar_info[NTB_B2B_BAR_2].sbarlmt_off = XEON_SBAR4LMT_OFFSET;
        ntb->bar_info[NTB_B2B_BAR_2].pbarxlat_off = XEON_PBAR4XLAT_OFFSET;
 
        if (!HAS_FEATURE(NTB_SPLIT_BAR))
@@ -550,8 +615,6 @@ ntb_map_pci_bars(struct ntb_softc *ntb)
                rc = map_memory_window_bar(ntb, &ntb->bar_info[NTB_B2B_BAR_3]);
        ntb->bar_info[NTB_B2B_BAR_3].psz_off = XEON_PBAR5SZ_OFFSET;
        ntb->bar_info[NTB_B2B_BAR_3].ssz_off = XEON_SBAR5SZ_OFFSET;
-       ntb->bar_info[NTB_B2B_BAR_3].sbarbase_off = XEON_SBAR5BASE_OFFSET;
-       ntb->bar_info[NTB_B2B_BAR_3].sbarlmt_off = XEON_SBAR5LMT_OFFSET;
        ntb->bar_info[NTB_B2B_BAR_3].pbarxlat_off = XEON_PBAR5XLAT_OFFSET;
 
 out:
@@ -677,8 +740,8 @@ ntb_setup_msix(struct ntb_softc *ntb, uint32_t num_vectors)
                ntb->int_info[i].tag = NULL;
                ntb->allocated_interrupts++;
                rc = bus_setup_intr(ntb->device, ntb->int_info[i].res,
-                   INTR_MPSAFE | INTR_TYPE_MISC, NULL, handle_irq,
-                   &ntb->db_cb[i], &ntb->int_info[i].tag);
+                   INTR_MPSAFE | INTR_TYPE_MISC, NULL, ndev_vec_isr,
+                   &ntb->msix_vec[i], &ntb->int_info[i].tag);
                if (rc != 0) {
                        device_printf(ntb->device, "bus_setup_intr failed\n");
                        return (ENXIO);
@@ -736,20 +799,15 @@ static int
 ntb_init_isr(struct ntb_softc *ntb)
 {
        uint32_t desired_vectors, num_vectors;
-       uint64_t mask;
        int rc;
 
        ntb->allocated_interrupts = 0;
        ntb->last_ts = ticks;
 
        /*
-        * On SOC, disable all interrupts.  On XEON, disable all but Link
-        * Interrupt.  The rest will be unmasked as callbacks are registered.
+        * Mask all doorbell interrupts.
         */
-       mask = ntb->db_valid_mask;
-       if (ntb->type == NTB_XEON)
-               mask &= ~ntb->db_link_mask;
-       ntb_db_set_mask(ntb, mask);
+       ntb_db_set_mask(ntb, ntb->db_valid_mask);
 
        num_vectors = desired_vectors = MIN(pci_msix_count(ntb->device),
            ntb->db_count);
@@ -774,31 +832,17 @@ ntb_init_isr(struct ntb_softc *ntb)
                num_vectors = 1;
 
        if (ntb->type == NTB_XEON && num_vectors < ntb->db_vec_count) {
-               /*
-                * 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.
-                */
                ntb->db_vec_count = 1;
                ntb->db_vec_shift = ntb->db_count;
-               ntb_create_callbacks(ntb, ntb->db_count);
                rc = ntb_setup_legacy_interrupt(ntb);
        } else {
-               ntb_create_callbacks(ntb, num_vectors);
+               ntb_create_msix_vec(ntb, num_vectors);
                rc = ntb_setup_msix(ntb, num_vectors);
-               if (rc == 0 && ntb->type == NTB_XEON) {
-                       /*
-                        * Prevent consumers from registering callbacks on the link event irq
-                        * slot, from which they will never be called back.
-                        */
-                       ntb->db_cb[num_vectors - 1].reserved = true;
-                       ntb->max_cbs--;
-               }
        }
        if (rc != 0) {
                device_printf(ntb->device,
                    "Error allocating interrupts: %d\n", rc);
-               ntb_free_callbacks(ntb);
+               ntb_free_msix_vec(ntb);
        }
 
        return (rc);
@@ -821,7 +865,7 @@ ntb_setup_legacy_interrupt(struct ntb_softc *ntb)
        ntb->allocated_interrupts = 1;
 
        rc = bus_setup_intr(ntb->device, ntb->int_info[0].res,
-           INTR_MPSAFE | INTR_TYPE_MISC, NULL, ntb_handle_legacy_interrupt,
+           INTR_MPSAFE | INTR_TYPE_MISC, NULL, ndev_irq_isr,
            ntb, &ntb->int_info[0].tag);
        if (rc != 0) {
                device_printf(ntb->device, "bus_setup_intr failed\n");
@@ -848,7 +892,7 @@ ntb_teardown_interrupts(struct ntb_softc *ntb)
                            rman_get_rid(current_int->res), current_int->res);
        }
 
-       ntb_free_callbacks(ntb);
+       ntb_free_msix_vec(ntb);
        pci_release_msi(ntb->device);
 }
 
@@ -857,7 +901,7 @@ ntb_teardown_interrupts(struct ntb_softc *ntb)
  * out to make code clearer.
  */
 static inline uint64_t
-ntb_db_read(struct ntb_softc *ntb, uint64_t regoff)
+db_ioread(struct ntb_softc *ntb, uint64_t regoff)
 {
 
        if (ntb->type == NTB_SOC)
@@ -869,7 +913,7 @@ ntb_db_read(struct ntb_softc *ntb, uint64_t regoff)
 }
 
 static inline void
-ntb_db_write(struct ntb_softc *ntb, uint64_t regoff, uint64_t val)
+db_iowrite(struct ntb_softc *ntb, uint64_t regoff, uint64_t val)
 {
 
        KASSERT((val & ~ntb->db_valid_mask) == 0,
@@ -878,7 +922,7 @@ ntb_db_write(struct ntb_softc *ntb, uint64_t regoff, uint64_t val)
             (uintmax_t)ntb->db_valid_mask));
 
        if (regoff == ntb->reg_ofs.ldb_mask)
-               HW_ASSERT(ntb, MA_OWNED);
+               DB_MASK_ASSERT(ntb, MA_OWNED);
 
        if (ntb->type == NTB_SOC) {
                ntb_reg_write(8, regoff, val);
@@ -889,17 +933,17 @@ ntb_db_write(struct ntb_softc *ntb, uint64_t regoff, uint64_t val)
        ntb_reg_write(2, regoff, (uint16_t)val);
 }
 
-static inline void
+void
 ntb_db_set_mask(struct ntb_softc *ntb, uint64_t bits)
 {
 
-       HW_LOCK(ntb);
+       DB_MASK_LOCK(ntb);
        ntb->db_mask |= bits;
-       ntb_db_write(ntb, ntb->reg_ofs.ldb_mask, ntb->db_mask);
-       HW_UNLOCK(ntb);
+       db_iowrite(ntb, ntb->reg_ofs.ldb_mask, ntb->db_mask);
+       DB_MASK_UNLOCK(ntb);
 }
 
-static inline void
+void
 ntb_db_clear_mask(struct ntb_softc *ntb, uint64_t bits)
 {
 
@@ -908,28 +952,29 @@ ntb_db_clear_mask(struct ntb_softc *ntb, uint64_t bits)
             (uintmax_t)(bits & ~ntb->db_valid_mask),
             (uintmax_t)ntb->db_valid_mask));
 
-       HW_LOCK(ntb);
+       DB_MASK_LOCK(ntb);
        ntb->db_mask &= ~bits;
-       ntb_db_write(ntb, ntb->reg_ofs.ldb_mask, ntb->db_mask);
-       HW_UNLOCK(ntb);
+       db_iowrite(ntb, ntb->reg_ofs.ldb_mask, ntb->db_mask);
+       DB_MASK_UNLOCK(ntb);
 }
 
-static inline void
-mask_ldb_interrupt(struct ntb_softc *ntb, unsigned int idx)
+uint64_t
+ntb_db_read(struct ntb_softc *ntb)
 {
-       uint64_t mask;
 
-       mask = 1ull << (idx * ntb->db_vec_shift);
-       ntb_db_set_mask(ntb, mask);
+       return (db_ioread(ntb, ntb->reg_ofs.ldb));
 }
 
-static inline void
-unmask_ldb_interrupt(struct ntb_softc *ntb, unsigned int idx)
+void
+ntb_db_clear(struct ntb_softc *ntb, uint64_t bits)
 {
-       uint64_t mask;
 
-       mask = 1ull << (idx * ntb->db_vec_shift);
-       ntb_db_clear_mask(ntb, mask);
+       KASSERT((bits & ~ntb->db_valid_mask) == 0,
+           ("%s: Invalid bits 0x%jx (valid: 0x%jx)", __func__,
+            (uintmax_t)(bits & ~ntb->db_valid_mask),
+            (uintmax_t)ntb->db_valid_mask));
+
+       db_iowrite(ntb, ntb->reg_ofs.ldb, bits);
 }
 
 static inline uint64_t
@@ -943,79 +988,61 @@ ntb_vec_mask(struct ntb_softc *ntb, uint64_t db_vector)
 }
 
 static void
-handle_irq(void *arg)
+ntb_interrupt(struct ntb_softc *ntb, uint32_t vec)
 {
-       struct ntb_db_cb *db_cb = arg;
-       struct ntb_softc *ntb = db_cb->ntb;
        uint64_t vec_mask;
-       int rc;
 
        ntb->last_ts = ticks;
-       vec_mask = ntb_vec_mask(ntb, db_cb->db_num);
+       vec_mask = ntb_vec_mask(ntb, vec);
 
        if ((vec_mask & ntb->db_link_mask) != 0) {
-               rc = ntb_poll_link(ntb);
-               if (rc != 0)
-                       device_printf(ntb->device,
-                           "Error determining link status\n");
+               if (ntb_poll_link(ntb))
+                       ntb_link_event(ntb);
        }
 
-       if (db_cb->callback != NULL) {
-               KASSERT(!db_cb->reserved, ("user callback on link event cb"));
-               mask_ldb_interrupt(ntb, db_cb->db_num);
-       }
+       if ((vec_mask & ntb->db_valid_mask) != 0)
+               ntb_db_event(ntb, vec);
+}
 
-       ntb_db_write(ntb, ntb->reg_ofs.ldb, vec_mask);
+static void
+ndev_vec_isr(void *arg)
+{
+       struct ntb_vec *nvec = arg;
 
-       if (db_cb->callback != NULL)
-               callout_reset(&db_cb->irq_work, 0, ntb_irq_work, db_cb);
+       ntb_interrupt(nvec->ntb, nvec->num);
 }
 
 static void
-ntb_handle_legacy_interrupt(void *arg)
+ndev_irq_isr(void *arg)
 {
-       struct ntb_softc *ntb = arg;
-       unsigned int i;
-       uint64_t ldb;
-
-       ldb = ntb_db_read(ntb, ntb->reg_ofs.ldb);
-       while (ldb != 0) {
-               i = ffs(ldb);
-               ldb &= ldb - 1;
-               handle_irq(&ntb->db_cb[i]);
-       }
+       /* If we couldn't set up MSI-X, we only have the one vector. */
+       ntb_interrupt(arg, 0);
 }
 
 static int
-ntb_create_callbacks(struct ntb_softc *ntb, uint32_t num_vectors)
+ntb_create_msix_vec(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,
+       ntb->msix_vec = malloc(num_vectors * sizeof(*ntb->msix_vec), M_NTB,
            M_ZERO | M_WAITOK);
        for (i = 0; i < num_vectors; i++) {
-               ntb->db_cb[i].db_num = i;
-               ntb->db_cb[i].ntb = ntb;
+               ntb->msix_vec[i].num = i;
+               ntb->msix_vec[i].ntb = ntb;
        }
 
        return (0);
 }
 
 static void
-ntb_free_callbacks(struct ntb_softc *ntb)
+ntb_free_msix_vec(struct ntb_softc *ntb)
 {
-       uint8_t i;
 
-       if (ntb->db_cb == NULL)
+       if (ntb->msix_vec == NULL)
                return;
 
-       for (i = 0; i < ntb->max_cbs; i++)
-               ntb_unregister_db_callback(ntb, i);
-
-       free(ntb->db_cb, M_NTB);
-       ntb->db_cb = NULL;
-       ntb->max_cbs = 0;
+       free(ntb->msix_vec, M_NTB);
+       ntb->msix_vec = NULL;
 }
 
 static struct ntb_hw_info *
@@ -1179,7 +1206,7 @@ ntb_xeon_init_dev(struct ntb_softc *ntb)
            PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN);
 
        /* Enable link training */
-       ntb_link_enable(ntb);
+       ntb_link_enable(ntb, NTB_SPEED_AUTO, NTB_WIDTH_AUTO);
 
        return (0);
 }
@@ -1220,7 +1247,7 @@ ntb_soc_init_dev(struct ntb_softc *ntb)
            PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN);
 
        /* Initiate PCI-E link training */
-       ntb_link_enable(ntb);
+       ntb_link_enable(ntb, NTB_SPEED_AUTO, NTB_WIDTH_AUTO);
 
        callout_reset(&ntb->heartbeat_timer, 0, soc_link_hb, ntb);
 
@@ -1288,29 +1315,33 @@ xeon_reset_sbar_size(struct ntb_softc *ntb, enum ntb_bar idx,
 }
 
 static void
-xeon_set_sbar_base_and_limit(struct ntb_softc *ntb, uint64_t base_addr,
+xeon_set_sbar_base_and_limit(struct ntb_softc *ntb, uint64_t bar_addr,
     enum ntb_bar idx, enum ntb_bar regbar)
 {
-       struct ntb_pci_bar_info *bar;
-       vm_paddr_t bar_addr;
+       uint64_t reg_val;
+       uint32_t base_reg, lmt_reg;
 
-       bar = &ntb->bar_info[idx];
-       bar_addr = base_addr + ((idx == regbar) ? ntb->b2b_off : 0);
+       bar_get_xlat_params(ntb, idx, &base_reg, NULL, &lmt_reg);
+       if (idx == regbar)
+               bar_addr += ntb->b2b_off;
 
-       if (HAS_FEATURE(NTB_SPLIT_BAR) && idx >= NTB_B2B_BAR_2) {
-               ntb_reg_write(4, bar->sbarbase_off, bar_addr);
-               ntb_reg_write(4, bar->sbarlmt_off, bar_addr);
-               bar_addr = ntb_reg_read(4, bar->sbarbase_off);
-               (void)bar_addr;
-               bar_addr = ntb_reg_read(4, bar->sbarlmt_off);
+       if (!bar_is_64bit(ntb, idx)) {
+               ntb_reg_write(4, base_reg, bar_addr);
+               reg_val = ntb_reg_read(4, base_reg);
+               (void)reg_val;
+
+               ntb_reg_write(4, lmt_reg, bar_addr);
+               reg_val = ntb_reg_read(4, lmt_reg);
+               (void)reg_val;
        } else {
-               ntb_reg_write(8, bar->sbarbase_off, bar_addr);
-               ntb_reg_write(8, bar->sbarlmt_off, bar_addr);
-               bar_addr = ntb_reg_read(8, bar->sbarbase_off);
-               (void)bar_addr;
-               bar_addr = ntb_reg_read(8, bar->sbarlmt_off);
+               ntb_reg_write(8, base_reg, bar_addr);
+               reg_val = ntb_reg_read(8, base_reg);
+               (void)reg_val;
+
+               ntb_reg_write(8, lmt_reg, bar_addr);
+               reg_val = ntb_reg_read(8, lmt_reg);
+               (void)reg_val;
        }
-       (void)bar_addr;
 }
 
 static void
@@ -1444,41 +1475,62 @@ xeon_setup_b2b_mw(struct ntb_softc *ntb, const struct ntb_b2b_addr *addr,
        return (0);
 }
 
+static inline bool
+link_is_up(struct ntb_softc *ntb)
+{
+
+       if (ntb->type == NTB_XEON)
+               return ((ntb->lnk_sta & NTB_LINK_STATUS_ACTIVE) != 0);
+
+       KASSERT(ntb->type == NTB_SOC, ("ntb type"));
+       return ((ntb->ntb_ctl & SOC_CNTL_LINK_DOWN) == 0);
+}
+
+static inline bool
+soc_link_is_err(struct ntb_softc *ntb)
+{
+       uint32_t status;
+
+       KASSERT(ntb->type == NTB_SOC, ("ntb type"));
+
+       status = ntb_reg_read(4, SOC_LTSSMSTATEJMP_OFFSET);
+       if ((status & SOC_LTSSMSTATEJMP_FORCEDETECT) != 0)
+               return (true);
+
+       status = ntb_reg_read(4, SOC_IBSTERRRCRVSTS0_OFFSET);
+       return ((status & SOC_IBIST_ERR_OFLOW) != 0);
+}
+
 /* SOC does not have link status interrupt, poll on that platform */
 static void
 soc_link_hb(void *arg)
 {
        struct ntb_softc *ntb = arg;
-       uint32_t status32;
-       int rc;
+       sbintime_t timo, poll_ts;
+
+       timo = NTB_HB_TIMEOUT * hz;
+       poll_ts = ntb->last_ts + timo;
 
        /*
         * Delay polling the link status if an interrupt was received, unless
         * the cached link status says the link is down.
         */
-       if ((long)ticks - ((long)ntb->last_ts + NTB_HB_TIMEOUT * hz) < 0 &&
-           (ntb->ntb_ctl & SOC_CNTL_LINK_DOWN) == 0)
+       if ((sbintime_t)ticks - poll_ts < 0 && link_is_up(ntb)) {
+               timo = poll_ts - ticks;
                goto out;
+       }
 
+       if (ntb_poll_link(ntb))
+               ntb_link_event(ntb);
 
-       rc = ntb_poll_link(ntb);
-       if (rc != 0)
-               device_printf(ntb->device,
-                   "Error determining link status\n");
-
-       /* Check to see if a link error is the cause of the link down */
-       if (ntb->link_status == NTB_LINK_DOWN) {
-               status32 = ntb_reg_read(4, SOC_LTSSMSTATEJMP_OFFSET);
-               if ((status32 & SOC_LTSSMSTATEJMP_FORCEDETECT) != 0) {
-                       callout_reset(&ntb->lr_timer, 0, recover_soc_link,
-                           ntb);
-                       return;
-               }
+       if (!link_is_up(ntb) && soc_link_is_err(ntb)) {
+               /* Link is down with error, proceed with recovery */
+               callout_reset(&ntb->lr_timer, 0, recover_soc_link, ntb);
+               return;
        }
 
 out:
-       callout_reset(&ntb->heartbeat_timer, NTB_HB_TIMEOUT * hz, soc_link_hb,
-           ntb);
+       callout_reset(&ntb->heartbeat_timer, timo, soc_link_hb, ntb);
 }
 
 static void
@@ -1520,58 +1572,141 @@ soc_perform_link_restart(struct ntb_softc *ntb)
        ntb_reg_write(4, SOC_LTSSMSTATEJMP_OFFSET, status);
 }
 
-static void
-ntb_handle_link_event(struct ntb_softc *ntb, int link_state)
+/*
+ * ntb_set_ctx() - associate a driver context with an ntb device
+ * @ntb:        NTB device context
+ * @ctx:        Driver context
+ * @ctx_ops:    Driver context operations
+ *
+ * Associate a driver context and operations with a ntb device.  The context is
+ * provided by the client driver, and the driver may associate a different
+ * context with each ntb device.
+ *
+ * Return: Zero if the context is associated, otherwise an error number.
+ */
+int
+ntb_set_ctx(struct ntb_softc *ntb, void *ctx, const struct ntb_ctx_ops *ops)
 {
-       enum ntb_hw_event event;
-       uint16_t status;
 
-       if (ntb->link_status == link_state)
-               return;
-
-       if (link_state == NTB_LINK_UP) {
-               device_printf(ntb->device, "Link Up\n");
-               ntb->link_status = NTB_LINK_UP;
-               event = NTB_EVENT_HW_LINK_UP;
+       if (ctx == NULL || ops == NULL)
+               return (EINVAL);
+       if (ntb->ctx_ops != NULL)
+               return (EINVAL);
 
-               if (ntb->type == NTB_SOC ||
-                   ntb->conn_type == NTB_CONN_TRANSPARENT)
-                       status = ntb_reg_read(2, ntb->reg->lnk_sta);
-               else
-                       status = pci_read_config(ntb->device,
-                           XEON_LINK_STATUS_OFFSET, 2);
-               ntb->link_width = (status & NTB_LINK_WIDTH_MASK) >> 4;
-               ntb->link_speed = (status & NTB_LINK_SPEED_MASK);
-               device_printf(ntb->device, "Link Width %d, Link Speed %d\n",
-                   ntb->link_width, ntb->link_speed);
-               callout_reset(&ntb->heartbeat_timer, NTB_HB_TIMEOUT * hz,
-                   soc_link_hb, ntb);
-       } else {
-               device_printf(ntb->device, "Link Down\n");
-               ntb->link_status = NTB_LINK_DOWN;
-               event = NTB_EVENT_HW_LINK_DOWN;
-               /* Do not modify link width/speed, we need it in link recovery */
+       CTX_LOCK(ntb);
+       if (ntb->ctx_ops != NULL) {
+               CTX_UNLOCK(ntb);
+               return (EINVAL);
        }
+       ntb->ntb_ctx = ctx;
+       ntb->ctx_ops = ops;
+       CTX_UNLOCK(ntb);
 
-       /* notify the upper layer if we have an event change */
-       if (ntb->event_cb != NULL)
-               ntb->event_cb(ntb->ntb_transport, event);
+       return (0);
 }
 
+/*
+ * It is expected that this will only be used from contexts where the ctx_lock
+ * is not needed to protect ntb_ctx lifetime.
+ */
+void *
+ntb_get_ctx(struct ntb_softc *ntb, const struct ntb_ctx_ops **ops)
+{
+
+       KASSERT(ntb->ntb_ctx != NULL && ntb->ctx_ops != NULL, ("bogus"));
+       if (ops != NULL)
+               *ops = ntb->ctx_ops;
+       return (ntb->ntb_ctx);
+}
+
+/*
+ * ntb_clear_ctx() - disassociate any driver context from an ntb device
+ * @ntb:        NTB device context
+ *
+ * Clear any association that may exist between a driver context and the ntb
+ * device.
+ */
+void
+ntb_clear_ctx(struct ntb_softc *ntb)
+{
+
+       CTX_LOCK(ntb);
+       ntb->ntb_ctx = NULL;
+       ntb->ctx_ops = NULL;
+       CTX_UNLOCK(ntb);
+}
+
+/*
+ * ntb_link_event() - notify driver context of a change in link status
+ * @ntb:        NTB device context
+ *
+ * Notify the driver context that the link status may have changed.  The driver
+ * should call ntb_link_is_up() to get the current status.
+ */
+void
+ntb_link_event(struct ntb_softc *ntb)
+{
+
+       CTX_LOCK(ntb);
+       if (ntb->ctx_ops != NULL && ntb->ctx_ops->link_event != NULL)
+               ntb->ctx_ops->link_event(ntb->ntb_ctx);
+       CTX_UNLOCK(ntb);
+}
+
+/*
+ * ntb_db_event() - notify driver context of a doorbell event
+ * @ntb:        NTB device context
+ * @vector:     Interrupt vector number
+ *
+ * Notify the driver context of a doorbell event.  If hardware supports
+ * multiple interrupt vectors for doorbells, the vector number indicates which
+ * vector received the interrupt.  The vector number is relative to the first
+ * vector used for doorbells, starting at zero, and must be less than
+ * ntb_db_vector_count().  The driver may call ntb_db_read() to check which
+ * doorbell bits need service, and ntb_db_vector_mask() to determine which of
+ * those bits are associated with the vector number.
+ */
 static void
-ntb_link_enable(struct ntb_softc *ntb)
+ntb_db_event(struct ntb_softc *ntb, uint32_t vec)
+{
+
+       CTX_LOCK(ntb);
+       if (ntb->ctx_ops != NULL && ntb->ctx_ops->db_event != NULL)
+               ntb->ctx_ops->db_event(ntb->ntb_ctx, vec);
+       CTX_UNLOCK(ntb);
+}
+
+/*
+ * ntb_link_enable() - enable the link on the secondary side of the ntb
+ * @ntb:        NTB device context
+ * @max_speed:  The maximum link speed expressed as PCIe generation number[0]
+ * @max_width:  The maximum link width expressed as the number of PCIe lanes[0]
+ *
+ * Enable the link on the secondary side of the ntb.  This can only be done
+ * from the primary side of the ntb in primary or b2b topology.  The ntb device
+ * should train the link to its maximum speed and width, or the requested speed
+ * and width, whichever is smaller, if supported.
+ *
+ * Return: Zero on success, otherwise an error number.
+ *
+ * [0]: Only NTB_SPEED_AUTO and NTB_WIDTH_AUTO are valid inputs; other speed
+ *      and width input will be ignored.
+ */
+int
+ntb_link_enable(struct ntb_softc *ntb, enum ntb_speed s __unused,
+    enum ntb_width w __unused)
 {
        uint32_t cntl;
 
        if (ntb->type == NTB_SOC) {
                pci_write_config(ntb->device, NTB_PPD_OFFSET,
                    ntb->ppd | SOC_PPD_INIT_LINK, 4);
-               return;
+               return (0);
        }
 
        if (ntb->conn_type == NTB_CONN_TRANSPARENT) {
-               ntb_handle_link_event(ntb, NTB_LINK_UP);
-               return;
+               ntb_link_event(ntb);
+               return (0);
        }
 
        cntl = ntb_reg_read(4, ntb->reg->ntb_ctl);
@@ -1581,16 +1716,29 @@ ntb_link_enable(struct ntb_softc *ntb)
        if (HAS_FEATURE(NTB_SPLIT_BAR))
                cntl |= NTB_CNTL_P2S_BAR5_SNOOP | NTB_CNTL_S2P_BAR5_SNOOP;
        ntb_reg_write(4, ntb->reg->ntb_ctl, cntl);
+       return (0);
 }
 
-static void
+/*
+ * ntb_link_disable() - disable the link on the secondary side of the ntb
+ * @ntb:        NTB device context
+ *
+ * Disable the link on the secondary side of the ntb.  This can only be done
+ * from the primary side of the ntb in primary or b2b topology.  The ntb device
+ * should disable the link.  Returning from this call must indicate that a
+ * barrier has passed, though with no more writes may pass in either direction
+ * across the link, except if this call returns an error number.
+ *
+ * Return: Zero on success, otherwise an error number.
+ */
+int
 ntb_link_disable(struct ntb_softc *ntb)
 {
        uint32_t cntl;
 
        if (ntb->conn_type == NTB_CONN_TRANSPARENT) {
-               ntb_handle_link_event(ntb, NTB_LINK_DOWN);
-               return;
+               ntb_link_event(ntb);
+               return (0);
        }
 
        cntl = ntb_reg_read(4, ntb->reg->ntb_ctl);
@@ -1600,6 +1748,7 @@ ntb_link_disable(struct ntb_softc *ntb)
                cntl &= ~(NTB_CNTL_P2S_BAR5_SNOOP | NTB_CNTL_S2P_BAR5_SNOOP);
        cntl |= NTB_CNTL_LINK_DISABLE | NTB_CNTL_CFG_LOCK;
        ntb_reg_write(4, ntb->reg->ntb_ctl, cntl);
+       return (0);
 }
 
 static void
@@ -1648,217 +1797,55 @@ retry:
            ntb);
 }
 
-static int
+/*
+ * Polls the HW link status register(s); returns true if something has changed.
+ */
+static bool
 ntb_poll_link(struct ntb_softc *ntb)
 {
-       int link_state;
        uint32_t ntb_cntl;
-       uint16_t status;
+       uint16_t reg_val;
 
        if (ntb->type == NTB_SOC) {
-               HW_LOCK(ntb);
                ntb_cntl = ntb_reg_read(4, ntb->reg->ntb_ctl);
-               if (ntb_cntl == ntb->ntb_ctl) {
-                       HW_UNLOCK(ntb);
-                       return (0);
-               }
+               if (ntb_cntl == ntb->ntb_ctl)
+                       return (false);
+
                ntb->ntb_ctl = ntb_cntl;
                ntb->lnk_sta = ntb_reg_read(4, ntb->reg->lnk_sta);
-               HW_UNLOCK(ntb);
-
-               if ((ntb_cntl & SOC_CNTL_LINK_DOWN) != 0)
-                       link_state = NTB_LINK_DOWN;
-               else
-                       link_state = NTB_LINK_UP;
        } else {
-               status = pci_read_config(ntb->device, ntb->reg->lnk_sta, 2);
-               if (status == ntb->lnk_sta)
-                       return (0);
-               ntb->lnk_sta = status;
+               db_iowrite(ntb, ntb->reg_ofs.ldb, ntb->db_link_mask);
 
-               if ((status & NTB_LINK_STATUS_ACTIVE) != 0)
-                       link_state = NTB_LINK_UP;
-               else
-                       link_state = NTB_LINK_DOWN;
-       }
+               reg_val = pci_read_config(ntb->device, ntb->reg->lnk_sta, 2);
+               if (reg_val == ntb->lnk_sta)
+                       return (false);
 
-       ntb_handle_link_event(ntb, link_state);
-       return (0);
-}
-
-static void
-ntb_irq_work(void *arg)
-{
-       struct ntb_db_cb *db_cb = arg;
-       struct ntb_softc *ntb;
-       int rc;
-
-       rc = db_cb->callback(db_cb->data, db_cb->db_num);
-       /* Poll if forward progress was made. */
-       if (rc != 0) {
-               callout_reset(&db_cb->irq_work, 0, ntb_irq_work, db_cb);
-               return;
-       }
-
-       /* Unmask interrupt if no progress was made. */
-       ntb = db_cb->ntb;
-       unmask_ldb_interrupt(ntb, db_cb->db_num);
-}
-
-/*
- * Public API to the rest of the OS
- */
-
-/**
- * ntb_register_event_callback() - register event callback
- * @ntb: pointer to ntb_softc instance
- * @func: callback function to register
- *
- * This function registers a callback for any HW driver events such as link
- * up/down, power management notices and etc.
- *
- * RETURNS: An appropriate ERRNO error value on error, or zero for success.
- */
-int
-ntb_register_event_callback(struct ntb_softc *ntb, ntb_event_callback func)
-{
-
-       if (ntb->event_cb != NULL)
-               return (EINVAL);
-
-       ntb->event_cb = func;
-
-       return (0);
-}
-
-/**
- * ntb_unregister_event_callback() - unregisters the event callback
- * @ntb: pointer to ntb_softc instance
- *
- * This function unregisters the existing callback from transport
- */
-void
-ntb_unregister_event_callback(struct ntb_softc *ntb)
-{
-
-       ntb->event_cb = NULL;
-}
-
-/**
- * ntb_register_db_callback() - register a callback for doorbell interrupt
- * @ntb: pointer to ntb_softc instance
- * @idx: doorbell index to register callback, zero based
- * @data: pointer to be returned to caller with every callback
- * @func: callback function to register
- *
- * This function registers a callback function for the doorbell interrupt
- * on the primary side. The function will unmask the doorbell as well to
- * allow interrupt.
- *
- * RETURNS: An appropriate ERRNO error value on error, or zero for success.
- */
-int
-ntb_register_db_callback(struct ntb_softc *ntb, unsigned int idx, void *data,
-    ntb_db_callback func)
-{
-       struct ntb_db_cb *db_cb = &ntb->db_cb[idx];
-
-       if (idx >= ntb->max_cbs || db_cb->callback != NULL || db_cb->reserved) {
-               device_printf(ntb->device, "Invalid Index.\n");
-               return (EINVAL);
+               ntb->lnk_sta = reg_val;
        }
-
-       db_cb->callback = func;
-       db_cb->data = data;
-       callout_init(&db_cb->irq_work, 1);
-
-       unmask_ldb_interrupt(ntb, idx);
-
-       return (0);
+       return (true);
 }
 
-/**
- * ntb_unregister_db_callback() - unregister a callback for doorbell interrupt
- * @ntb: pointer to ntb_softc instance
- * @idx: doorbell index to register callback, zero based
- *
- * This function unregisters a callback function for the doorbell interrupt
- * on the primary side. The function will also mask the said doorbell.
- */
-void
-ntb_unregister_db_callback(struct ntb_softc *ntb, unsigned int idx)
+static inline enum ntb_speed
+ntb_link_sta_speed(struct ntb_softc *ntb)
 {
 
-       if (idx >= ntb->max_cbs || ntb->db_cb[idx].callback == NULL)
-               return;
-
-       mask_ldb_interrupt(ntb, idx);
-
-       callout_drain(&ntb->db_cb[idx].irq_work);
-       ntb->db_cb[idx].callback = NULL;
+       if (!link_is_up(ntb))
+               return (NTB_SPEED_NONE);
+       return (ntb->lnk_sta & NTB_LINK_SPEED_MASK);
 }
 
-/**
- * ntb_find_transport() - find the transport pointer
- * @transport: pointer to pci device
- *
- * Given the pci device pointer, return the transport pointer passed in when
- * the transport attached when it was inited.
- *
- * RETURNS: pointer to transport.
- */
-void *
-ntb_find_transport(struct ntb_softc *ntb)
+static inline enum ntb_width
+ntb_link_sta_width(struct ntb_softc *ntb)
 {
 
-       return (ntb->ntb_transport);
+       if (!link_is_up(ntb))
+               return (NTB_WIDTH_NONE);
+       return (NTB_LNK_STA_WIDTH(ntb->lnk_sta));
 }
 
-/**
- * ntb_register_transport() - Register NTB transport with NTB HW driver
- * @transport: transport identifier
- *
- * This function allows a transport to reserve the hardware driver for
- * NTB usage.
- *
- * RETURNS: pointer to ntb_softc, NULL on error.
- */
-struct ntb_softc *
-ntb_register_transport(struct ntb_softc *ntb, void *transport)
-{
-
-       /*
-        * TODO: when we have more than one transport, we will need to rewrite
-        * this to prevent race conditions
-        */
-       if (ntb->ntb_transport != NULL)
-               return (NULL);
-
-       ntb->ntb_transport = transport;
-       return (ntb);
-}
-
-/**
- * ntb_unregister_transport() - Unregister the transport with the NTB HW driver
- * @ntb - ntb_softc of the transport to be freed
- *
- * This function unregisters the transport from the HW driver and performs any
- * necessary cleanups.
+/*
+ * Public API to the rest of the OS
  */
-void
-ntb_unregister_transport(struct ntb_softc *ntb)
-{
-       uint8_t i;
-
-       if (ntb->ntb_transport == NULL)
-               return;
-
-       for (i = 0; i < ntb->max_cbs; i++)
-               ntb_unregister_db_callback(ntb, i);
-
-       ntb_unregister_event_callback(ntb);
-       ntb->ntb_transport = NULL;
-}
 
 /**
  * ntb_get_max_spads() - get the total scratch regs usable
@@ -1876,13 +1863,6 @@ ntb_get_max_spads(struct ntb_softc *ntb)
        return (ntb->spad_count);
 }
 
-uint8_t
-ntb_get_max_cbs(struct ntb_softc *ntb)
-{
-
-       return (ntb->max_cbs);
-}
-
 uint8_t
 ntb_mw_count(struct ntb_softc *ntb)
 {
@@ -1988,89 +1968,153 @@ ntb_peer_spad_read(struct ntb_softc *ntb, unsigned int idx, uint32_t *val)
        return (0);
 }
 
-/**
- * ntb_get_mw_vbase() - get virtual addr for the NTB memory window
- * @ntb: pointer to ntb_softc instance
- * @mw: memory window number
+/*
+ * ntb_mw_get_range() - get the range of a memory window
+ * @ntb:        NTB device context
+ * @idx:        Memory window number
+ * @base:       OUT - the base address for mapping the memory window
+ * @size:       OUT - the size for mapping the memory window
+ * @align:      OUT - the base alignment for translating the memory window
+ * @align_size: OUT - the size alignment for translating the memory window
  *
- * This function provides the base virtual address of the memory window
- * specified.
+ * Get the range of a memory window.  NULL may be given for any output
+ * parameter if the value is not needed.  The base and size may be used for
+ * mapping the memory window, to access the peer memory.  The alignment and
+ * size may be used for translating the memory window, for the peer to access
+ * memory on the local system.
  *
- * RETURNS: pointer to virtual address, or NULL on error.
+ * Return: Zero on success, otherwise an error number.
  */
-void *
-ntb_get_mw_vbase(struct ntb_softc *ntb, unsigned int mw)
+int
+ntb_mw_get_range(struct ntb_softc *ntb, unsigned mw_idx, vm_paddr_t *base,
+    void **vbase, size_t *size, size_t *align, size_t *align_size)
 {
+       struct ntb_pci_bar_info *bar;
+       size_t bar_b2b_off;
 
-       if (mw >= ntb_mw_count(ntb))
-               return (NULL);
-
-       return (ntb->bar_info[ntb_mw_to_bar(ntb, mw)].vbase);
-}
-
-bus_addr_t
-ntb_get_mw_pbase(struct ntb_softc *ntb, unsigned int mw)
-{
+       if (mw_idx >= ntb_mw_count(ntb))
+               return (EINVAL);
 
-       if (mw >= ntb_mw_count(ntb))
-               return (0);
+       bar = &ntb->bar_info[ntb_mw_to_bar(ntb, mw_idx)];
+       bar_b2b_off = 0;
+       if (mw_idx == ntb->b2b_mw_idx) {
+               KASSERT(ntb->b2b_off != 0,
+                   ("user shouldn't get non-shared b2b mw"));
+               bar_b2b_off = ntb->b2b_off;
+       }
 
-       return (ntb->bar_info[ntb_mw_to_bar(ntb, mw)].pbase);
+       if (base != NULL)
+               *base = bar->pbase + bar_b2b_off;
+       if (vbase != NULL)
+               *vbase = (char *)bar->vbase + bar_b2b_off;
+       if (size != NULL)
+               *size = bar->size - bar_b2b_off;
+       if (align != NULL)
+               *align = bar->size;
+       if (align_size != NULL)
+               *align_size = 1;
+       return (0);
 }
 
-/**
- * ntb_get_mw_size() - return size of NTB memory window
- * @ntb: pointer to ntb_softc instance
- * @mw: memory window number
+/*
+ * ntb_mw_set_trans() - set the translation of a memory window
+ * @ntb:        NTB device context
+ * @idx:        Memory window number
+ * @addr:       The dma address local memory to expose to the peer
+ * @size:       The size of the local memory to expose to the peer
  *
- * This function provides the physical size of the memory window specified
+ * Set the translation of a memory window.  The peer may access local memory
+ * through the window starting at the address, up to the size.  The address
+ * must be aligned to the alignment specified by ntb_mw_get_range().  The size
+ * must be aligned to the size alignment specified by ntb_mw_get_range().
  *
- * RETURNS: the size of the memory window or zero on error
+ * Return: Zero on success, otherwise an error number.
  */
-u_long
-ntb_get_mw_size(struct ntb_softc *ntb, unsigned int mw)
+int
+ntb_mw_set_trans(struct ntb_softc *ntb, unsigned idx, bus_addr_t addr,
+    size_t size)
 {
+       struct ntb_pci_bar_info *bar;
+       uint64_t base, limit, reg_val;
+       size_t bar_size, mw_size;
+       uint32_t base_reg, xlat_reg, limit_reg;
+       enum ntb_bar bar_num;
 
-       if (mw >= ntb_mw_count(ntb))
-               return (0);
+       if (idx >= ntb_mw_count(ntb))
+               return (EINVAL);
 
-       return (ntb->bar_info[ntb_mw_to_bar(ntb, mw)].size);
-}
+       bar_num = ntb_mw_to_bar(ntb, idx);
+       bar = &ntb->bar_info[bar_num];
 
-/**
- * ntb_set_mw_addr - set the memory window address
- * @ntb: pointer to ntb_softc instance
- * @mw: memory window number
- * @addr: base address for data
- *
- * This function sets the base physical address of the memory window.  This
- * memory address is where data from the remote system will be transfered into
- * or out of depending on how the transport is configured.
- */
-void
-ntb_set_mw_addr(struct ntb_softc *ntb, unsigned int mw, uint64_t addr)
-{
+       bar_size = bar->size;
+       if (idx == ntb->b2b_mw_idx)
+               mw_size = bar_size - ntb->b2b_off;
+       else
+               mw_size = bar_size;
 
-       if (mw >= ntb_mw_count(ntb))
-               return;
+       /* Hardware requires that addr is aligned to bar size */
+       if ((addr & (bar_size - 1)) != 0)
+               return (EINVAL);
 
-       switch (ntb_mw_to_bar(ntb, mw)) {
-       case NTB_B2B_BAR_1:
-               ntb_reg_write(8, ntb->xlat_reg->bar2_xlat, addr);
-               break;
-       case NTB_B2B_BAR_2:
-               if (HAS_FEATURE(NTB_SPLIT_BAR))
-                       ntb_reg_write(4, ntb->reg_ofs.bar4_xlat, addr);
-               else
-                       ntb_reg_write(8, ntb->reg_ofs.bar4_xlat, addr);
-               break;
-       case NTB_B2B_BAR_3:
-               ntb_reg_write(4, ntb->reg_ofs.bar5_xlat, addr);
-               break;
-       default:
-               KASSERT(false, ("invalid BAR"));
-               break;
+       if (size > mw_size)
+               return (EINVAL);
+
+       bar_get_xlat_params(ntb, bar_num, &base_reg, &xlat_reg, &limit_reg);
+
+       limit = 0;
+       if (bar_is_64bit(ntb, bar_num)) {
+               base = ntb_reg_read(8, base_reg);
+
+               if (limit_reg != 0 && size != mw_size)
+                       limit = base + size;
+
+               /* Set and verify translation address */
+               ntb_reg_write(8, xlat_reg, addr);
+               reg_val = ntb_reg_read(8, xlat_reg);
+               if (reg_val != addr) {
+                       ntb_reg_write(8, xlat_reg, 0);
+                       return (EIO);
+               }
+
+               /* Set and verify the limit */
+               ntb_reg_write(8, limit_reg, limit);
+               reg_val = ntb_reg_read(8, limit_reg);
+               if (reg_val != limit) {
+                       ntb_reg_write(8, limit_reg, base);
+                       ntb_reg_write(8, xlat_reg, 0);
+                       return (EIO);
+               }
+       } else {
+               /* Configure 32-bit (split) BAR MW */
+
+               if ((addr & ~UINT32_MAX) != 0)
+                       return (EINVAL);
+               if (((addr + size) & ~UINT32_MAX) != 0)
+                       return (EINVAL);
+
+               base = ntb_reg_read(4, base_reg);
+
+               if (limit_reg != 0 && size != mw_size)
+                       limit = base + size;
+
+               /* Set and verify translation address */
+               ntb_reg_write(4, xlat_reg, addr);
+               reg_val = ntb_reg_read(4, xlat_reg);
+               if (reg_val != addr) {
+                       ntb_reg_write(4, xlat_reg, 0);
+                       return (EIO);
+               }
+
+               /* Set and verify the limit */
+               ntb_reg_write(4, limit_reg, limit);
+               reg_val = ntb_reg_read(4, limit_reg);
+               if (reg_val != limit) {
+                       ntb_reg_write(4, limit_reg, base);
+                       ntb_reg_write(4, xlat_reg, 0);
+                       return (EIO);
+               }
        }
+       return (0);
 }
 
 /**
@@ -2090,7 +2134,7 @@ ntb_peer_db_set(struct ntb_softc *ntb, uint64_t bit)
                return;
        }
 
-       ntb_db_write(ntb, ntb->peer_reg->db_bell, bit);
+       db_iowrite(ntb, ntb->peer_reg->db_bell, bit);
 }
 
 /*
@@ -2132,18 +2176,23 @@ ntb_get_peer_db_addr(struct ntb_softc *ntb, vm_size_t *sz_out)
 }
 
 /**
- * ntb_link_is_up() - return the hardware link status
- * @ndev: pointer to ntb_device instance
- *
- * Returns true if the hardware is connected to the remote system
+ * ntb_link_is_up() - get the current ntb link state
+ * @ntb:        NTB device context
+ * @speed:      OUT - The link speed expressed as PCIe generation number
+ * @width:      OUT - The link width expressed as the number of PCIe lanes
  *
  * RETURNS: true or false based on the hardware link state
  */
 bool
-ntb_link_is_up(struct ntb_softc *ntb)
+ntb_link_is_up(struct ntb_softc *ntb, enum ntb_speed *speed,
+    enum ntb_width *width)
 {
 
-       return (ntb->link_status == NTB_LINK_UP);
+       if (speed != NULL)
+               *speed = ntb_link_sta_speed(ntb);
+       if (width != NULL)
+               *width = ntb_link_sta_width(ntb);
+       return (link_is_up(ntb));
 }
 
 static void
index 05a5f90b583709d64a3426ae15644699ab99ffcf..c28596dc7fa9b12b707b22f5068d0637305d12d9 100644 (file)
@@ -34,36 +34,53 @@ struct ntb_softc;
 
 #define NTB_MAX_NUM_MW 3
 
-enum ntb_link_event {
-       NTB_LINK_DOWN = 0,
-       NTB_LINK_UP,
+enum ntb_speed {
+       NTB_SPEED_AUTO = -1,
+       NTB_SPEED_NONE = 0,
+       NTB_SPEED_GEN1 = 1,
+       NTB_SPEED_GEN2 = 2,
+       NTB_SPEED_GEN3 = 3,
 };
 
-enum ntb_hw_event {
-       NTB_EVENT_SW_EVENT0 = 0,
-       NTB_EVENT_SW_EVENT1,
-       NTB_EVENT_SW_EVENT2,
-       NTB_EVENT_HW_ERROR,
-       NTB_EVENT_HW_LINK_UP,
-       NTB_EVENT_HW_LINK_DOWN,
+enum ntb_width {
+       NTB_WIDTH_AUTO = -1,
+       NTB_WIDTH_NONE = 0,
+       NTB_WIDTH_1 = 1,
+       NTB_WIDTH_2 = 2,
+       NTB_WIDTH_4 = 4,
+       NTB_WIDTH_8 = 8,
+       NTB_WIDTH_12 = 12,
+       NTB_WIDTH_16 = 16,
+       NTB_WIDTH_32 = 32,
 };
 
 SYSCTL_DECL(_hw_ntb);
 
-typedef int (*ntb_db_callback)(void *data, int db_num);
-typedef void (*ntb_event_callback)(void *data, enum ntb_hw_event event);
-
-int ntb_register_event_callback(struct ntb_softc *ntb, ntb_event_callback func);
-void ntb_unregister_event_callback(struct ntb_softc *ntb);
-int ntb_register_db_callback(struct ntb_softc *ntb, unsigned int idx,
-    void *data, ntb_db_callback func);
-void ntb_unregister_db_callback(struct ntb_softc *ntb, unsigned int idx);
-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_mw_count(struct ntb_softc *ntb);
+typedef void (*ntb_db_callback)(void *data, int vector);
+typedef void (*ntb_event_callback)(void *data);
+
+struct ntb_ctx_ops {
+       ntb_event_callback      link_event;
+       ntb_db_callback         db_event;
+};
+
+device_t ntb_get_device(struct ntb_softc *);
+
+bool ntb_link_is_up(struct ntb_softc *, enum ntb_speed *, enum ntb_width *);
+void ntb_link_event(struct ntb_softc *);
+int ntb_link_enable(struct ntb_softc *, enum ntb_speed, enum ntb_width);
+int ntb_link_disable(struct ntb_softc *);
+
+int ntb_set_ctx(struct ntb_softc *, void *, const struct ntb_ctx_ops *);
+void *ntb_get_ctx(struct ntb_softc *, const struct ntb_ctx_ops **);
+void ntb_clear_ctx(struct ntb_softc *);
+
+uint8_t ntb_mw_count(struct ntb_softc *);
+int ntb_mw_get_range(struct ntb_softc *, unsigned mw_idx, vm_paddr_t *base,
+    void **vbase, size_t *size, size_t *align, size_t *align_size);
+int ntb_mw_set_trans(struct ntb_softc *, unsigned mw_idx, bus_addr_t, size_t);
+int ntb_mw_clear_trans(struct ntb_softc *, unsigned mw_idx);
+
 uint8_t ntb_get_max_spads(struct ntb_softc *ntb);
 int ntb_spad_write(struct ntb_softc *ntb, unsigned int idx, uint32_t val);
 int ntb_spad_read(struct ntb_softc *ntb, unsigned int idx, uint32_t *val);
@@ -71,14 +88,16 @@ int ntb_peer_spad_write(struct ntb_softc *ntb, unsigned int idx,
     uint32_t val);
 int ntb_peer_spad_read(struct ntb_softc *ntb, unsigned int idx,
     uint32_t *val);
-void *ntb_get_mw_vbase(struct ntb_softc *ntb, unsigned int mw);
-bus_addr_t ntb_get_mw_pbase(struct ntb_softc *ntb, unsigned int mw);
-u_long ntb_get_mw_size(struct ntb_softc *ntb, unsigned int mw);
-void ntb_set_mw_addr(struct ntb_softc *ntb, unsigned int mw, uint64_t addr);
-void ntb_peer_db_set(struct ntb_softc *ntb, uint64_t bit);
-bus_addr_t ntb_get_peer_db_addr(struct ntb_softc *ntb, vm_size_t *sz_out);
-bool ntb_link_is_up(struct ntb_softc *ntb);
-device_t ntb_get_device(struct ntb_softc *ntb);
+
+uint64_t ntb_db_valid_mask(struct ntb_softc *);
+bus_addr_t ntb_get_peer_db_addr(struct ntb_softc *, vm_size_t *sz_out);
+
+void ntb_db_clear(struct ntb_softc *, uint64_t bits);
+void ntb_db_clear_mask(struct ntb_softc *, uint64_t bits);
+uint64_t ntb_db_read(struct ntb_softc *);
+void ntb_db_set_mask(struct ntb_softc *, uint64_t bits);
+uint64_t ntb_db_vector_mask(struct ntb_softc *, int vector);
+void ntb_peer_db_set(struct ntb_softc *, uint64_t bits);
 
 /* Hardware owns the low 32 bits of features. */
 #define NTB_BAR_SIZE_4K                (1 << 0)
index 2daf681fd918465552525762fd4668d52e6d127a..22d24227c44bbd424a13c08022003947972c640b 100644 (file)
@@ -33,6 +33,7 @@
 #define NTB_LINK_STATUS_ACTIVE 0x2000
 #define NTB_LINK_SPEED_MASK    0x000f
 #define NTB_LINK_WIDTH_MASK    0x03f0
+#define NTB_LNK_STA_WIDTH(sta) (((sta) & NTB_LINK_WIDTH_MASK) >> 4)
 
 #define XEON_SNB_MW_COUNT      2
 #define XEON_HSX_SPLIT_MW_COUNT        3