/* * Copyright (c) 2016-2017 Intel Corporation. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TEST_TLE_DRING_H_ #define TEST_TLE_DRING_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct dring_arg { struct tle_dring *dr; struct rte_ring *r; uint32_t iter; int32_t enq_type; int32_t deq_type; uint32_t enq; uint32_t deq; }; class dring: public ::testing::Test { protected: virtual void SetUp(void) {}; virtual void TearDown(void) {}; int32_t rc; struct rte_ring *r; struct tle_dring dr; struct dring_arg arg[RTE_MAX_LCORE]; }; #define OBJ_NUM UINT16_MAX #define ITER_NUM (4 * OBJ_NUM) enum { NONE, SINGLE, MULTI, }; /* * Each enqueued object will contain: * [2-3]B: it's own sequence number. * [0-1]B: next object sequence number, or UINT16_MAX. */ static void test_fill_obj(uintptr_t obj[], uint32_t num) { uint32_t i; for (i = 0; i != num - 1; i++) obj[i] = i << 16 | (i + 1); obj[i] = i << 16 | UINT16_MAX; } static uint32_t test_check_obj(uintptr_t obj[], uint32_t num) { uint32_t i, h, l, oh, ol; h = obj[0] >> 16; l = obj[0] & UINT16_MAX; if (h + 1 != l && l != UINT16_MAX) return 0; if (l == UINT16_MAX) l = 0; for (i = 1; i != num; i++) { oh = obj[i] >> 16; ol = obj[i] & UINT16_MAX; if (l != oh || (oh + 1 != ol && ol != UINT16_MAX)) return i; l = ol; if (l == UINT16_MAX) l = 0; } return num; } /* * free memory allocated for drbs and for the ring itself. */ static void fini_drb_ring(struct rte_ring *r) { struct tle_drb *drb; /* free drbs. */ while (rte_ring_dequeue(r, (void **)&drb) == 0) free(drb); /* free ring. */ free(r); } static struct rte_ring * init_drb_ring(uint32_t num) { uint32_t i, k, n; size_t sz, tsz; struct rte_ring *r; struct tle_drb *drb; /* allocate and initialise rte_ring. */ n = rte_align32pow2(num); sz = rte_ring_get_memsize(n); r = (struct rte_ring *)calloc(1, sz); if (r == NULL) { printf("%s:%d(%u) failed to allocate %zu bytes;\n", __func__, __LINE__, num, sz); return NULL; } rte_ring_init(r, __func__, n, 0); /* allocate drbs and put them into the ring. */ tsz = sz; for (i = 0; i != num; i += k) { k = rte_rand() % (UINT8_MAX + 1) + 1; k = RTE_MIN(k, num - i); sz = tle_drb_calc_size(k); drb = (struct tle_drb *)calloc(1, sz); if (drb == NULL) { printf("%s:%d(%u) %u-th iteration: " "failed to allocate %zu bytes;\n", __func__, __LINE__, num, i, sz); fini_drb_ring(r); return NULL; } drb->size = k; rte_ring_enqueue(r, drb); tsz += sz; } printf("%s(%u) total %zu bytes allocated, number of drbs: %u;\n", __func__, num, tsz, rte_ring_count(r)); return r; } static int test_dring_enqueue(struct tle_dring *dr, struct rte_ring *r, uint32_t num, int32_t type) { uint32_t i, j, k, lc, nb; struct tle_drb *drb[num]; uintptr_t obj[num]; lc = rte_lcore_id(); /* prepare drbs to enqueue up to *num* objects. */ for (i = 0, j = 0; i != num; i += k, j++) { if (rte_ring_dequeue(r, (void **)&drb[j]) != 0) break; /* udata value for unused drb should be zero. */ if (drb[j]->udata != NULL) { printf("error @ %s:%d(%p, %u) at lcore %u: " "erroneous drb@%p={udata=%p, size=%u,};\n", __func__, __LINE__, dr, num, lc, drb[j], drb[j]->udata, drb[j]->size); return -EFAULT; } /* update udata value with current lcore id. */ drb[j]->udata = (void *)(uintptr_t)(lc + 1); k = drb[j]->size; k = RTE_MIN(k, num - i); } /* no free drbs left. */ if (i == 0) return 0; /* fill objects to enqueue. */ test_fill_obj(obj, i); /* enqueue into the dring. */ nb = j; if (type == SINGLE) k = tle_dring_sp_enqueue(dr, (const void **)obj, i, drb, &nb); else if (type == MULTI) k = tle_dring_mp_enqueue(dr, (const void **)obj, i, drb, &nb); else return -EINVAL; if (k != i) { printf("%s:%d(%p, %p, %u): failed to enqueue %u objects;\n", __func__, __LINE__, dr, r, num, i); } /* free unused drbs */ for (i = j - nb; i != j; i++) { if ((uintptr_t)drb[i]->udata != lc + 1) { printf("error @ %s:%d(%p, %u) at lcore %u: " "erroneous drb@%p={udata=%p, size=%u,};\n", __func__, __LINE__, dr, num, lc, drb[i], drb[i]->udata, drb[i]->size); return -EFAULT; } drb[i]->udata = NULL; rte_ring_enqueue(r, drb[i]); } return k; } static int test_dring_dequeue(struct tle_dring *dr, struct rte_ring *r, uint32_t num, int32_t type) { uint32_t i, k, lc, n, t; struct tle_drb *drb[num]; uintptr_t obj[num]; lc = rte_lcore_id(); k = num; /* dequeue objects. */ if (type == SINGLE) n = tle_dring_sc_dequeue(dr, (const void **)obj, num, drb, &k); else if (type == MULTI) n = tle_dring_mc_dequeue(dr, (const void **)obj, num, drb, &k); else return -EINVAL; if (n == 0) return 0; /* check the data returned. */ t = test_check_obj(obj, n); if (t != n) { printf("%s:%d(%p, %u) at lcore %u: invalid dequeued object, " "n=%u, idx=%u, obj=%#x, prev obj=%#x;\n", __func__, __LINE__, dr, num, lc, n, t, (uint32_t)obj[t], (t == 0) ? 0 : (uint32_t)obj[t - 1]); return -EFAULT; } /* check and free drbs. */ for (i = 0; i != k; i++) { /* udata value for drb in use shouldn't be zero. */ if (drb[i]->udata == NULL) { printf("error @ %s:%d(%p, %u) at lcore %u: " "erroneous drb@%p={udata=%p, size=%u,};\n", __func__, __LINE__, dr, num, lc, drb[i], drb[i]->udata, drb[i]->size); return -EFAULT; } drb[i]->udata = NULL; rte_ring_enqueue(r, drb[i]); } return n; } static int test_dring_enq_deq(struct dring_arg *arg) { int32_t rc; uint32_t i, lc, n; rc = 0; arg->enq = 0; arg->deq = 0; lc = rte_lcore_id(); for (i = 0; i != arg->iter; i++) { /* try to enqueue random number of objects. */ if (arg->enq_type != NONE) { n = rte_rand() % (UINT8_MAX + 1); rc = test_dring_enqueue(arg->dr, arg->r, n, arg->enq_type); if (rc < 0) break; arg->enq += rc; } /* try to dequeue random number of objects. */ if (arg->deq_type != NONE) { n = rte_rand() % (UINT8_MAX + 1); rc = test_dring_dequeue(arg->dr, arg->r, n, arg->deq_type); if (rc < 0) break; arg->deq += rc; } } if (rc < 0) return rc; /* dequeue remaining objects. */ while (arg->deq_type != NONE && arg->enq != arg->deq) { /* try to dequeue random number of objects. */ n = rte_rand() % (UINT8_MAX + 1) + 1; rc = test_dring_dequeue(arg->dr, arg->r, n, arg->deq_type); if (rc <= 0) break; arg->deq += rc; } printf("%s:%d(lcore=%u, enq_type=%d, deq_type=%d): " "%u objects enqueued, %u objects dequeued\n", __func__, __LINE__, lc, arg->enq_type, arg->deq_type, arg->enq, arg->deq); return 0; } static int test_dring_worker(void *arg) { struct dring_arg *p; p = (struct dring_arg *)arg; return test_dring_enq_deq(p); } /* * enqueue/dequeue by multiple threads. */ static int test_dring_mt(int32_t master_enq_type, int32_t master_deq_type, int32_t slave_enq_type, int32_t slave_deq_type) { int32_t rc; uint32_t lc; uint64_t deq, enq; struct rte_ring *r; struct tle_dring dr; struct dring_arg arg[RTE_MAX_LCORE]; tle_dring_reset(&dr, 0); r = init_drb_ring(OBJ_NUM); if (r == NULL) return -ENOMEM; memset(arg, 0, sizeof(arg)); /* launch on all slaves */ RTE_LCORE_FOREACH_SLAVE(lc) { arg[lc].dr = &dr; arg[lc].r = r; arg[lc].iter = ITER_NUM; arg[lc].enq_type = slave_enq_type; arg[lc].deq_type = slave_deq_type; rte_eal_remote_launch(test_dring_worker, &arg[lc], lc); } /* launch on master */ lc = rte_lcore_id(); arg[lc].dr = &dr; arg[lc].r = r; arg[lc].iter = ITER_NUM; arg[lc].enq_type = master_enq_type; arg[lc].deq_type = master_deq_type; rc = test_dring_worker(&arg[lc]); enq = arg[lc].enq; deq = arg[lc].deq; /* wait for slaves. */ RTE_LCORE_FOREACH_SLAVE(lc) { rc |= rte_eal_wait_lcore(lc); enq += arg[lc].enq; deq += arg[lc].deq; } printf("%s:%d: total %" PRIu64 " objects enqueued, %" PRIu64 " objects dequeued\n", __func__, __LINE__, enq, deq); rc = (rc != 0) ? rc : (enq != deq); if (rc != 0) tle_dring_dump(stdout, 1, &dr); fini_drb_ring(r); return rc; } #endif /* TEST_TLE_DRING_H_ */