/* * Copyright (c) 2014, Linaro Limited * Copyright (c) 2014, Texas Instruments Incorporated * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include odp_queue_t *em_queue_map; static const char SHM_EMQUEUE_DEFAULT_NAME[] = "em_queue_map"; static const char odp_queue_table_name[] = "odp_queue_entreis"; shres_table_t *queue_tbl; void odp_queue_param_init(odp_queue_param_t *params) { memset(params, 0, sizeof(odp_queue_param_t)); } int odp_queue_info(odp_queue_t handle, odp_queue_info_t *info) { queue_entry_t *entry; if (odp_unlikely(info == NULL)) { ODP_ERR("Unable to store info, NULL ptr given\n"); return -1; } entry = _odp_queue_to_qentry(handle); shres_lock(entry); info->name = entry->name; info->type = entry->type; info->param = entry->param; shres_unlock(entry); return 0; } static int init_qmss_queue(queue_entry_t *entry) { uint8_t allocated = 0; entry->qmss_queue = Qmss_queueOpen( Qmss_QueueType_GENERAL_PURPOSE_QUEUE, QMSS_PARAM_NOT_SPECIFIED, &allocated); if (allocated) Qmss_queueEmpty(entry->qmss_queue); odp_pr_vdbg(">>>>>> queue_s: %p, qmss_queue: %d\n", entry, entry->qmss_queue); if (entry->qmss_queue < 0) return -1; /* Init scheduler tag to 0 for POLL queue */ entry->em_queue_sched_tag = 0; return 0; } static int init_sched_queue(queue_entry_t *entry) { ti_em_queue_id_t hw_queue_id; entry->em_queue = em_queue_create("em test queue", entry->param.sched.sync, entry->param.sched.prio, entry->param.sched.group); if (entry->em_queue == EM_QUEUE_UNDEF) { odp_pr_err("Failed to create a test queue\n"); return -1; } ODP_AS_STR(entry->em_queue < EM_MAX_QUEUES, "EM queue handler is wrong\n"); hw_queue_id = ti_em_queue_get_queue_id(entry->em_queue); if (hw_queue_id == 0xFFFF) { odp_pr_err("Failed to read EM HW queue ID\n"); em_queue_delete(entry->em_queue); return -1; } entry->qmss_queue = Qmss_getHandleFromQID(hw_queue_id); entry->em_queue_sched_tag = ti_em_queue_get_sd_tag(entry->em_queue); if (!entry->em_queue_sched_tag) { odp_pr_err("Failed to read EM scheduler tag\n"); em_queue_delete(entry->em_queue); return -1; } em_queue_map[entry->em_queue] = _odp_qentry_to_queue(entry); odp_pr_dbg("Created a queue: %x, qmss_queue: 0x%x, sd_tag: 0x%x, sync: %d, prio: %d, group: %d\n", entry->em_queue, entry->qmss_queue, entry->em_queue_sched_tag, entry->param.sched.sync, entry->param.sched.prio, entry->param.sched.group); return 0; } static int queue_sched_enq(queue_entry_t *entry, odp_event_t ev) { Cppi_HostDesc *desc = _odp_ev_to_cppi_desc(ev); odp_pr_dbg("queue: %s, event: %p, qmss_queue: 0x%x, em_queue: 0x%x\n", entry->name, ev, entry->qmss_queue, entry->em_queue); /* Insert queue's scheduler tag into the buffer */ desc->softwareInfo0 = entry->em_queue_sched_tag; Qmss_queuePushDescSize(entry->qmss_queue, desc, NWAL_DESC_SIZE); return 0; } static int queue_sched_enq_multi(queue_entry_t *entry, const odp_event_t ev[], int num) { int i; for (i = 0; i < num; i++) if (queue_sched_enq(entry, ev[i])) return -1; return 0; } static int queue_init(queue_entry_t *entry, const char *name, odp_queue_type_t type, odp_queue_param_t *param) { strncpy(entry->name, name, ODP_QUEUE_NAME_LEN - 1); entry->type = type; if (param) { entry->param = *param; } else { /* Defaults */ if (type == ODP_QUEUE_TYPE_SCHED) { entry->param.sched.prio = ODP_SCHED_PRIO_DEFAULT; entry->param.sched.sync = ODP_SCHED_SYNC_ATOMIC; entry->param.sched.group = ODP_SCHED_GROUP_ALL; } else { entry->param.sched.sync = ODP_SCHED_SYNC_UNDEF; } entry->param.context = NULL; } entry->enqueue = NULL; entry->dequeue = NULL; entry->enqueue_multi = NULL; entry->dequeue_multi = NULL; switch (type) { case ODP_QUEUE_TYPE_PKTIN: /** * Currently there is no way in API to distinguish whether * PKTIN queue is a scheduler queue or poll queue. * If sched.sync type is not specified directly assume it is * a scheduler queue. */ if (entry->param.sched.sync == ODP_SCHED_SYNC_UNDEF) { if (init_qmss_queue(entry)) return -1; entry->dequeue = pktin_dequeue; entry->dequeue_multi = pktin_deq_multi; } else { if (init_sched_queue(entry)) return -1; } break; case ODP_QUEUE_TYPE_PKTOUT: entry->enqueue = pktout_enqueue; entry->enqueue_multi = pktout_enq_multi; break; case ODP_QUEUE_TYPE_POLL: if (init_qmss_queue(entry)) return -1; entry->enqueue = queue_poll_enq; entry->dequeue = queue_poll_deq; entry->enqueue_multi = queue_poll_enq_multi; entry->dequeue_multi = queue_poll_deq_multi; break; case ODP_QUEUE_TYPE_SCHED: if (init_sched_queue(entry)) return -1; entry->enqueue = queue_sched_enq; entry->enqueue_multi = queue_sched_enq_multi; break; default: ODP_ERR("Unknown queue type: %d\n", type); break; } return 0; } static int queue_free_cb(void *data) { queue_entry_t *entry = data; if (entry->type == ODP_QUEUE_TYPE_SCHED) { em_status_t status; status = em_queue_delete(entry->em_queue); if (status == EM_OK) return 0; return -1; } if ((entry->type == ODP_QUEUE_TYPE_PKTIN && entry->param.sched.sync == ODP_SCHED_SYNC_UNDEF) || entry->type == ODP_QUEUE_TYPE_POLL) { uint32_t count = Qmss_getQueueEntryCount(entry->qmss_queue); if (count) { ODP_ERR("Queue still has %d events\n", count); shres_unlock(entry); return -1; } Qmss_Result qmss_ret = Qmss_queueClose(entry->qmss_queue); if (qmss_ret != QMSS_SOK) { ODP_ERR("QMSS queue close failed: %d\n", qmss_ret); shres_unlock(entry); return -1; } } return 0; } int odp_queue_init_global(void) { odp_shm_t shm; odp_pr_dbg("Queue init ... "); queue_tbl = shres_table_create(odp_queue_table_name, queue_entry_t, ODP_CONFIG_QUEUES, queue_free_cb); if (queue_tbl == SHRES_TABLE_INVALID) return -1; shm = odp_shm_reserve(SHM_EMQUEUE_DEFAULT_NAME, sizeof(odp_queue_t) * EM_MAX_QUEUES, sizeof(odp_queue_t), 0); em_queue_map = odp_shm_addr(shm); if (em_queue_map == NULL) return -1; for (int i = 0; i < EM_MAX_QUEUES; i++) em_queue_map[i] = ODP_QUEUE_INVALID; odp_pr_dbg("done\n"); odp_pr_dbg("Queue init global\n"); odp_pr_dbg(" queue_entry_t size %zu\n", sizeof(queue_entry_t)); odp_pr_dbg("\n"); return 0; } int odp_queue_term_global(void) { int ret; ret = odp_shm_free(odp_shm_lookup(SHM_EMQUEUE_DEFAULT_NAME)); if (ret < 0) { ODP_ERR("shm free failed for %s\n", SHM_EMQUEUE_DEFAULT_NAME); } if (shres_table_destroy(odp_queue_table_name) < 0) ret = -1; return ret; } int odp_queue_destroy(odp_queue_t queue) { if (queue == ODP_QUEUE_INVALID) { ODP_ERR("Invalid input parameters\n"); return -1; } queue_entry_t *entry = _odp_queue_to_qentry(queue); if (shres_free(queue_tbl, entry) < 0) return -1; shres_lock(entry); if (shres_is_allocated(entry)) { shres_unlock(entry); ODP_ERR("Queue in use cannot be destroyed right now. It will be destroyed later\n"); return -1; } shres_unlock(entry); return 0; } odp_queue_t odp_queue_create(const char *name, odp_queue_type_t type, odp_queue_param_t *param) { queue_entry_t *entry; odp_queue_t queue = ODP_QUEUE_INVALID; odp_pr_vdbg(">>>>>> name: %s, type: %d\n", name, type); entry = shres_alloc(queue_tbl, typeof(*entry)); shres_lock(entry); if (queue_init(entry, name, type, param) < 0) return ODP_QUEUE_INVALID; shres_unlock(entry); queue = _odp_qentry_to_queue(entry); odp_pr_vdbg(">>>>>> handle: %u\n", queue); return queue; } odp_queue_t odp_queue_lookup(const char *name) { queue_entry_t *entry; shres_table_for_each_allocated_entry(queue_tbl, entry) { shres_lock(entry); if (!shres_is_allocated(entry)) { shres_unlock(entry); continue; } shres_unlock(entry); /* * name is assigned only while creation, so can be changed only * while deletion. But anyway, we doesn't get it here, so the * queue can be deleted in any time */ if (strcmp(name, entry->name) == 0) return _odp_qentry_to_queue(entry); } return ODP_QUEUE_INVALID; } int odp_queue_lock_count(odp_queue_t handle ODP_UNUSED) { ODP_UNIMPLEMENTED(); return ODP_CONFIG_MAX_ORDERED_LOCKS_PER_QUEUE; } int queue_poll_enq(queue_entry_t *entry, odp_event_t ev) { odp_pr_vdbg("queue: %s, buf: %p, qmss_queue: %d\n", entry->name, ev, entry->qmss_queue); Qmss_queuePushDescSize(entry->qmss_queue, _odp_ev_to_cppi_desc(ev), NWAL_DESC_SIZE); return 0; } int queue_poll_enq_multi(queue_entry_t *entry, const odp_event_t ev[], int num) { int i; for (i = 0; i < num; i++) { /** @todo: Implement multi dequeue a lower level */ odp_pr_vdbg("queue: %s, buf: %p, qmss_queue: %d\n", entry->name, ev[i], entry->qmss_queue); Qmss_queuePushDescSize(entry->qmss_queue, _odp_ev_to_cppi_desc(ev[i]), NWAL_DESC_SIZE); } return num; } odp_event_t queue_poll_deq(queue_entry_t *entry) { Cppi_HostDesc *desc; desc = (void *)QMSS_DESC_PTR(Qmss_queuePop(entry->qmss_queue)); odp_pr_vdbg("queue: %s, buf: %p, qmss_queue: %d\n", entry->name, desc, entry->qmss_queue); return _cppi_desc_to_odp_ev(desc); } int queue_poll_deq_multi(queue_entry_t *entry, odp_event_t ev[], int num) { int i; for (i = 0; i < num; i++) { Cppi_HostDesc *desc; /** @todo: Implement multi dequeue a lower level */ desc = Qmss_queuePop(entry->qmss_queue); desc = (void *)QMSS_DESC_PTR(desc); ev[i] = _cppi_desc_to_odp_ev(desc); if (!ev[i]) break; } return i; } void queue_lock(queue_entry_t *entry) { shres_lock(entry); } void queue_unlock(queue_entry_t *entry) { shres_unlock(entry); } int _queue_get(odp_queue_t queue) { queue_entry_t *entry = _odp_queue_to_qentry(queue); return shres_get(queue_tbl, entry); } void _queue_put(odp_queue_t queue) { queue_entry_t *entry = _odp_queue_to_qentry(queue); shres_put(queue_tbl, entry); }