/* *------------------------------------------------------------------ * svm.c - shared VM allocation, mmap(...MAP_FIXED...) * library * * Copyright (c) 2009 Cisco and/or its affiliates. * 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. *------------------------------------------------------------------ */ #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/mman.h> #include <sys/stat.h> #include <netinet/in.h> #include <signal.h> #include <pthread.h> #include <unistd.h> #include <time.h> #include <fcntl.h> #include <string.h> #include <vppinfra/clib.h> #include <vppinfra/vec.h> #include <vppinfra/hash.h> #include <vppinfra/bitmap.h> #include <vppinfra/fifo.h> #include <vppinfra/time.h> #include <vppinfra/mheap.h> #include <vppinfra/heap.h> #include <vppinfra/pool.h> #include <vppinfra/format.h> #include "svm.h" static svm_region_t *root_rp; static int root_rp_refcount; #define MAXLOCK 2 static pthread_mutex_t *mutexes_held[MAXLOCK]; static int nheld; svm_region_t * svm_get_root_rp (void) { return root_rp; } #define MUTEX_DEBUG u64 svm_get_global_region_base_va () { #if __aarch64__ /* On AArch64 VA space can have different size, from 36 to 48 bits. Here we are trying to detect VA bits by parsing /proc/self/maps address ranges */ int fd; unformat_input_t input; u64 start, end = 0; u8 bits = 0; if ((fd = open ("/proc/self/maps", 0)) < 0) clib_unix_error ("open '/proc/self/maps'"); unformat_init_clib_file (&input, fd); while (unformat_check_input (&input) != UNFORMAT_END_OF_INPUT) { if (unformat (&input, "%llx-%llx", &start, &end)) end--; unformat_skip_line (&input); } unformat_free (&input); close (fd); bits = count_leading_zeros (end); bits = 64 - bits; if (bits >= 36 && bits <= 48) return ((1ul << bits) / 4) - (2 * SVM_GLOBAL_REGION_SIZE); else clib_unix_error ("unexpected va bits '%u'", bits); #endif /* default value */ return 0x130000000ULL; } static void region_lock (svm_region_t * rp, int tag) { pthread_mutex_lock (&rp->mutex); #ifdef MUTEX_DEBUG rp->mutex_owner_pid = getpid (); rp->mutex_owner_tag = tag; #endif ASSERT (nheld < MAXLOCK); /* * Keep score of held mutexes so we can try to exit * cleanly if the world comes to an end at the worst possible * moment */ mutexes_held[nheld++] = &rp->mutex; } static void region_unlock (svm_region_t * rp) { int i, j; #ifdef MUTEX_DEBUG rp->mutex_owner_pid = 0; rp->mutex_owner_tag = 0; #endif for (i = nheld - 1; i >= 0; i--) { if (mutexes_held[i] == &rp->mutex) { for (j = i; j < MAXLOCK - 1; j++) mutexes_held[j] = mutexes_held[j + 1]; nheld--; goto found; } } ASSERT (0); found: CLIB_MEMORY_BARRIER (); pthread_mutex_unlock (&rp->mutex); } static u8 * format_svm_flags (u8 * s, va_list * args) { uword f = va_arg (*args, uword); if (f & SVM_FLAGS_MHEAP) s = format (s, "MHEAP "); if (f & SVM_FLAGS_FILE) s = format (s, "FILE "); if (f & SVM_FLAGS_NODATA) s = format (s, "NODATA "); if (f & SVM_FLAGS_NEED_DATA_INIT) s = format (s, "INIT "); return (s); } static u8 * format_svm_size (u8 * s, va_list * args) { uword size = va_arg (*args, uword); if (size >= (1 << 20)) { s = format (s, "(%d mb)", size >> 20); } else if (size >= (1 << 10)) { s = format (s, "(%d kb)", size >> 10); } else { s = format (s, "(%d bytes)", size); } return (s); } u8 * format_svm_region (u8 * s, va_list * args) { svm_region_t *rp = va_arg (*args, svm_region_t *); int verbose = va_arg (*args, int); int i; uword lo, hi; s = format (s, "%s: base va 0x%x size 0x%x %U\n", rp->region_name, rp->virtual_base, rp->virtual_size, format_svm_size, rp->virtual_size); s = format (s, " user_ctx 0x%x, bitmap_size %d\n", rp->user_ctx, rp->bitmap_size); if (verbose) { s = format (s, " flags: 0x%x %U\n", rp->flags, format_svm_flags, rp->flags); s = format (s, " region_heap 0x%x data_base 0x%x data_heap 0x%x\n", rp->region_heap, rp->data_base, rp->data_heap); } s = format (s, " %d clients, pids: ", vec_len (rp->client_pids)); for (i = 0; i < vec_len (rp->client_pids); i++) s = format (s, "%d ", rp->client_pids[i]); s = format (s, "\n"); if (verbose) { lo = hi = ~0; s = format (s, " VM in use: "); for (i = 0; i < rp->bitmap_size; i++) { if (clib_bitmap_get_no_check (rp->bitmap, i) != 0) { if (lo == ~0) { hi = lo = rp->virtual_base + i * MMAP_PAGESIZE; } else { hi = rp->virtual_base + i * MMAP_PAGESIZE; } } else { if (lo != ~0) { hi = rp->virtual_base + i * MMAP_PAGESIZE - 1; s = format (s, " 0x%x - 0x%x (%dk)\n", lo, hi, (hi - lo) >> 10); lo = hi = ~0; } } } #if USE_DLMALLOC == 0 s = format (s, " rgn heap stats: %U", format_mheap, rp->region_heap, 0); if ((rp->flags & SVM_FLAGS_MHEAP) && rp->data_heap) { s = format (s, "\n data heap stats: %U", format_mheap, rp->data_heap, 1); } s = format (s, "\n"); #endif } return (s); } /* * rnd_pagesize * Round to a pagesize multiple, presumably 4k works */ static u64 rnd_pagesize (u64 size) { u64 rv; rv = (size + (MMAP_PAGESIZE - 1)) & ~(MMAP_PAGESIZE - 1); return (rv); } /* * svm_data_region_setup */ static int svm_data_region_create (svm_map_region_args_t * a, svm_region_t * rp) { int fd; u8 junk = 0; uword map_size; map_size = rp->virtual_size - (MMAP_PAGESIZE + (a->pvt_heap_size ? a->pvt_heap_size : SVM_PVT_MHEAP_SIZE)); if (a->flags & SVM_FLAGS_FILE) { struct stat statb; fd = open (a->backing_file, O_RDWR | O_CREAT, 0777); if (fd < 0) { clib_unix_warning ("open"); return -1; } if (fstat (fd, &statb) < 0) { clib_unix_warning ("fstat"); close (fd); return -2; } if (statb.st_mode & S_IFREG) { if (statb.st_size == 0) { if (lseek (fd, map_size, SEEK_SET) == (off_t) - 1) { clib_unix_warning ("seek region size"); close (fd); return -3; } if (write (fd, &junk, 1) != 1) { clib_unix_warning ("set region size"); close (fd); return -3; } } else { map_size = rnd_pagesize (statb.st_size); } } else { map_size = a->backing_mmap_size; } ASSERT (map_size <= rp->virtual_size - (MMAP_PAGESIZE + SVM_PVT_MHEAP_SIZE)); if (mmap (rp->data_base, map_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0) == MAP_FAILED) { clib_unix_warning ("mmap"); close (fd); return -3; } close (fd); rp->backing_file = (char *) format (0, "%s\0", a->backing_file); rp->flags |= SVM_FLAGS_FILE; } if (a->flags & SVM_FLAGS_MHEAP) { #if USE_DLMALLOC == 0 mheap_t *heap_header; rp->data_heap = mheap_alloc_with_flags ((void *) (rp->data_base), map_size, MHEAP_FLAG_DISABLE_VM); heap_header = mheap_header (rp->data_heap); heap_header->flags |= MHEAP_FLAG_THREAD_SAFE; #else rp->data_heap = create_mspace_with_base (rp->data_base, map_size, 1 /* locked */ ); mspace_disable_expand (rp->data_heap); #endif rp->flags |= SVM_FLAGS_MHEAP; } return 0; } static int svm_data_region_map (svm_map_region_args_t * a, svm_region_t * rp) { int fd; u8 junk = 0; uword map_size; struct stat statb; map_size = rp->virtual_size - (MMAP_PAGESIZE + (a->pvt_heap_size ? a->pvt_heap_size : SVM_PVT_MHEAP_SIZE)); if (a->flags & SVM_FLAGS_FILE) { fd = open (a->backing_file, O_RDWR, 0777); if (fd < 0) { clib_unix_warning ("open"); return -1; } if (fstat (fd, &statb) < 0) { clib_unix_warning ("fstat"); close (fd); return -2; } if (statb.st_mode & S_IFREG) { if (statb.st_size == 0) { if (lseek (fd, map_size, SEEK_SET) == (off_t) - 1) { clib_unix_warning ("seek region size"); close (fd); return -3; } if (write (fd, &junk, 1) != 1) { clib_unix_warning ("set region size"); close (fd); return -3; } } else { map_size = rnd_pagesize (statb.st_size); } } else { map_size = a->backing_mmap_size; } ASSERT (map_size <= rp->virtual_size - (MMAP_PAGESIZE + (a->pvt_heap_size ? a->pvt_heap_size : SVM_PVT_MHEAP_SIZE))); if (mmap (rp->data_base, map_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0) == MAP_FAILED) { clib_unix_warning ("mmap"); close (fd); return -3; } close (fd); } return 0; } u8 * shm_name_from_svm_map_region_args (svm_map_region_args_t * a) { u8 *path; u8 *shm_name; u8 *split_point; u8 *mkdir_arg = 0; int root_path_offset = 0; int name_offset = 0; if (a->root_path) { /* Tolerate present or absent slashes */ if (a->root_path[0] == '/') root_path_offset++; /* create the root_path under /dev/shm iterate through path creating directories */ path = format (0, "/dev/shm/%s%c", &a->root_path[root_path_offset], 0); split_point = path + 1; vec_add1 (mkdir_arg, '-'); while (*split_point) { while (*split_point && *split_point != '/') { vec_add1 (mkdir_arg, *split_point); split_point++; } vec_add1 (mkdir_arg, 0); /* ready to descend another level */ mkdir_arg[vec_len (mkdir_arg) - 1] = '-'; split_point++; } vec_free (mkdir_arg); vec_free (path); if (a->name[0] == '/') name_offset = 1; shm_name = format (0, "/%s-%s%c", &a->root_path[root_path_offset], &a->name[name_offset], 0); } else shm_name = format (0, "%s%c", a->name, 0); return (shm_name); } void svm_region_init_mapped_region (svm_map_region_args_t * a, svm_region_t * rp) { pthread_mutexattr_t attr; pthread_condattr_t cattr; int nbits, words, bit; int overhead_space; void *oldheap; uword data_base; ASSERT (rp); int rv; clib_memset (rp, 0, sizeof (*rp)); if (pthread_mutexattr_init (&attr)) clib_unix_warning ("mutexattr_init"); if (pthread_mutexattr_setpshared (&attr, PTHREAD_PROCESS_SHARED)) clib_unix_warning ("mutexattr_setpshared"); if (pthread_mutex_init (&rp->mutex, &attr)) clib_unix_warning ("mutex_init"); if (pthread_mutexattr_destroy (&attr)) clib_unix_warning ("mutexattr_destroy"); if (pthread_condattr_init (&cattr)) clib_unix_warning ("condattr_init"); if (pthread_condattr_setpshared (&cattr, PTHREAD_PROCESS_SHARED)) clib_unix_warning ("condattr_setpshared"); if (pthread_cond_init (&rp->condvar, &cattr)) clib_unix_warning ("cond_init"); if (pthread_condattr_destroy (&cattr)) clib_unix_warning ("condattr_destroy"); region_lock (rp, 1); rp->virtual_base = a->baseva; rp->virtual_size = a->size; #if USE_DLMALLOC == 0 rp->region_heap = mheap_alloc_with_flags (uword_to_pointer (a->baseva + MMAP_PAGESIZE, void *), (a->pvt_heap_size != 0) ? a->pvt_heap_size : SVM_PVT_MHEAP_SIZE, MHEAP_FLAG_DISABLE_VM); #else rp->region_heap = create_mspace_with_base (uword_to_pointer (a->baseva + MMAP_PAGESIZE, void *), (a->pvt_heap_size != 0) ? a->pvt_heap_size : SVM_PVT_MHEAP_SIZE, 1 /* locked */ ); mspace_disable_expand (rp->region_heap); #endif oldheap = svm_push_pvt_heap (rp); rp->region_name = (char *) format (0, "%s%c", a->name, 0); vec_add1 (rp->client_pids, getpid ()); nbits = rp->virtual_size / MMAP_PAGESIZE; ASSERT (nbits > 0); rp->bitmap_size = nbits; words = (nbits + BITS (uword) - 1) / BITS (uword); vec_validate (rp->bitmap, words - 1); overhead_space = MMAP_PAGESIZE /* header */ + ((a->pvt_heap_size != 0) ? a->pvt_heap_size : SVM_PVT_MHEAP_SIZE); bit = 0; data_base = (uword) rp->virtual_base; if (a->flags & SVM_FLAGS_NODATA) rp->flags |= SVM_FLAGS_NEED_DATA_INIT; do { clib_bitmap_set_no_check (rp->bitmap, bit, 1); bit++; overhead_space -= MMAP_PAGESIZE; data_base += MMAP_PAGESIZE; } while (overhead_space > 0); rp->data_base = (void *) data_base; /* * Note: although the POSIX spec guarantees that only one * process enters this block, we have to play games * to hold off clients until e.g. the mutex is ready */ rp->version = SVM_VERSION; /* setup the data portion of the region */ rv = svm_data_region_create (a, rp); if (rv) { clib_warning ("data_region_create: %d", rv); } region_unlock (rp); svm_pop_heap (oldheap); } /* * svm_map_region */ void * svm_map_region (svm_map_region_args_t * a) { int svm_fd; svm_region_t *rp; int deadman = 0; u8 junk = 0; void *oldheap; int rv; int pid_holding_region_lock; u8 *shm_name; int dead_region_recovery = 0; int time_left; struct stat stat; struct timespec ts, tsrem; ASSERT ((a->size & ~(MMAP_PAGESIZE - 1)) == a->size); ASSERT (a->name); shm_name = shm_name_from_svm_map_region_args (a); if (CLIB_DEBUG > 1) clib_warning ("[%d] map region %s: shm_open (%s)", getpid (), a->name, shm_name); svm_fd = shm_open ((char *) shm_name, O_RDWR | O_CREAT | O_EXCL, 0777); if (svm_fd >= 0) { if (fchmod (svm_fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) < 0) clib_unix_warning ("segment chmod"); /* This turns out to fail harmlessly if the client starts first */ if (fchown (svm_fd, a->uid, a->gid) < 0) clib_unix_warning ("segment chown [ok if client starts first]"); vec_free (shm_name); if (lseek (svm_fd, a->size, SEEK_SET) == (off_t) - 1) { clib_warning ("seek region size"); close (svm_fd); return (0); } if (write (svm_fd, &junk, 1) != 1) { clib_warning ("set region size"); close (svm_fd); return (0); } rp = mmap (uword_to_pointer (a->baseva, void *), a->size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, svm_fd, 0); if (rp == (svm_region_t *) MAP_FAILED) { clib_unix_warning ("mmap create"); close (svm_fd); return (0); } close (svm_fd); svm_region_init_mapped_region (a, rp); return ((void *) rp); } else { svm_fd = shm_open ((char *) shm_name, O_RDWR, 0777); vec_free (shm_name); if (svm_fd < 0) { perror ("svm_region_map(mmap open)"); return (0); } /* Reset ownership in case the client started first */ if (fchown (svm_fd, a->uid, a->gid) < 0) clib_unix_warning ("segment chown [ok if client starts first]"); time_left = 20; while (1) { if (0 != fstat (svm_fd, &stat)) { clib_warning ("fstat failed: %d", errno); close (svm_fd); return (0); } if (stat.st_size > 0) { break; } if (0 == time_left) { clib_warning ("waiting for resize of shm file timed out"); close (svm_fd); return (0); } ts.tv_sec = 0; ts.tv_nsec = 100000000; while (nanosleep (&ts, &tsrem) < 0) ts = tsrem; time_left--; } rp = mmap (0, MMAP_PAGESIZE, PROT_READ | PROT_WRITE, MAP_SHARED, svm_fd, 0); if (rp == (svm_region_t *) MAP_FAILED) { close (svm_fd); clib_warning ("mmap"); return (0); } /* * We lost the footrace to create this region; make sure * the winner has crossed the finish line. */ while (rp->version == 0 && deadman++ < 5) { sleep (1); } /* * <bleep>-ed? */ if (rp->version == 0) { clib_warning ("rp->version %d not %d", rp->version, SVM_VERSION); close (svm_fd); munmap (rp, a->size); return (0); } /* Remap now that the region has been placed */ a->baseva = rp->virtual_base; a->size = rp->virtual_size; munmap (rp, MMAP_PAGESIZE); rp = (void *) mmap (uword_to_pointer (a->baseva, void *), a->size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, svm_fd, 0); if ((uword) rp == (uword) MAP_FAILED) { clib_unix_warning ("mmap"); close (svm_fd); return (0); } close (svm_fd); if ((uword) rp != rp->virtual_base) { clib_warning ("mmap botch"); } /* * Try to fix the region mutex if it is held by * a dead process */ pid_holding_region_lock = rp->mutex_owner_pid; if (pid_holding_region_lock && kill (pid_holding_region_lock, 0) < 0) { clib_warning ("region %s mutex held by dead pid %d, tag %d, force unlock", rp->region_name, pid_holding_region_lock, rp->mutex_owner_tag); /* owner pid is nonexistent */ rp->mutex.__data.__owner = 0; rp->mutex.__data.__lock = 0; dead_region_recovery = 1; } if (dead_region_recovery) clib_warning ("recovery: attempt to re-lock region"); region_lock (rp, 2); oldheap = svm_push_pvt_heap (rp); vec_add1 (rp->client_pids, getpid ()); if (dead_region_recovery) clib_warning ("recovery: attempt svm_data_region_map"); rv = svm_data_region_map (a, rp); if (rv) { clib_warning ("data_region_map: %d", rv); } if (dead_region_recovery) clib_warning ("unlock and continue"); region_unlock (rp); svm_pop_heap (oldheap); return ((void *) rp); } return 0; /* NOTREACHED */ } static void svm_mutex_cleanup (void) { int i; for (i = 0; i < nheld; i++) { pthread_mutex_unlock (mutexes_held[i]); } } static int svm_region_init_internal (svm_map_region_args_t * a) { svm_region_t *rp; u64 ticks = clib_cpu_time_now (); uword randomize_baseva; /* guard against klutz calls */ if (root_rp) return -1; root_rp_refcount++; atexit (svm_mutex_cleanup); /* Randomize the shared-VM base at init time */ if (MMAP_PAGESIZE <= (4 << 10)) randomize_baseva = (ticks & 15) * MMAP_PAGESIZE; else randomize_baseva = (ticks & 3) * MMAP_PAGESIZE; a->baseva += randomize_baseva; rp = svm_map_region (a); if (!rp) return -1; region_lock (rp, 3); /* Set up the main region data structures */ if (rp->flags & SVM_FLAGS_NEED_DATA_INIT) { svm_main_region_t *mp = 0; void *oldheap; rp->flags &= ~(SVM_FLAGS_NEED_DATA_INIT); oldheap = svm_push_pvt_heap (rp); vec_validate (mp, 0); mp->name_hash = hash_create_string (0, sizeof (uword)); mp->root_path = a->root_path ? format (0, "%s%c", a->root_path, 0) : 0; mp->uid = a->uid; mp->gid = a->gid; rp->data_base = mp; svm_pop_heap (oldheap); } region_unlock (rp); root_rp = rp; return 0; } void svm_region_init (void) { svm_map_region_args_t _a, *a = &_a; clib_memset (a, 0, sizeof (*a)); a->root_path = 0; a->name = SVM_GLOBAL_REGION_NAME; a->baseva = svm_get_global_region_base_va (); a->size =<style>.highlight .hll { background-color: #ffffcc } .highlight .c { color: #888888 } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { color: #008800; font-weight: bold } /* Keyword */ .highlight .ch { color: #888888 } /* Comment.Hashbang */ .highlight .cm { color: #888888 } /* Comment.Multiline */ .highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */ .highlight .cpf { color: #888888 } /* Comment.PreprocFile */ .highlight .c1 { color: #888888 } /* Comment.Single */ .highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */</style><div class="highlight"><pre><span></span><span class="cm">/*</span> <span class="cm"> * Copyright (c) 2015 Cisco and/or its affiliates.</span> <span class="cm"> * Licensed under the Apache License, Version 2.0 (the "License");</span> <span class="cm"> * you may not use this file except in compliance with the License.</span> <span class="cm"> * You may obtain a copy of the License at:</span> <span class="cm"> *</span> <span class="cm"> * http://www.apache.org/licenses/LICENSE-2.0</span> <span class="cm"> *</span> <span class="cm"> * Unless required by applicable law or agreed to in writing, software</span> <span class="cm"> * distributed under the License is distributed on an "AS IS" BASIS,</span> <span class="cm"> * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.</span> <span class="cm"> * See the License for the specific language governing permissions and</span> <span class="cm"> * limitations under the License.</span> <span class="cm"> */</span> <span class="cm">/*</span> <span class="cm"> Copyright (c) 2005 Eliot Dresselhaus</span> <span class="cm"> Permission is hereby granted, free of charge, to any person obtaining</span> <span class="cm"> a copy of this software and associated documentation files (the</span> <span class="cm"> "Software"), to deal in the Software without restriction, including</span> <span class="cm"> without limitation the rights to use, copy, modify, merge, publish,</span> <span class="cm"> distribute, sublicense, and/or sell copies of the Software, and to</span> <span class="cm"> permit persons to whom the Software is furnished to do so, subject to</span> <span class="cm"> the following conditions:</span> <span class="cm"> The above copyright notice and this permission notice shall be</span> <span class="cm"> included in all copies or substantial portions of the Software.</span> <span class="cm"> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,</span> <span class="cm"> EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF</span> <span class="cm"> MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND</span> <span class="cm"> NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE</span> <span class="cm"> LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION</span> <span class="cm"> OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION</span> <span class="cm"> WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</span> <span class="cm">*/</span> <span class="cp">#ifndef included_clib_serialize_h</span> <span class="cp">#define included_clib_serialize_h</span> <span class="cp">#include</span> <span class="cpf"><stdarg.h></span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf"><vppinfra/byte_order.h></span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf"><vppinfra/types.h></span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf"><vppinfra/vec.h></span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf"><vppinfra/longjmp.h></span><span class="cp"></span> <span class="k">struct</span> <span class="n">serialize_main_header_t</span><span class="p">;</span> <span class="k">struct</span> <span class="n">serialize_stream_t</span><span class="p">;</span> <span class="k">typedef</span> <span class="nf">void</span> <span class="p">(</span><span class="n">serialize_data_function_t</span><span class="p">)</span> <span class="p">(</span><span class="k">struct</span> <span class="n">serialize_main_header_t</span> <span class="o">*</span> <span class="n">h</span><span class="p">,</span> <span class="k">struct</span> <span class="n">serialize_stream_t</span> <span class="o">*</span> <span class="n">s</span><span class="p">);</span> <span class="k">typedef</span> <span class="k">struct</span> <span class="n">serialize_stream_t</span> <span class="p">{</span> <span class="cm">/* Current data buffer being serialized/unserialized. */</span> <span class="n">u8</span> <span class="o">*</span><span class="n">buffer</span><span class="p">;</span> <span class="cm">/* Size of buffer in bytes. */</span> <span class="n">u32</span> <span class="n">n_buffer_bytes</span><span class="p">;</span> <span class="cm">/* Current index into buffer. */</span> <span class="n">u32</span> <span class="n">current_buffer_index</span><span class="p">;</span> <span class="cm">/* Overflow buffer for when there is not enough room at the end of</span> <span class="cm"> buffer to hold serialized/unserialized data. */</span> <span class="n">u8</span> <span class="o">*</span><span class="n">overflow_buffer</span><span class="p">;</span> <span class="cm">/* Current index in overflow buffer for reads. */</span> <span class="n">u32</span> <span class="n">current_overflow_index</span><span class="p">;</span> <span class="n">u32</span> <span class="n">flags</span><span class="p">;</span> <span class="cp">#define SERIALIZE_END_OF_STREAM (1 << 0)</span> <span class="n">uword</span> <span class="n">data_function_opaque</span><span class="p">;</span> <span class="n">u32</span> <span class="n">opaque</span><span class="p">[</span><span class="mi">64</span> <span class="o">-</span> <span class="mi">4</span> <span class="o">*</span> <span class="k">sizeof</span> <span class="p">(</span><span class="n">u32</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span> <span class="o">*</span> <span class="k">sizeof</span> <span class="p">(</span><span class="n">uword</span><span class="p">)</span> <span class="o">-</span> <span class="mi">2</span> <span class="o">*</span> <span class="k">sizeof</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)];</span> <span class="p">}</span> <span class="n">serialize_stream_t</span><span class="p">;</span> <span class="n">always_inline</span> <span class=