@@ -786,6 +786,29 @@ int dlb_string_to_int(int *result, const char *str)
return qm_qid;
}
+static int32_t
+dlb_hw_create_dir_queue(struct dlb_eventdev *dlb, int32_t qm_port_id)
+{
+ struct dlb_hw_dev *handle = &dlb->qm_instance;
+ struct dlb_create_dir_queue_args cfg;
+ struct dlb_cmd_response response;
+ int32_t ret;
+
+ cfg.response = (uintptr_t)&response;
+
+ /* The directed port is always configured before its queue */
+ cfg.port_id = qm_port_id;
+
+ ret = dlb_iface_dir_queue_create(handle, &cfg);
+ if (ret < 0) {
+ DLB_LOG_ERR("dlb: create DIR event queue error, ret=%d (driver status: %s)\n",
+ ret, dlb_error_strings[response.status]);
+ return -EINVAL;
+ }
+
+ return response.id;
+}
+
static inline void
dlb_hw_do_enqueue(struct dlb_port *qm_port,
bool do_sfence,
@@ -803,6 +826,42 @@ int dlb_string_to_int(int *result, const char *str)
dlb_pp_write(qm_port->qe4, port_data);
}
+static int16_t
+dlb_hw_map_ldb_qid_to_port(struct dlb_hw_dev *handle,
+ uint32_t qm_port_id,
+ uint16_t qm_qid,
+ uint8_t priority)
+{
+ struct dlb_map_qid_args cfg;
+ struct dlb_cmd_response response;
+ int32_t ret;
+
+ if (handle == NULL)
+ return -EINVAL;
+
+ /* Build message */
+ cfg.response = (uintptr_t)&response;
+ cfg.port_id = qm_port_id;
+ cfg.qid = qm_qid;
+ cfg.priority = EV_TO_DLB_PRIO(priority);
+
+ ret = dlb_iface_map_qid(handle, &cfg);
+ if (ret < 0) {
+ DLB_LOG_ERR("dlb: map qid error, ret=%d (driver status: %s)\n",
+ ret, dlb_error_strings[response.status]);
+ DLB_LOG_ERR("dlb: device_id=%d grp=%d, qm_port=%d, qm_qid=%d prio=%d\n",
+ handle->device_id,
+ handle->domain_id, cfg.port_id,
+ cfg.qid,
+ cfg.priority);
+ } else {
+ DLB_LOG_DBG("dlb: mapped queue %d to qm_port %d\n",
+ qm_qid, qm_port_id);
+ }
+
+ return ret;
+}
+
/* VDEV-only notes:
* This function first unmaps all memory mappings and closes the
* domain's file descriptor, which causes the driver to reset the
@@ -1784,6 +1843,249 @@ int dlb_string_to_int(int *result, const char *str)
return 0;
}
+static int
+dlb_event_queue_join_ldb(struct dlb_eventdev *dlb,
+ struct dlb_eventdev_port *ev_port,
+ struct dlb_eventdev_queue *ev_queue,
+ uint8_t priority)
+{
+ int first_avail = -1;
+ int ret, i;
+
+ for (i = 0; i < DLB_MAX_NUM_QIDS_PER_LDB_CQ; i++) {
+ if (ev_port->link[i].valid) {
+ if (ev_port->link[i].queue_id == ev_queue->id &&
+ ev_port->link[i].priority == priority) {
+ if (ev_port->link[i].mapped)
+ return 0; /* already mapped */
+ first_avail = i;
+ }
+ } else {
+ if (first_avail == -1)
+ first_avail = i;
+ }
+ }
+ if (first_avail == -1) {
+ DLB_LOG_ERR("dlb: qm_port %d has no available QID slots.\n",
+ ev_port->qm_port.id);
+ return -EINVAL;
+ }
+
+ ret = dlb_hw_map_ldb_qid_to_port(&dlb->qm_instance,
+ ev_port->qm_port.id,
+ ev_queue->qm_queue.id,
+ priority);
+
+ if (!ret)
+ ev_port->link[first_avail].mapped = true;
+
+ return ret;
+}
+
+static int
+dlb_eventdev_dir_queue_setup(struct dlb_eventdev *dlb,
+ struct dlb_eventdev_queue *ev_queue,
+ struct dlb_eventdev_port *ev_port)
+{
+ int32_t qm_qid;
+
+ qm_qid = dlb_hw_create_dir_queue(dlb, ev_port->qm_port.id);
+
+ if (qm_qid < 0) {
+ DLB_LOG_ERR("Failed to create the DIR queue\n");
+ return qm_qid;
+ }
+
+ dlb->qm_dir_to_ev_queue_id[qm_qid] = ev_queue->id;
+
+ ev_queue->qm_queue.id = qm_qid;
+
+ return 0;
+}
+
+static int
+dlb_do_port_link(struct rte_eventdev *dev,
+ struct dlb_eventdev_queue *ev_queue,
+ struct dlb_eventdev_port *ev_port,
+ uint8_t prio)
+{
+ struct dlb_eventdev *dlb = dlb_pmd_priv(dev);
+ int err;
+
+ /* Don't link until start time. */
+ if (dlb->run_state == DLB_RUN_STATE_STOPPED)
+ return 0;
+
+ if (ev_queue->qm_queue.is_directed)
+ err = dlb_eventdev_dir_queue_setup(dlb, ev_queue, ev_port);
+ else
+ err = dlb_event_queue_join_ldb(dlb, ev_port, ev_queue, prio);
+
+ if (err) {
+ DLB_LOG_ERR("port link failure for %s ev_q %d, ev_port %d\n",
+ ev_queue->qm_queue.is_directed ? "DIR" : "LDB",
+ ev_queue->id, ev_port->id);
+
+ rte_errno = err;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+dlb_validate_port_link(struct dlb_eventdev_port *ev_port,
+ uint8_t queue_id,
+ bool link_exists,
+ int index)
+{
+ struct dlb_eventdev *dlb = ev_port->dlb;
+ struct dlb_eventdev_queue *ev_queue;
+ bool port_is_dir, queue_is_dir;
+
+ if (queue_id > dlb->num_queues) {
+ rte_errno = -EINVAL;
+ return -1;
+ }
+
+ ev_queue = &dlb->ev_queues[queue_id];
+
+ if (!ev_queue->setup_done &&
+ ev_queue->qm_queue.config_state != DLB_PREV_CONFIGURED) {
+ rte_errno = -EINVAL;
+ return -1;
+ }
+
+ port_is_dir = ev_port->qm_port.is_directed;
+ queue_is_dir = ev_queue->qm_queue.is_directed;
+
+ if (port_is_dir != queue_is_dir) {
+ DLB_LOG_ERR("%s queue %u can't link to %s port %u\n",
+ queue_is_dir ? "DIR" : "LDB", ev_queue->id,
+ port_is_dir ? "DIR" : "LDB", ev_port->id);
+
+ rte_errno = -EINVAL;
+ return -1;
+ }
+
+ /* Check if there is space for the requested link */
+ if (!link_exists && index == -1) {
+ DLB_LOG_ERR("no space for new link\n");
+ rte_errno = -ENOSPC;
+ return -1;
+ }
+
+ /* Check if the directed port is already linked */
+ if (ev_port->qm_port.is_directed && ev_port->num_links > 0 &&
+ !link_exists) {
+ DLB_LOG_ERR("Can't link DIR port %d to >1 queues\n",
+ ev_port->id);
+ rte_errno = -EINVAL;
+ return -1;
+ }
+
+ /* Check if the directed queue is already linked */
+ if (ev_queue->qm_queue.is_directed && ev_queue->num_links > 0 &&
+ !link_exists) {
+ DLB_LOG_ERR("Can't link DIR queue %d to >1 ports\n",
+ ev_queue->id);
+ rte_errno = -EINVAL;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+dlb_eventdev_port_link(struct rte_eventdev *dev, void *event_port,
+ const uint8_t queues[], const uint8_t priorities[],
+ uint16_t nb_links)
+
+{
+ struct dlb_eventdev_port *ev_port = event_port;
+ struct dlb_eventdev *dlb;
+ int i, j;
+
+ RTE_SET_USED(dev);
+
+ if (!ev_port) {
+ DLB_LOG_ERR("dlb: evport not setup\n");
+ rte_errno = -EINVAL;
+ return 0;
+ }
+
+ if (!ev_port->setup_done &&
+ ev_port->qm_port.config_state != DLB_PREV_CONFIGURED) {
+ DLB_LOG_ERR("dlb: evport not setup\n");
+ rte_errno = -EINVAL;
+ return 0;
+ }
+
+ /* Note: rte_event_port_link() ensures the PMD won't receive a NULL
+ * queues pointer.
+ */
+ if (nb_links == 0) {
+ DLB_LOG_DBG("dlb: nb_links is 0\n");
+ return 0; /* Ignore and return success */
+ }
+
+ dlb = ev_port->dlb;
+
+ DLB_LOG_DBG("Linking %u queues to %s port %d\n",
+ nb_links,
+ ev_port->qm_port.is_directed ? "DIR" : "LDB",
+ ev_port->id);
+
+ for (i = 0; i < nb_links; i++) {
+ struct dlb_eventdev_queue *ev_queue;
+ uint8_t queue_id, prio;
+ bool found = false;
+ int index = -1;
+
+ queue_id = queues[i];
+ prio = priorities[i];
+
+ /* Check if the link already exists. */
+ for (j = 0; j < DLB_MAX_NUM_QIDS_PER_LDB_CQ; j++)
+ if (ev_port->link[j].valid) {
+ if (ev_port->link[j].queue_id == queue_id) {
+ found = true;
+ index = j;
+ break;
+ }
+ } else {
+ if (index == -1)
+ index = j;
+ }
+
+ /* could not link */
+ if (index == -1)
+ break;
+
+ /* Check if already linked at the requested priority */
+ if (found && ev_port->link[j].priority == prio)
+ continue;
+
+ if (dlb_validate_port_link(ev_port, queue_id, found, index))
+ break; /* return index of offending queue */
+
+ ev_queue = &dlb->ev_queues[queue_id];
+
+ if (dlb_do_port_link(dev, ev_queue, ev_port, prio))
+ break; /* return index of offending queue */
+
+ ev_queue->num_links++;
+
+ ev_port->link[index].queue_id = queue_id;
+ ev_port->link[index].priority = prio;
+ ev_port->link[index].valid = true;
+ /* Entry already exists? If so, then must be prio change */
+ if (!found)
+ ev_port->num_links++;
+ }
+ return i;
+}
+
static void
dlb_eventdev_port_default_conf_get(struct rte_eventdev *dev,
uint8_t port_id,
@@ -2254,6 +2556,7 @@ static int dlb_num_dir_queues_setup(struct dlb_eventdev *dlb)
.queue_setup = dlb_eventdev_queue_setup,
.port_def_conf = dlb_eventdev_port_default_conf_get,
.port_setup = dlb_eventdev_port_setup,
+ .port_link = dlb_eventdev_port_link,
};
/* Expose PMD's eventdev interface */