/** * ntb_transport_create_queue - Create a new NTB transport layer queue * @rx_handler: receive callback function * @tx_handler: transmit callback function * @event_handler: event callback function * * Create a new NTB transport layer queue and provide the queue with a callback * routine for both transmit and receive. The receive callback routine will be * used to pass up data when the transport has received it on the queue. The * transmit callback routine will be called when the transport has completed the * transmission of the data on the queue and the data is ready to be freed. * * 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, const struct ntb_queue_handlers *handlers) { struct ntb_queue_entry *entry; struct ntb_transport_qp *qp; struct ntb_netdev *nt; unsigned int free_queue; int rc, i; nt = ntb_find_transport(pdev); if (nt == NULL) goto err; free_queue = ffs(nt->qp_bitmap); if (free_queue == 0) goto err; /* decrement free_queue to make it zero based */ free_queue--; clear_bit(free_queue, &nt->qp_bitmap); qp = &nt->qps[free_queue]; 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->cb_data = nt->ifp; entry->buf = NULL; entry->len = transport_mtu; ntb_list_add(&qp->ntb_rx_pend_q_lock, entry, &qp->rx_pend_q); } for (i = 0; i < NTB_QP_DEF_NUM_ENTRIES; i++) { entry = malloc(sizeof(struct ntb_queue_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; 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); }
static void ntb_transport_free_queue(struct ntb_transport_qp *qp) { struct ntb_queue_entry *entry; if (qp == NULL) return; callout_drain(&qp->link_work); 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_q_lock, &qp->rx_pend_q))) free(entry, M_NTB_IF); while ((entry = ntb_list_rm(&qp->ntb_rx_q_lock, &qp->rx_post_q))) free(entry, M_NTB_IF); 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_free); }
static void ntb_rx_completion_task(void *arg, int pending) { struct ntb_transport_qp *qp = arg; struct mbuf *m; struct ntb_queue_entry *entry; CTR0(KTR_NTB, "RX: rx_completion_task"); while ((entry = ntb_list_rm(&qp->ntb_rx_free_q_lock, &qp->rx_free_q))) { m = entry->buf; CTR2(KTR_NTB, "RX: completing entry %p, mbuf %p", entry, m); if (qp->rx_handler && qp->client_ready == NTB_LINK_UP) qp->rx_handler(qp, qp->cb_data, m, entry->len); entry->buf = NULL; entry->len = qp->transport->bufsize; CTR1(KTR_NTB,"RX: entry %p removed from rx_free_q " "and added to rx_pend_q", entry); ntb_list_add(&qp->ntb_rx_pend_q_lock, entry, &qp->rx_pend_q); if (qp->rx_err_no_buf > qp->last_rx_no_buf) { qp->last_rx_no_buf = qp->rx_err_no_buf; CTR0(KTR_NTB, "RX: could spawn rx task"); callout_reset(&qp->rx_full, hz / 1000, ntb_rx_pendq_full, qp); } } }
/** * ntb_transport_tx_enqueue - Enqueue a new NTB queue entry * @qp: NTB transport layer queue the entry is to be enqueued on * @cb: per buffer pointer for callback function to use * @data: pointer to data buffer that will be sent * @len: length of the data buffer * * Enqueue a new transmit buffer onto the transport queue from which a NTB * payload will be transmitted. This assumes that a lock is behing held to * serialize access to the qp. * * RETURNS: An appropriate ERRNO error value on error, or zero for success. */ static int ntb_transport_tx_enqueue(struct ntb_transport_qp *qp, void *cb, void *data, unsigned int len) { struct ntb_queue_entry *entry; int rc; if (qp == NULL || qp->qp_link != NTB_LINK_UP || len == 0) { CTR0(KTR_NTB, "TX: link not up"); return (EINVAL); } entry = ntb_list_rm(&qp->ntb_tx_free_q_lock, &qp->tx_free_q); if (entry == NULL) { CTR0(KTR_NTB, "TX: could not get entry from tx_free_q"); return (ENOMEM); } CTR1(KTR_NTB, "TX: got entry %p from tx_free_q", entry); entry->cb_data = cb; entry->buf = data; entry->len = len; entry->flags = 0; rc = ntb_process_tx(qp, entry); if (rc != 0) { ntb_list_add(&qp->ntb_tx_free_q_lock, entry, &qp->tx_free_q); CTR1(KTR_NTB, "TX: process_tx failed. Returning entry %p to tx_free_q", entry); } return (rc); }
static void ntb_send_link_down(struct ntb_transport_qp *qp) { struct ntb_queue_entry *entry; int i, rc; if (qp->qp_link == NTB_LINK_DOWN) return; qp->qp_link = NTB_LINK_DOWN; for (i = 0; i < NTB_LINK_DOWN_TIMEOUT; i++) { entry = ntb_list_rm(&qp->ntb_tx_free_q_lock, &qp->tx_free_q); if (entry != NULL) break; pause("NTB Wait for link down", hz / 10); } if (entry == NULL) return; entry->cb_data = NULL; entry->buf = NULL; entry->len = 0; entry->flags = IF_NTB_LINK_DOWN_FLAG; mtx_lock(&qp->transport->tx_lock); rc = ntb_process_tx(qp, entry); if (rc != 0) printf("ntb: Failed to send link down\n"); mtx_unlock(&qp->transport->tx_lock); }
static void ntb_transport_free_queue(struct ntb_transport_qp *qp) { struct ntb_queue_entry *entry; if (qp == NULL) return; callout_drain(&qp->link_work); ntb_unregister_db_callback(qp->ntb, qp->qp_num); while ((entry = ntb_list_rm(&qp->ntb_rx_free_q_lock, &qp->rx_free_q))) free(entry, M_NTB_IF); while ((entry = ntb_list_rm(&qp->ntb_rx_pend_q_lock, &qp->rx_pend_q))) free(entry, M_NTB_IF); 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); }
static int ntb_process_rxc(struct ntb_transport_qp *qp) { struct ntb_payload_header *hdr; struct ntb_queue_entry *entry; void *offset; offset = (void *) ((char *)qp->rx_buff + qp->rx_max_frame * qp->rx_index); hdr = (void *) ((char *)offset + qp->rx_max_frame - 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); 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); 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; } 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); qp->rx_err_oflow++; } qp->rx_bytes += hdr->len; qp->rx_pkts++; CTR1(KTR_NTB, "RX: received %ld rx_pkts", 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; qp->rx_index++; qp->rx_index %= qp->rx_max_entry; return (0); }