mempool: cache optimisations
[dpdk.git] / lib / librte_mempool / rte_mempool.c
1 /*-
2  *   BSD LICENSE
3  * 
4  *   Copyright(c) 2010-2012 Intel Corporation. All rights reserved.
5  *   All rights reserved.
6  * 
7  *   Redistribution and use in source and binary forms, with or without 
8  *   modification, are permitted provided that the following conditions 
9  *   are met:
10  * 
11  *     * Redistributions of source code must retain the above copyright 
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright 
14  *       notice, this list of conditions and the following disclaimer in 
15  *       the documentation and/or other materials provided with the 
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its 
18  *       contributors may be used to endorse or promote products derived 
19  *       from this software without specific prior written permission.
20  * 
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  * 
33  */
34
35 #include <stdio.h>
36 #include <string.h>
37 #include <stdint.h>
38 #include <stdarg.h>
39 #include <inttypes.h>
40 #include <errno.h>
41 #include <sys/queue.h>
42
43 #include <rte_common.h>
44 #include <rte_log.h>
45 #include <rte_debug.h>
46 #include <rte_memory.h>
47 #include <rte_memzone.h>
48 #include <rte_atomic.h>
49 #include <rte_launch.h>
50 #include <rte_tailq.h>
51 #include <rte_eal.h>
52 #include <rte_eal_memconfig.h>
53 #include <rte_per_lcore.h>
54 #include <rte_lcore.h>
55 #include <rte_branch_prediction.h>
56 #include <rte_ring.h>
57 #include <rte_errno.h>
58 #include <rte_string_fns.h>
59
60 #include "rte_mempool.h"
61
62 TAILQ_HEAD(rte_mempool_list, rte_mempool);
63
64 #define CACHE_FLUSHTHRESH_MULTIPLIER 1.5
65
66 /*
67  * return the greatest common divisor between a and b (fast algorithm)
68  *
69  */
70 static unsigned get_gcd(unsigned a, unsigned b)
71 {
72         unsigned c;
73
74         if (0 == a)
75                 return b;
76         if (0 == b)
77                 return a;
78
79         if (a < b) {
80                 c = a;
81                 a = b;
82                 b = c;
83         }
84
85         while (b != 0) {
86                 c = a % b;
87                 a = b;
88                 b = c;
89         }
90
91         return a;
92 }
93
94 /*
95  * Depending on memory configuration, objects addresses are spreaded
96  * between channels and ranks in RAM: the pool allocator will add
97  * padding between objects. This function return the new size of the
98  * object.
99  */
100 static unsigned optimize_object_size(unsigned obj_size)
101 {
102         unsigned nrank, nchan;
103         unsigned new_obj_size;
104
105         /* get number of channels */
106         nchan = rte_memory_get_nchannel();
107         if (nchan == 0)
108                 nchan = 1;
109
110         nrank = rte_memory_get_nrank();
111         if (nrank == 0)
112                 nrank = 1;
113
114         /* process new object size */
115         new_obj_size = (obj_size + CACHE_LINE_MASK) / CACHE_LINE_SIZE;
116         while (get_gcd(new_obj_size, nrank * nchan) != 1 ||
117                         get_gcd(nchan, new_obj_size) != 1)
118                 new_obj_size++;
119         return new_obj_size * CACHE_LINE_SIZE;
120 }
121
122 /* create the mempool */
123 struct rte_mempool *
124 rte_mempool_create(const char *name, unsigned n, unsigned elt_size,
125                    unsigned cache_size, unsigned private_data_size,
126                    rte_mempool_ctor_t *mp_init, void *mp_init_arg,
127                    rte_mempool_obj_ctor_t *obj_init, void *obj_init_arg,
128                    int socket_id, unsigned flags)
129 {
130         char mz_name[RTE_MEMZONE_NAMESIZE];
131         char rg_name[RTE_RING_NAMESIZE];
132         struct rte_mempool *mp = NULL;
133         struct rte_ring *r;
134         const struct rte_memzone *mz;
135         size_t mempool_size;
136         int mz_flags = RTE_MEMZONE_1GB|RTE_MEMZONE_SIZE_HINT_ONLY;
137         int rg_flags = 0;
138         uint32_t header_size, trailer_size;
139         uint32_t total_elt_size;
140         unsigned i;
141         void *obj;
142
143         /* compilation-time checks */
144         RTE_BUILD_BUG_ON((sizeof(struct rte_mempool) &
145                           CACHE_LINE_MASK) != 0);
146 #if RTE_MEMPOOL_CACHE_MAX_SIZE > 0
147         RTE_BUILD_BUG_ON((sizeof(struct rte_mempool_cache) &
148                           CACHE_LINE_MASK) != 0);
149         RTE_BUILD_BUG_ON((offsetof(struct rte_mempool, local_cache) &
150                           CACHE_LINE_MASK) != 0);
151 #endif
152 #ifdef RTE_LIBRTE_MEMPOOL_DEBUG
153         RTE_BUILD_BUG_ON((sizeof(struct rte_mempool_debug_stats) &
154                           CACHE_LINE_MASK) != 0);
155         RTE_BUILD_BUG_ON((offsetof(struct rte_mempool, stats) &
156                           CACHE_LINE_MASK) != 0);
157 #endif
158
159         /* check that we have an initialised tail queue */
160         if (RTE_TAILQ_LOOKUP_BY_IDX(RTE_TAILQ_MEMPOOL, rte_mempool_list) == NULL) {
161                 rte_errno = E_RTE_NO_TAILQ;
162                 return NULL;    
163         }
164         
165         /* asked cache too big */
166         if (cache_size > RTE_MEMPOOL_CACHE_MAX_SIZE){
167                 rte_errno = EINVAL;
168                 return NULL;
169         }
170
171         /* "no cache align" imply "no spread" */
172         if (flags & MEMPOOL_F_NO_CACHE_ALIGN)
173                 flags |= MEMPOOL_F_NO_SPREAD;
174
175         /* ring flags */
176         if (flags & MEMPOOL_F_SP_PUT)
177                 rg_flags |= RING_F_SP_ENQ;
178         if (flags & MEMPOOL_F_SC_GET)
179                 rg_flags |= RING_F_SC_DEQ;
180
181         /* allocate the ring that will be used to store objects */
182         /* Ring functions will return appropriate errors if we are
183          * running as a secondary process etc., so no checks made
184          * in this function for that condition */
185         rte_snprintf(rg_name, sizeof(rg_name), "MP_%s", name);
186         r = rte_ring_create(rg_name, rte_align32pow2(n+1), socket_id, rg_flags);
187         if (r == NULL)
188                 return NULL;
189
190         /*
191          * In header, we have at least the pointer to the pool, and
192          * optionaly a 64 bits cookie.
193          */
194         header_size = 0;
195         header_size += sizeof(struct rte_mempool *); /* ptr to pool */
196 #ifdef RTE_LIBRTE_MEMPOOL_DEBUG
197         header_size += sizeof(uint64_t); /* cookie */
198 #endif
199         if ((flags & MEMPOOL_F_NO_CACHE_ALIGN) == 0)
200                 header_size = (header_size + CACHE_LINE_MASK) & (~CACHE_LINE_MASK);
201
202         /* trailer contains the cookie in debug mode */
203         trailer_size = 0;
204 #ifdef RTE_LIBRTE_MEMPOOL_DEBUG
205         trailer_size += sizeof(uint64_t); /* cookie */
206 #endif
207         /* element size is 8 bytes-aligned at least */
208         elt_size = (elt_size + 7) & (~7);
209
210         /* expand trailer to next cache line */
211         if ((flags & MEMPOOL_F_NO_CACHE_ALIGN) == 0) {
212                 total_elt_size = header_size + elt_size + trailer_size;
213                 trailer_size += ((CACHE_LINE_SIZE -
214                                   (total_elt_size & CACHE_LINE_MASK)) &
215                                  CACHE_LINE_MASK);
216         }
217
218         /*
219          * increase trailer to add padding between objects in order to
220          * spread them accross memory channels/ranks
221          */
222         if ((flags & MEMPOOL_F_NO_SPREAD) == 0) {
223                 unsigned new_size;
224                 new_size = optimize_object_size(header_size + elt_size +
225                                                 trailer_size);
226                 trailer_size = new_size - header_size - elt_size;
227         }
228
229         /* this is the size of an object, including header and trailer */
230         total_elt_size = header_size + elt_size + trailer_size;
231
232         /* reserve a memory zone for this mempool: private data is
233          * cache-aligned */
234         private_data_size = (private_data_size +
235                              CACHE_LINE_MASK) & (~CACHE_LINE_MASK);
236         mempool_size = total_elt_size * n +
237                 sizeof(struct rte_mempool) + private_data_size;
238         rte_snprintf(mz_name, sizeof(mz_name), "MP_%s", name);
239         mz = rte_memzone_reserve(mz_name, mempool_size, socket_id, mz_flags);
240
241         /*
242          * no more memory: in this case we loose previously reserved
243          * space for the as we cannot free it
244          */
245         if (mz == NULL)
246                 return NULL;
247
248         /* init the mempool structure */
249         mp = mz->addr;
250         memset(mp, 0, sizeof(*mp));
251         rte_snprintf(mp->name, sizeof(mp->name), "%s", name);
252         mp->phys_addr = mz->phys_addr;
253         mp->ring = r;
254         mp->size = n;
255         mp->flags = flags;
256         mp->elt_size = elt_size;
257         mp->header_size = header_size;
258         mp->trailer_size = trailer_size;
259         mp->cache_size = cache_size;
260         mp->cache_flushthresh = (uint32_t)(cache_size * CACHE_FLUSHTHRESH_MULTIPLIER);
261         mp->private_data_size = private_data_size;
262
263         /* call the initializer */
264         if (mp_init)
265                 mp_init(mp, mp_init_arg);
266
267         /* fill the headers and trailers, and add objects in ring */
268         obj = (char *)mp + sizeof(struct rte_mempool) + private_data_size;
269         for (i = 0; i < n; i++) {
270                 struct rte_mempool **mpp;
271                 obj = (char *)obj + header_size;
272
273                 /* set mempool ptr in header */
274                 mpp = __mempool_from_obj(obj);
275                 *mpp = mp;
276
277 #ifdef RTE_LIBRTE_MEMPOOL_DEBUG
278                 __mempool_write_header_cookie(obj, 1);
279                 __mempool_write_trailer_cookie(obj);
280 #endif
281                 /* call the initializer */
282                 if (obj_init)
283                         obj_init(mp, obj_init_arg, obj, i);
284
285                 /* enqueue in ring */
286                 rte_ring_sp_enqueue(mp->ring, obj);
287                 obj = (char *)obj + elt_size + trailer_size;
288         }
289
290         RTE_EAL_TAILQ_INSERT_TAIL(RTE_TAILQ_MEMPOOL, rte_mempool_list, mp);
291
292         return mp;
293 }
294
295 /* Return the number of entries in the mempool */
296 unsigned
297 rte_mempool_count(const struct rte_mempool *mp)
298 {
299         unsigned count;
300
301         count = rte_ring_count(mp->ring);
302
303 #if RTE_MEMPOOL_CACHE_MAX_SIZE > 0
304         {
305                 unsigned lcore_id;
306                 if (mp->cache_size == 0)
307                         return count;
308
309                 for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++)
310                         count += mp->local_cache[lcore_id].len;
311         }
312 #endif
313
314         /*
315          * due to race condition (access to len is not locked), the
316          * total can be greater than size... so fix the result
317          */
318         if (count > mp->size)
319                 return mp->size;
320         return count;
321 }
322
323 /* dump the cache status */
324 static unsigned
325 rte_mempool_dump_cache(const struct rte_mempool *mp)
326 {
327 #if RTE_MEMPOOL_CACHE_MAX_SIZE > 0
328         unsigned lcore_id;
329         unsigned count = 0;
330         unsigned cache_count;
331
332         printf("  cache infos:\n");
333         printf("    cache_size=%"PRIu32"\n", mp->cache_size);
334         for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
335                 cache_count = mp->local_cache[lcore_id].len;
336                 printf("    cache_count[%u]=%u\n", lcore_id, cache_count);
337                 count += cache_count;
338         }
339         printf("    total_cache_count=%u\n", count);
340         return count;
341 #else
342         RTE_SET_USED(mp);
343         printf("  cache disabled\n");
344         return 0;
345 #endif
346 }
347
348 #ifdef RTE_LIBRTE_MEMPOOL_DEBUG
349 /* check cookies before and after objects */
350 #ifndef __INTEL_COMPILER
351 #pragma GCC diagnostic ignored "-Wcast-qual"
352 #endif
353 static void
354 mempool_audit_cookies(const struct rte_mempool *mp)
355 {
356         unsigned i;
357         void *obj;
358         void * const *obj_table;
359
360         obj = (char *)mp + sizeof(struct rte_mempool) + mp->private_data_size;
361         for (i = 0; i < mp->size; i++) {
362                 obj = (char *)obj + mp->header_size;
363                 obj_table = &obj;
364                 __mempool_check_cookies(mp, obj_table, 1, 2);
365                 obj = (char *)obj + mp->elt_size + mp->trailer_size;
366         }
367 }
368 #ifndef __INTEL_COMPILER
369 #pragma GCC diagnostic error "-Wcast-qual"
370 #endif
371 #else
372 #define mempool_audit_cookies(mp) do {} while(0)
373 #endif
374
375 #if RTE_MEMPOOL_CACHE_MAX_SIZE > 0
376 /* check cookies before and after objects */
377 static void
378 mempool_audit_cache(const struct rte_mempool *mp)
379 {
380         /* check cache size consistency */
381         unsigned lcore_id;
382         for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
383                 if (mp->local_cache[lcore_id].len > mp->cache_flushthresh) {
384                         RTE_LOG(CRIT, MEMPOOL, "badness on cache[%u]\n",
385                                 lcore_id);
386                         rte_panic("MEMPOOL: invalid cache len\n");
387                 }
388         }
389 }
390 #else
391 #define mempool_audit_cache(mp) do {} while(0)
392 #endif
393
394
395 /* check the consistency of mempool (size, cookies, ...) */
396 void
397 rte_mempool_audit(const struct rte_mempool *mp)
398 {
399         mempool_audit_cache(mp);
400         mempool_audit_cookies(mp);
401 }
402
403 /* dump the status of the mempool on the console */
404 void
405 rte_mempool_dump(const struct rte_mempool *mp)
406 {
407 #ifdef RTE_LIBRTE_MEMPOOL_DEBUG
408         struct rte_mempool_debug_stats sum;
409         unsigned lcore_id;
410 #endif
411         unsigned common_count;
412         unsigned cache_count;
413
414         printf("mempool <%s>@%p\n", mp->name, mp);
415         printf("  flags=%x\n", mp->flags);
416         printf("  ring=<%s>@%p\n", mp->ring->name, mp->ring);
417         printf("  size=%"PRIu32"\n", mp->size);
418         printf("  header_size=%"PRIu32"\n", mp->header_size);
419         printf("  elt_size=%"PRIu32"\n", mp->elt_size);
420         printf("  trailer_size=%"PRIu32"\n", mp->trailer_size);
421         printf("  total_obj_size=%"PRIu32"\n",
422                mp->header_size + mp->elt_size + mp->trailer_size);
423
424         cache_count = rte_mempool_dump_cache(mp);
425         common_count = rte_ring_count(mp->ring);
426         if ((cache_count + common_count) > mp->size)
427                 common_count = mp->size - cache_count;
428         printf("  common_pool_count=%u\n", common_count);
429
430         /* sum and dump statistics */
431 #ifdef RTE_LIBRTE_MEMPOOL_DEBUG
432         memset(&sum, 0, sizeof(sum));
433         for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
434                 sum.put_bulk += mp->stats[lcore_id].put_bulk;
435                 sum.put_objs += mp->stats[lcore_id].put_objs;
436                 sum.get_success_bulk += mp->stats[lcore_id].get_success_bulk;
437                 sum.get_success_objs += mp->stats[lcore_id].get_success_objs;
438                 sum.get_fail_bulk += mp->stats[lcore_id].get_fail_bulk;
439                 sum.get_fail_objs += mp->stats[lcore_id].get_fail_objs;
440         }
441         printf("  stats:\n");
442         printf("    put_bulk=%"PRIu64"\n", sum.put_bulk);
443         printf("    put_objs=%"PRIu64"\n", sum.put_objs);
444         printf("    get_success_bulk=%"PRIu64"\n", sum.get_success_bulk);
445         printf("    get_success_objs=%"PRIu64"\n", sum.get_success_objs);
446         printf("    get_fail_bulk=%"PRIu64"\n", sum.get_fail_bulk);
447         printf("    get_fail_objs=%"PRIu64"\n", sum.get_fail_objs);
448 #else
449         printf("  no statistics available\n");
450 #endif
451
452         rte_mempool_audit(mp);
453 }
454
455 /* dump the status of all mempools on the console */
456 void
457 rte_mempool_list_dump(void)
458 {
459         const struct rte_mempool *mp = NULL;
460         struct rte_mempool_list *mempool_list;
461
462         if ((mempool_list = 
463              RTE_TAILQ_LOOKUP_BY_IDX(RTE_TAILQ_MEMPOOL, rte_mempool_list)) == NULL) {
464                 rte_errno = E_RTE_NO_TAILQ;
465                 return; 
466         }
467
468         TAILQ_FOREACH(mp, mempool_list, next) {
469                 rte_mempool_dump(mp);
470         }
471 }
472
473 /* search a mempool from its name */
474 struct rte_mempool *
475 rte_mempool_lookup(const char *name)
476 {
477         struct rte_mempool *mp = NULL;
478         struct rte_mempool_list *mempool_list;
479
480         if ((mempool_list = 
481              RTE_TAILQ_LOOKUP_BY_IDX(RTE_TAILQ_MEMPOOL, rte_mempool_list)) == NULL) {
482                 rte_errno = E_RTE_NO_TAILQ;
483                 return NULL;
484         }
485
486         TAILQ_FOREACH(mp, mempool_list, next) {
487                 if (strncmp(name, mp->name, RTE_MEMPOOL_NAMESIZE) == 0)
488                         break;
489         }
490         if (mp == NULL)
491                 rte_errno = ENOENT;
492
493         return mp;
494 }