Merge branch 'master' into benchmark
authorDavid Goulet <dgoulet@efficios.com>
Mon, 9 Jan 2012 21:35:12 +0000 (16:35 -0500)
committerDavid Goulet <dgoulet@efficios.com>
Mon, 9 Jan 2012 21:35:12 +0000 (16:35 -0500)
Signed-off-by: David Goulet <dgoulet@efficios.com>
56 files changed:
ChangeLog
Makefile.am
common/Makefile.am [new file with mode: 0644]
common/runas.c [new file with mode: 0644]
configure.ac
doc/dev/lttng-cli.txt
doc/quickstart.txt
hashtable/hash.c [deleted file]
hashtable/hash.h [deleted file]
hashtable/rculfhash.c [deleted file]
hashtable/rculfhash.h [deleted file]
include/Makefile.am
include/lttng-ht.h [new file with mode: 0644]
include/lttng-sessiond-comm.h
include/lttng-share.h
include/lttng/lttng-consumer.h
include/runas.h [new file with mode: 0644]
liblttng-consumer/lttng-consumer.c
liblttng-ht/Makefile.am [new file with mode: 0644]
liblttng-ht/lttng-ht.c [new file with mode: 0644]
liblttng-ht/rculfhash-internal.h [new file with mode: 0644]
liblttng-ht/rculfhash-mm-chunk.c [new file with mode: 0644]
liblttng-ht/rculfhash-mm-mmap.c [new file with mode: 0644]
liblttng-ht/rculfhash-mm-order.c [new file with mode: 0644]
liblttng-ht/rculfhash.c [new file with mode: 0644]
liblttng-ht/rculfhash.h [new file with mode: 0644]
liblttng-ht/urcu-flavor.h [new file with mode: 0644]
liblttng-ht/utils.c [new file with mode: 0644]
liblttng-ht/utils.h [new file with mode: 0644]
liblttng-kconsumer/lttng-kconsumer.c
liblttng-sessiond-comm/lttng-sessiond-comm.c
liblttng-ustconsumer/Makefile.am
liblttng-ustconsumer/lttng-ustconsumer.c
liblttngctl/lttngctl.c
lttng-consumerd/Makefile.am
lttng-sessiond/Makefile.am
lttng-sessiond/channel.c
lttng-sessiond/context.c
lttng-sessiond/event.c
lttng-sessiond/hashtable.c [deleted file]
lttng-sessiond/hashtable.h [deleted file]
lttng-sessiond/lttng-sessiond.h
lttng-sessiond/lttng-ust-ctl.h
lttng-sessiond/main.c
lttng-sessiond/session.c
lttng-sessiond/session.h
lttng-sessiond/trace-kernel.h
lttng-sessiond/trace-ust.c
lttng-sessiond/trace-ust.h
lttng-sessiond/ust-app.c
lttng-sessiond/ust-app.h
lttng-sessiond/ust-consumer.c
lttng-sessiond/utils.c
lttng-sessiond/utils.h
tests/Makefile.am
tests/test_sessions.c

index e5064990f08901d030b9b9dfb778e446e1da7642..5f41df797636d07e1479254e838c6d8144042aa3 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,30 @@
+2011-12-23 lttng-tools 2.0-pre16
+       * Per-user lttng-sessiond now fully functionnal
+       * Fix nested double usage of hashtable iterators
+       * Support creds passing between lttng and sessiond
+       * Rename sessiond internal "uid" fields to "id"
+       * Create all trace directories and files with client user credentials
+       * Create output directory at session creation command
+       * Only allow the user or group owning the session (or root) to control a session
+       * Add disable kernel tracing option to sessiond
+
+2011-12-13 lttng-tools 2.0-pre15
+       * (MAJOR) User-space tracing support for global domain (lttng-ust 2.0)
+       * UST consumer 32/64 bit support
+       * Multiple bug fixes
+       * Multiple segfault, race and double lock fixes
+       * API change for lttng_list_domains, lttng_start/stop and lttng_destroy
+       * UST loglevel support
+       * ABIs now support 256 char event names
+       * Dependency on libpopt >= 1.13
+       * Dependency on URCU >= 0.6.7
+       * Rename kernel-ctl.c/.h to kernel.c/.h
+       * Change malloc to zmalloc in lttng-sessiond code
+       * RCU hashtable support for all UST data structure
+       * New libconsumer for UST, kernel and one generic
+       * Change "--all" option from lttng command line
+       * Remove function entry option to discourage its use
+
 2011-09-30 lttng-tools 2.0-pre14
        * Syscall tracing support
        * Multiple bug fixes (nothing critical)
index ae88bcaa0cadc5a541533cd7dbb27bdd8d3d76d1..fa7f5ec565a6a4944f55ac413826d488307efdd8 100644 (file)
@@ -1,8 +1,10 @@
 ACLOCAL_AMFLAGS = -I config
 
 SUBDIRS = benchmark \
+                 common \
                  liblttng-sessiond-comm \
                  libkernelctl \
+                 liblttng-ht \
                  liblttng-kconsumer \
                  liblttng-ustconsumer \
                  liblttng-consumer \
diff --git a/common/Makefile.am b/common/Makefile.am
new file mode 100644 (file)
index 0000000..c855317
--- /dev/null
@@ -0,0 +1,7 @@
+AM_CPPFLAGS = -I$(top_srcdir)/include
+
+AM_CFLAGS = -fno-strict-aliasing
+
+noinst_LTLIBRARIES = libcommon.la
+
+libcommon_la_SOURCES = runas.c
diff --git a/common/runas.c b/common/runas.c
new file mode 100644 (file)
index 0000000..f612ccc
--- /dev/null
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *                      Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; only version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#define _GNU_SOURCE
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sched.h>
+#include <sys/mman.h>
+
+#include <lttngerr.h>
+
+#define CHILD_STACK_SIZE       10485760
+
+struct run_as_data {
+       int (*cmd)(void *data);
+       void *data;
+       uid_t uid;
+       gid_t gid;
+       int retval_pipe;
+};
+
+struct mkdir_data {
+       const char *path;
+       mode_t mode;
+};
+
+struct open_data {
+       const char *path;
+       int flags;
+       mode_t mode;
+};
+
+/*
+ * Create recursively directory using the FULL path.
+ */
+static
+int _mkdir_recursive(void *_data)
+{
+       struct mkdir_data *data = _data;
+       const char *path;
+       char *p, tmp[PATH_MAX];
+       struct stat statbuf;
+       mode_t mode;
+       size_t len;
+       int ret;
+
+       path = data->path;
+       mode = data->mode;
+
+       ret = snprintf(tmp, sizeof(tmp), "%s", path);
+       if (ret < 0) {
+               PERROR("snprintf mkdir");
+               goto error;
+       }
+
+       len = ret;
+       if (tmp[len - 1] == '/') {
+               tmp[len - 1] = 0;
+       }
+
+       for (p = tmp + 1; *p; p++) {
+               if (*p == '/') {
+                       *p = 0;
+                       ret = stat(tmp, &statbuf);
+                       if (ret < 0) {
+                               ret = mkdir(tmp, mode);
+                               if (ret < 0) {
+                                       if (!(errno == EEXIST)) {
+                                               PERROR("mkdir recursive");
+                                               ret = -errno;
+                                               goto error;
+                                       }
+                               }
+                       }
+                       *p = '/';
+               }
+       }
+
+       ret = mkdir(tmp, mode);
+       if (ret < 0) {
+               if (!(errno == EEXIST)) {
+                       PERROR("mkdir recursive last piece");
+                       ret = -errno;
+               } else {
+                       ret = 0;
+               }
+       }
+
+error:
+       return ret;
+}
+
+static
+int _mkdir(void *_data)
+{
+       struct mkdir_data *data = _data;
+       return mkdir(data->path, data->mode);
+}
+
+static
+int _open(void *_data)
+{
+       struct open_data *data = _data;
+       return open(data->path, data->flags, data->mode);
+}
+
+static
+int child_run_as(void *_data)
+{
+       struct run_as_data *data = _data;
+       size_t writelen, writeleft, index;
+       union {
+               int i;
+               char c[sizeof(int)];
+       } sendret;
+       int ret;
+
+       /*
+        * Child: it is safe to drop egid and euid while sharing the
+        * file descriptors with the parent process, since we do not
+        * drop "uid": therefore, the user we are dropping egid/euid to
+        * cannot attach to this process with, e.g. ptrace, nor map this
+        * process memory.
+        */
+       if (data->gid != getegid()) {
+               ret = setegid(data->gid);
+               if (ret < 0) {
+                       perror("setegid");
+                       return EXIT_FAILURE;
+               }
+       }
+       if (data->uid != geteuid()) {
+               ret = seteuid(data->uid);
+               if (ret < 0) {
+                       perror("seteuid");
+                       return EXIT_FAILURE;
+               }
+       }
+       /*
+        * Also set umask to 0 for mkdir executable bit.
+        */
+       umask(0);
+       sendret.i = (*data->cmd)(data->data);
+       /* send back return value */
+       writeleft = sizeof(sendret);
+       index = 0;
+       do {
+               writelen = write(data->retval_pipe, &sendret.c[index],
+                               writeleft);
+               if (writelen < 0) {
+                       perror("write");
+                       return EXIT_FAILURE;
+               }
+               writeleft -= writelen;
+               index += writelen;
+       } while (writeleft > 0);
+       return EXIT_SUCCESS;
+}
+
+static
+int run_as(int (*cmd)(void *data), void *data, uid_t uid, gid_t gid)
+{
+       struct run_as_data run_as_data;
+       int ret = 0;
+       int status;
+       pid_t pid;
+       int retval_pipe[2];
+       ssize_t readlen, readleft, index;
+       void *child_stack;
+       union {
+               int i;
+               char c[sizeof(int)];
+       } retval;
+
+       /*
+        * If we are non-root, we can only deal with our own uid.
+        */
+       if (geteuid() != 0) {
+               if (uid != geteuid()) {
+                       ERR("Client (%d)/Server (%d) UID mismatch (and sessiond is not root)",
+                               uid, geteuid());
+                       return -EPERM;
+               }
+       }
+
+       ret = pipe(retval_pipe);
+       if (ret < 0) {
+               perror("pipe");
+               goto end;
+       }
+       run_as_data.data = data;
+       run_as_data.cmd = cmd;
+       run_as_data.uid = uid;
+       run_as_data.gid = gid;
+       run_as_data.retval_pipe = retval_pipe[1];       /* write end */
+       child_stack = mmap(NULL, CHILD_STACK_SIZE,
+               PROT_WRITE | PROT_READ,
+               MAP_PRIVATE | MAP_GROWSDOWN | MAP_ANONYMOUS | MAP_STACK,
+               -1, 0);
+       if (child_stack == MAP_FAILED) {
+               perror("mmap");
+               ret = -ENOMEM;
+               goto close_pipe;
+       }
+       /*
+        * Pointing to the middle of the stack to support architectures
+        * where the stack grows up (HPPA).
+        */
+       pid = clone(child_run_as, child_stack + (CHILD_STACK_SIZE / 2),
+               CLONE_FILES | SIGCHLD | CLONE_VM,
+               &run_as_data, NULL);
+       if (pid < 0) {
+               perror("clone");
+               ret = pid;
+               goto unmap_stack;
+       }
+       /* receive return value */
+       readleft = sizeof(retval);
+       index = 0;
+       do {
+               readlen = read(retval_pipe[0], &retval.c[index], readleft);
+               if (readlen < 0) {
+                       perror("read");
+                       ret = -1;
+                       break;
+               }
+               readleft -= readlen;
+               index += readlen;
+       } while (readleft > 0);
+
+       /*
+        * Parent: wait for child to return, in which case the
+        * shared memory map will have been created.
+        */
+       pid = waitpid(pid, &status, 0);
+       if (pid < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+               perror("wait");
+               ret = -1;
+       }
+unmap_stack:
+       ret = munmap(child_stack, CHILD_STACK_SIZE);
+       if (ret < 0) {
+               perror("munmap");
+       }
+close_pipe:
+       close(retval_pipe[0]);
+       close(retval_pipe[1]);
+end:
+       return retval.i;
+}
+
+int mkdir_recursive_run_as(const char *path, mode_t mode, uid_t uid, gid_t gid)
+{
+       struct mkdir_data data;
+
+       DBG3("mkdir() recursive %s with mode %d for uid %d and gid %d",
+                       path, mode, uid, gid);
+       data.path = path;
+       data.mode = mode;
+       return run_as(_mkdir_recursive, &data, uid, gid);
+}
+
+int mkdir_run_as(const char *path, mode_t mode, uid_t uid, gid_t gid)
+{
+       struct mkdir_data data;
+
+       DBG3("mkdir() %s with mode %d for uid %d and gid %d",
+                       path, mode, uid, gid);
+       data.path = path;
+       data.mode = mode;
+       return run_as(_mkdir, &data, uid, gid);
+}
+
+/*
+ * Note: open_run_as is currently not working. We'd need to pass the fd
+ * opened in the child to the parent.
+ */
+int open_run_as(const char *path, int flags, mode_t mode, uid_t uid, gid_t gid)
+{
+       struct open_data data;
+
+       DBG3("open() %s with flags %X mode %d for uid %d and gid %d",
+                       path, flags, mode, uid, gid);
+       data.path = path;
+       data.flags = flags;
+       data.mode = mode;
+       return run_as(_open, &data, uid, gid);
+}
index e4084aed308d7ab1b7ec341a8b5f9955be1ccaba..eb0b3a05a97a4ff8f4356f4f537819bd1796b80c 100644 (file)
@@ -1,4 +1,4 @@
-AC_INIT([lttng-tools],[2.0-pre14],[david.goulet@polymtl.ca],[],[http://lttng.org])
+AC_INIT([lttng-tools],[2.0-pre16],[david.goulet@polymtl.ca],[],[http://lttng.org])
 AC_CONFIG_AUX_DIR([config])
 AC_CANONICAL_TARGET
 AC_CANONICAL_HOST
@@ -63,7 +63,7 @@ AC_CHECK_DECL([caa_get_cycles], [],
 )
 
 # URCU library version needed or newer
-liburcu_version=">= 0.6.6"
+liburcu_version=">= 0.6.7"
 
 # Check liburcu needed function calls
 AC_CHECK_DECL([cds_list_add], [],
@@ -138,12 +138,14 @@ AC_CONFIG_FILES([
        Makefile
        include/Makefile
        benchmark/Makefile
+       common/Makefile
        libkernelctl/Makefile
        liblttng-consumer/Makefile
        liblttng-kconsumer/Makefile
        liblttng-ustconsumer/Makefile
        liblttngctl/Makefile
        liblttng-sessiond-comm/Makefile
+       liblttng-ht/Makefile
        lttng-consumerd/Makefile
        lttng-sessiond/Makefile
        lttng/Makefile
index 3f3fa4a4f883b9dbc3dc847dfba542c4bd5bbbef..f8fa172634dba6581f597c01dacfdd27bb913522 100644 (file)
@@ -1,3 +1,8 @@
+
+******** DEPRECATED **********
+* Kept for historic purposes *
+******** DEPRECATED **********
+
 lttng-tools command line interface
 
 (Note: as of June 8th, 2011, this document is at [RFC] stage.)
index 7078eecc97fb581621b58a250a448ac1f2724c59..f7ec99d373d166743fb39bdb6591c48dafd32c31 100644 (file)
@@ -1,7 +1,10 @@
 NOTES:
 --------------
-2011-07-21 : User-space tracer is not released. Tracing in user-space at this
-point is not possible with lttng-tools.
+
+2011-12-12: For user-space tracing, only the global UST domain ("-u" alone) is
+supported meaning that if you enable a tracepoint for user-space it will be
+enabled for all applications for the current tracing session you are working
+on.
 
 QUICKSTART
 --------------
@@ -13,15 +16,16 @@ trace.
 See the README file for installation procedure or use the various Linux
 distribution packages.
 
-In order to trace the kernel, you'll need the lttng-modules >= 2.0 compiled and
+In order to trace the kernel, you'll need the lttng-modules 2.0 compiled and
 installed. See http://lttng.org/lttng2.0 for more instructions for that part.
-For user-space tracing, you'll need an instrumented application, please see
-http://lttng.org/ust.
+For user-space tracing, you'll need an instrumented application with lttng-ust
+2.0.
 
 lttng-tools provide a session daemon (lttng-sessiond) that acts as a tracing
 registry. To trace any instrumented applications or the kernel, a registered
 tracing session is needed beforehand. To interact with the session daemon and a
-tracing session, you should use the lttng command line UI (lttng).
+tracing session, you should use the lttng command line UI (lttng). It is also
+possible to use the liblttngctl library for tracing control (lttng.h).
 
 Here is a list of some powerful features the LTTng 2.0 kernel tracer offers:
 
@@ -31,23 +35,31 @@ Here is a list of some powerful features the LTTng 2.0 kernel tracer offers:
        * Perf counter support
        * Tracepoint support
 
-The next sections explain how to do tracing :)
+And for the LTTng UST 2.0 tracer:
+
+       * Applications registration
+       * Automatic tracepoints activation upon app. registration
+       * Context information support
+       * Safe buffers after application crash
+       * Per-user tracing (root access *not* mandatory)
+
+The next sections explains how to do tracing :)
 
 Kernel Tracing
 --------------
 
-You can start the session daemon by invoking the command "lttng-sessiond",
-or let the lttng command line tool do it for you. The session daemon
-loads the LTTng tracer modules for you if those modules can be found on
-your system. If they are not found, the kernel tracing feature will be
-unavailable.
+You can start the session daemon by invoking the command "lttng-sessiond", or
+let the lttng command line tool do it for you. The session daemon loads the
+LTTng tracer modules for you if those modules can be found on your system. If
+they are not found, the kernel tracing feature will be unavailable.
 
 List available kernel events:
 
 # lttng list -k
 
-1) Create a tracing session. A .lttngrc will be created in $HOME containing
-the session name (here 'mysession') you are working on.
+1) Create a tracing session. The .lttng directory will be created with .lttngrc
+file in $HOME containing the session name (here 'mysession') you are working
+on.
 
 # lttng create mysession
 
@@ -99,7 +111,7 @@ event. For example, you can add the PID along with the event information:
 # lttng add-context -k -e sched_switch -t pid
 
 At this point, you will have to look at 'lttng add-context --help' for all
-possible context type which are integer values.
+possible context type.
 
 You can on the same line activate multiple context:
 
@@ -122,12 +134,15 @@ You'll have to use the add-context help for all possible perf counter values.
 Tracing is in progress at this point and traces will be written in
 $HOME/lttng-traces/mysession-<date>-<time>
 
+NOTE: It will start tracing for *all* domain(s).
+
 9) Stop tracing:
 
 # lttng stop
 
-Note: At this point, you can restart the trace (lttng start), enable/disable
+NOTE: At this point, you can restart the trace (lttng start), enable/disable
 events or just go take a break and come back 3 days later to start it again :).
+You can also read the trace since the buffers are flushed on stop command.
 
 10) Destroy your session after you are done with tracing
 
@@ -138,21 +153,89 @@ See Reading a trace section below to read you trace(s).
 User-space Tracing
 --------------
 
-User-space tracer 2.0 not released at this point. For the 0.x versions,
-you need to use 'ustctl' to control user-space tracing.
+Like kernel tracing, you can start the session daemon by invoking the command
+"lttng-sessiond", or let the lttng command line tool do it for you.
+
+NOTE: You do *not* need root credentials in order to tracer user-space
+applications. However, if you run the session daemon under non-root user
+rights, only applications of that user will be traced.
+
+So, after instrumenting you applications with LTTng-ust 2.0
+(http://lttng.org/lttng2.0), upon startup, it will automatically register to
+the session daemon. If there is none running, it will simply wait on a seperate
+thread for a session daemon to appear and then register.
+
+Start your instrumented application at any time but at least before starting
+tracing :).
+
+List available registered applications:
+
+$ lttng list -u
+
+1) Create a tracing session. The .lttng directory will be created with a
+.lttngrc file in $HOME containing the session name (here 'mysession') you are
+working on.
+
+$ lttng create mysession
+
+If you have multiple sessions, you can change the current session by using:
+
+$ lttng set-session myothersession
+
+2) Enable all tracepoints for the global UST domain ("-u" alone).
+
+$ lttng enable-event -a -u
+
+or enable a single tracepoint event.
+
+$ lttng enable-event ust_tests_hello:tptest -u
+
+3) This is also a new feature which allows you to add context information to an
+event. For example, you can add the PID along with the event information:
+
+$ lttng add-context -t pid -e ust_tests_hello:tptest -u
+
+At this point, you will have to look at 'lttng add-context --help' for all
+possible context type.
+
+You can on the same line activate multiple context:
+
+$ lttng add-context -u -e ust_tests_hello:tptest -t pid -t nice -t tid
+
+4) Start tracing:
+
+$ lttng start
+
+Tracing is in progress at this point and traces will be written in
+$HOME/lttng-traces/mysession-<date>-<time>/ust/<procname>-<pid>-<date>-<time>
+
+NOTE: It will start tracing for *all* domain(s).
+
+5) Stop tracing:
+
+$ lttng stop
+
+NOTE: At this point, you can restart the trace (lttng start), enable/disable
+events or just go take a break and come back 3 days later to start it again :).
+You can also read the trace since the buffers are flushed on stop command.
+
+6) Destroy your session after you are done with tracing
+
+$ lttng destroy
+
+See "Reading a trace" section below to read you trace(s).
+
 
 Reading a trace
 --------------
 
 The tool "Babeltrace" can be used to dump your binary trace into a
-human-readable text format. Please see
-http://www.efficios.com/babeltrace and git tree
-http://git.efficios.com/?p=babeltrace.git
+human-readable text format. Please see http://www.efficios.com/babeltrace and
+git tree http://git.efficios.com/?p=babeltrace.git
 
 # babeltrace $HOME/lttng-traces/mysession-<date>-<time> | less
 
 Voilà!
 
-Please report any bugs/comments on our mailing list
-(ltt-dev@lists.casi.polymtl.ca) or you can go on our IRC channel at
-irc.oftc.net, channel #lttng
+Please report any bugs/comments on our mailing list (lttng-dev@lists.lttng.org)
+or you can go on our IRC channel at irc.oftc.net, channel #lttng
diff --git a/hashtable/hash.c b/hashtable/hash.c
deleted file mode 100644 (file)
index 12f76d8..0000000
+++ /dev/null
@@ -1,504 +0,0 @@
-/*
- * Copyright (C) - Bob Jenkins, May 2006, Public Domain.
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- * Copyright (C) 2011 -  Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * These are functions for producing 32-bit hashes for hash table lookup.
- * hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() are
- * externally useful functions.  Routines to test the hash are included if
- * SELF_TEST is defined.  You can use this free for any purpose.  It's in the
- * public domain.  It has no warranty.
- *
- * You probably want to use hashlittle().  hashlittle() and hashbig() hash byte
- * arrays.  hashlittle() is is faster than hashbig() on little-endian machines.
- * Intel and AMD are little-endian machines.  On second thought, you probably
- * want hashlittle2(), which is identical to hashlittle() except it returns two
- * 32-bit hashes for the price of one.  You could implement hashbig2() if you
- * wanted but I haven't bothered here.
- *
- * If you want to find a hash of, say, exactly 7 integers, do
- *   a = i1;  b = i2;  c = i3;
- *   mix(a,b,c);
- *   a += i4; b += i5; c += i6;
- *   mix(a,b,c);
- *   a += i7;
- *   final(a,b,c);
- * then use c as the hash value.  If you have a variable length array of
- * 4-byte integers to hash, use hashword().  If you have a byte array (like
- * a character string), use hashlittle().  If you have several byte arrays, or
- * a mix of things, see the comments above hashlittle().
- *
- * Why is this so big?  I read 12 bytes at a time into 3 4-byte integers, then
- * mix those integers.  This is fast (you can do a lot more thorough mixing
- * with 12*3 instructions on 3 integers than you can with 3 instructions on 1
- * byte), but shoehorning those bytes into integers efficiently is messy.
- */
-
-#include <stdio.h>      /* defines printf for tests */
-#include <time.h>       /* defines time_t for timings in the test */
-#include <stdint.h>     /* defines uint32_t etc */
-#include <sys/param.h>  /* attempt to define endianness */
-#include <endian.h>    /* attempt to define endianness */
-#include <string.h>
-#include <assert.h>
-#include <urcu/compiler.h>
-
-/*
- * My best guess at if you are big-endian or little-endian.  This may
- * need adjustment.
- */
-#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \
-     __BYTE_ORDER == __LITTLE_ENDIAN) || \
-    (defined(i386) || defined(__i386__) || defined(__i486__) || \
-     defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL))
-# define HASH_LITTLE_ENDIAN 1
-# define HASH_BIG_ENDIAN 0
-#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \
-       __BYTE_ORDER == __BIG_ENDIAN) || \
-      (defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel))
-# define HASH_LITTLE_ENDIAN 0
-# define HASH_BIG_ENDIAN 1
-#else
-# define HASH_LITTLE_ENDIAN 0
-# define HASH_BIG_ENDIAN 0
-#endif
-
-#define hashsize(n) ((uint32_t)1<<(n))
-#define hashmask(n) (hashsize(n)-1)
-#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
-
-/*
- * mix -- mix 3 32-bit values reversibly.
- *
- * This is reversible, so any information in (a,b,c) before mix() is
- * still in (a,b,c) after mix().
- *
- * If four pairs of (a,b,c) inputs are run through mix(), or through
- * mix() in reverse, there are at least 32 bits of the output that
- * are sometimes the same for one pair and different for another pair.
- * This was tested for:
- * * pairs that differed by one bit, by two bits, in any combination
- *   of top bits of (a,b,c), or in any combination of bottom bits of
- *   (a,b,c).
- * * "differ" is defined as +, -, ^, or ~^.  For + and -, I transformed
- *   the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
- *   is commonly produced by subtraction) look like a single 1-bit
- *   difference.
- * * the base values were pseudorandom, all zero but one bit set, or
- *   all zero plus a counter that starts at zero.
- *
- * Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that
- * satisfy this are
- *     4  6  8 16 19  4
- *     9 15  3 18 27 15
- *    14  9  3  7 17  3
- * Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing
- * for "differ" defined as + with a one-bit base and a two-bit delta.  I
- * used http://burtleburtle.net/bob/hash/avalanche.html to choose
- * the operations, constants, and arrangements of the variables.
- *
- * This does not achieve avalanche.  There are input bits of (a,b,c)
- * that fail to affect some output bits of (a,b,c), especially of a.  The
- * most thoroughly mixed value is c, but it doesn't really even achieve
- * avalanche in c.
- *
- * This allows some parallelism.  Read-after-writes are good at doubling
- * the number of bits affected, so the goal of mixing pulls in the opposite
- * direction as the goal of parallelism.  I did what I could.  Rotates
- * seem to cost as much as shifts on every machine I could lay my hands
- * on, and rotates are much kinder to the top and bottom bits, so I used
- * rotates.
- */
-#define mix(a,b,c) \
-{ \
-  a -= c;  a ^= rot(c, 4);  c += b; \
-  b -= a;  b ^= rot(a, 6);  a += c; \
-  c -= b;  c ^= rot(b, 8);  b += a; \
-  a -= c;  a ^= rot(c,16);  c += b; \
-  b -= a;  b ^= rot(a,19);  a += c; \
-  c -= b;  c ^= rot(b, 4);  b += a; \
-}
-
-/*
- * final -- final mixing of 3 32-bit values (a,b,c) into c
- *
- * Pairs of (a,b,c) values differing in only a few bits will usually
- * produce values of c that look totally different.  This was tested for
- * * pairs that differed by one bit, by two bits, in any combination
- *   of top bits of (a,b,c), or in any combination of bottom bits of
- *   (a,b,c).
- * * "differ" is defined as +, -, ^, or ~^.  For + and -, I transformed
- *   the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
- *   is commonly produced by subtraction) look like a single 1-bit
- *   difference.
- * * the base values were pseudorandom, all zero but one bit set, or
- *   all zero plus a counter that starts at zero.
- *
- * These constants passed:
- *  14 11 25 16 4 14 24
- *  12 14 25 16 4 14 24
- * and these came close:
- *   4  8 15 26 3 22 24
- *  10  8 15 26 3 22 24
- *  11  8 15 26 3 22 24
- */
-#define final(a,b,c) \
-{ \
-  c ^= b; c -= rot(b,14); \
-  a ^= c; a -= rot(c,11); \
-  b ^= a; b -= rot(a,25); \
-  c ^= b; c -= rot(b,16); \
-  a ^= c; a -= rot(c,4);  \
-  b ^= a; b -= rot(a,14); \
-  c ^= b; c -= rot(b,24); \
-}
-
-static __attribute__((unused))
-uint32_t hashword(
-       const uint32_t *k,      /* the key, an array of uint32_t values */
-       size_t length,          /* the length of the key, in uint32_ts */
-       uint32_t initval)       /* the previous hash, or an arbitrary value */
-{
-       uint32_t a, b, c;
-
-       /* Set up the internal state */
-       a = b = c = 0xdeadbeef + (((uint32_t) length) << 2) + initval;
-
-       /*----------------------------------------- handle most of the key */
-       while (length > 3) {
-               a += k[0];
-               b += k[1];
-               c += k[2];
-               mix(a, b, c);
-               length -= 3;
-               k += 3;
-       }
-
-       /*----------------------------------- handle the last 3 uint32_t's */
-       switch (length) {       /* all the case statements fall through */
-       case 3: c += k[2];
-       case 2: b += k[1];
-       case 1: a += k[0];
-               final(a, b, c);
-       case 0:                 /* case 0: nothing left to add */
-               break;
-       }
-       /*---------------------------------------------- report the result */
-       return c;
-}
-
-
-/*
- * hashword2() -- same as hashword(), but take two seeds and return two 32-bit
- * values.  pc and pb must both be nonnull, and *pc and *pb must both be
- * initialized with seeds.  If you pass in (*pb)==0, the output (*pc) will be
- * the same as the return value from hashword().
- */
-static __attribute__((unused))
-void hashword2(const uint32_t *k, size_t length,
-               uint32_t *pc, uint32_t *pb)
-{
-       uint32_t a, b, c;
-
-       /* Set up the internal state */
-       a = b = c = 0xdeadbeef + ((uint32_t) (length << 2)) + *pc;
-       c += *pb;
-
-       while (length > 3) {
-               a += k[0];
-               b += k[1];
-               c += k[2];
-               mix(a, b, c);
-               length -= 3;
-               k += 3;
-       }
-
-       switch (length) {
-       case 3 :
-               c += k[2];
-       case 2 :
-               b += k[1];
-       case 1 :
-               a += k[0];
-               final(a, b, c);
-       case 0:     /* case 0: nothing left to add */
-               break;
-       }
-
-       *pc = c;
-       *pb = b;
-}
-
-/*
- * hashlittle() -- hash a variable-length key into a 32-bit value
- *   k       : the key (the unaligned variable-length array of bytes)
- *   length  : the length of the key, counting by bytes
- *   initval : can be any 4-byte value
- * Returns a 32-bit value.  Every bit of the key affects every bit of
- * the return value.  Two keys differing by one or two bits will have
- * totally different hash values.
- *
- * The best hash table sizes are powers of 2.  There is no need to do
- * mod a prime (mod is sooo slow!).  If you need less than 32 bits,
- * use a bitmask.  For example, if you need only 10 bits, do
- *   h = (h & hashmask(10));
- * In which case, the hash table should have hashsize(10) elements.
- *
- * If you are hashing n strings (uint8_t **)k, do it like this:
- *   for (i=0, h=0; i<n; ++i) h = hashlittle( k[i], len[i], h);
- *
- * By Bob Jenkins, 2006.  bob_jenkins@burtleburtle.net.  You may use this
- * code any way you wish, private, educational, or commercial.  It's free.
- *
- * Use for hash table lookup, or anything where one collision in 2^^32 is
- * acceptable.  Do NOT use for cryptographic purposes.
- */
-
-static uint32_t hashlittle(const void *key, size_t length, uint32_t initval)
-{
-       uint32_t a,b,c;
-       union {
-               const void *ptr;
-               size_t i;
-       } u;     /* needed for Mac Powerbook G4 */
-
-       /* Set up the internal state */
-       a = b = c = 0xdeadbeef + ((uint32_t)length) + initval;
-
-       u.ptr = key;
-       if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
-               const uint32_t *k = (const uint32_t *)key;         /* read 32-bit chunks */
-
-               /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
-               while (length > 12) {
-                       a += k[0];
-                       b += k[1];
-                       c += k[2];
-                       mix(a,b,c);
-                       length -= 12;
-                       k += 3;
-               }
-
-               /*
-                * "k[2]&0xffffff" actually reads beyond the end of the string, but
-                * then masks off the part it's not allowed to read.  Because the
-                * string is aligned, the masked-off tail is in the same word as the
-                * rest of the string.  Every machine with memory protection I've seen
-                * does it on word boundaries, so is OK with this.  But VALGRIND will
-                * still catch it and complain.  The masking trick does make the hash
-                * noticably faster for short strings (like English words).
-                */
-#ifndef VALGRIND
-
-               switch (length) {
-               case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
-               case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break;
-               case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break;
-               case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break;
-               case 8 : b+=k[1]; a+=k[0]; break;
-               case 7 : b+=k[1]&0xffffff; a+=k[0]; break;
-               case 6 : b+=k[1]&0xffff; a+=k[0]; break;
-               case 5 : b+=k[1]&0xff; a+=k[0]; break;
-               case 4 : a+=k[0]; break;
-               case 3 : a+=k[0]&0xffffff; break;
-               case 2 : a+=k[0]&0xffff; break;
-               case 1 : a+=k[0]&0xff; break;
-               case 0 : return c;              /* zero length strings require no mixing */
-               }
-#else /* make valgrind happy */
-               const uint8_t *k8;
-
-               k8 = (const uint8_t *)k;
-               switch (length) {
-               case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
-               case 11: c+=((uint32_t)k8[10])<<16;  /* fall through */
-               case 10: c+=((uint32_t)k8[9])<<8;    /* fall through */
-               case 9 : c+=k8[8];                   /* fall through */
-               case 8 : b+=k[1]; a+=k[0]; break;
-               case 7 : b+=((uint32_t)k8[6])<<16;   /* fall through */
-               case 6 : b+=((uint32_t)k8[5])<<8;    /* fall through */
-               case 5 : b+=k8[4];                   /* fall through */
-               case 4 : a+=k[0]; break;
-               case 3 : a+=((uint32_t)k8[2])<<16;   /* fall through */
-               case 2 : a+=((uint32_t)k8[1])<<8;    /* fall through */
-               case 1 : a+=k8[0]; break;
-               case 0 : return c;
-               }
-#endif /* !valgrind */
-       } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
-               const uint16_t *k = (const uint16_t *)key;         /* read 16-bit chunks */
-               const uint8_t *k8;
-
-               /*--------------- all but last block: aligned reads and different mixing */
-               while (length > 12) {
-                       a += k[0] + (((uint32_t)k[1])<<16);
-                       b += k[2] + (((uint32_t)k[3])<<16);
-                       c += k[4] + (((uint32_t)k[5])<<16);
-                       mix(a,b,c);
-                       length -= 12;
-                       k += 6;
-               }
-
-               k8 = (const uint8_t *)k;
-               switch (length) {
-               case 12:
-                       c+=k[4]+(((uint32_t)k[5])<<16);
-                       b+=k[2]+(((uint32_t)k[3])<<16);
-                       a+=k[0]+(((uint32_t)k[1])<<16);
-                       break;
-               case 11:
-                       c+=((uint32_t)k8[10])<<16;     /* fall through */
-               case 10:
-                       c+=k[4];
-                       b+=k[2]+(((uint32_t)k[3])<<16);
-                       a+=k[0]+(((uint32_t)k[1])<<16);
-                       break;
-               case 9:
-                       c+=k8[8];                      /* fall through */
-               case 8:
-                       b+=k[2]+(((uint32_t)k[3])<<16);
-                       a+=k[0]+(((uint32_t)k[1])<<16);
-                       break;
-               case 7:
-                       b+=((uint32_t)k8[6])<<16;      /* fall through */
-               case 6:
-                       b+=k[2];
-                       a+=k[0]+(((uint32_t)k[1])<<16);
-                       break;
-               case 5:
-                       b+=k8[4];                      /* fall through */
-               case 4:
-                       a+=k[0]+(((uint32_t)k[1])<<16);
-                       break;
-               case 3:
-                       a+=((uint32_t)k8[2])<<16;      /* fall through */
-               case 2:
-                       a+=k[0];
-                       break;
-               case 1:
-                       a+=k8[0];
-                       break;
-               case 0:
-                       return c;   /* zero length requires no mixing */
-               }
-
-       } else {    /* need to read the key one byte at a time */
-               const uint8_t *k = (const uint8_t *)key;
-
-               while (length > 12) {
-                       a += k[0];
-                       a += ((uint32_t)k[1])<<8;
-                       a += ((uint32_t)k[2])<<16;
-                       a += ((uint32_t)k[3])<<24;
-                       b += k[4];
-                       b += ((uint32_t)k[5])<<8;
-                       b += ((uint32_t)k[6])<<16;
-                       b += ((uint32_t)k[7])<<24;
-                       c += k[8];
-                       c += ((uint32_t)k[9])<<8;
-                       c += ((uint32_t)k[10])<<16;
-                       c += ((uint32_t)k[11])<<24;
-                       mix(a,b,c);
-                       length -= 12;
-                       k += 12;
-               }
-
-               switch(length) {                  /* all the case statements fall through */
-               case 12: c+=((uint32_t)k[11])<<24;
-               case 11: c+=((uint32_t)k[10])<<16;
-               case 10: c+=((uint32_t)k[9])<<8;
-               case 9: c+=k[8];
-               case 8: b+=((uint32_t)k[7])<<24;
-               case 7: b+=((uint32_t)k[6])<<16;
-               case 6: b+=((uint32_t)k[5])<<8;
-               case 5: b+=k[4];
-               case 4: a+=((uint32_t)k[3])<<24;
-               case 3: a+=((uint32_t)k[2])<<16;
-               case 2: a+=((uint32_t)k[1])<<8;
-               case 1:
-                       a+=k[0];
-                       break;
-               case 0:
-                       return c;
-               }
-       }
-
-       final(a,b,c);
-       return c;
-}
-
-#if (CAA_BITS_PER_LONG == 64)
-/*
- * Hash function for number value.
- */
-unsigned long hash_key(void *_key, size_t length, unsigned long seed)
-{
-       union {
-               uint64_t v64;
-               uint32_t v32[2];
-       } v;
-       union {
-               uint64_t v64;
-               uint32_t v32[2];
-       } key;
-
-       assert(length == sizeof(unsigned long));
-       v.v64 = (uint64_t) seed;
-       key.v64 = (uint64_t) _key;
-       hashword2(key.v32, 2, &v.v32[0], &v.v32[1]);
-       return v.v64;
-}
-#else
-/*
- * Hash function for number value.
- */
-unsigned long hash_key(void *_key, size_t length, unsigned long seed)
-{
-       uint32_t key = (uint32_t) _key;
-
-       assert(length == sizeof(uint32_t));
-       return hashword(&key, 1, seed);
-}
-#endif
-
-/*
- * Hash function for string.
- */
-unsigned long hash_key_str(void *key, size_t length, unsigned long seed)
-{
-       return hashlittle(key, length, seed);
-}
-
-/*
- * Hash function compare for number value.
- */
-unsigned long hash_compare_key(void *key1, size_t key1_len,
-               void *key2, size_t key2_len)
-{
-       if (key1_len != key2_len) {
-               return -1;
-       }
-
-       if (key1 == key2) {
-               return 0;
-       }
-
-       return 1;
-}
-
-/*
- * Hash compare function for string.
- */
-unsigned long hash_compare_key_str(void *key1, size_t key1_len,
-               void *key2, size_t key2_len)
-{
-       if (key1_len != key2_len) {
-               return -1;
-       }
-
-       if (strncmp(key1, key2, key1_len) == 0) {
-               return 0;
-       }
-
-       return 1;
-}
diff --git a/hashtable/hash.h b/hashtable/hash.h
deleted file mode 100644 (file)
index 796920e..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; only version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
- * Place - Suite 330, Boston, MA  02111-1307, USA.
- */
-
-#ifndef _LTT_HASH_H
-#define _LTT_HASH_H
-
-unsigned long hash_key(void *_key, size_t length, unsigned long seed);
-
-unsigned long hash_key_str(void *_key, size_t length, unsigned long seed);
-
-unsigned long hash_compare_key(void *key1, size_t key1_len,
-               void *key2, size_t key2_len);
-
-unsigned long hash_compare_key_str(void *key1, size_t key1_len,
-               void *key2, size_t key2_len);
-
-#endif /* _LTT_HASH_H */
diff --git a/hashtable/rculfhash.c b/hashtable/rculfhash.c
deleted file mode 100644 (file)
index 2e83153..0000000
+++ /dev/null
@@ -1,1846 +0,0 @@
-/*
- * rculfhash.c
- *
- * Userspace RCU library - Lock-Free Resizable RCU Hash Table
- *
- * Copyright 2010-2011 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-/*
- * Based on the following articles:
- * - Ori Shalev and Nir Shavit. Split-ordered lists: Lock-free
- *   extensible hash tables. J. ACM 53, 3 (May 2006), 379-405.
- * - Michael, M. M. High performance dynamic lock-free hash tables
- *   and list-based sets. In Proceedings of the fourteenth annual ACM
- *   symposium on Parallel algorithms and architectures, ACM Press,
- *   (2002), 73-82.
- *
- * Some specificities of this Lock-Free Resizable RCU Hash Table
- * implementation:
- *
- * - RCU read-side critical section allows readers to perform hash
- *   table lookups and use the returned objects safely by delaying
- *   memory reclaim of a grace period.
- * - Add and remove operations are lock-free, and do not need to
- *   allocate memory. They need to be executed within RCU read-side
- *   critical section to ensure the objects they read are valid and to
- *   deal with the cmpxchg ABA problem.
- * - add and add_unique operations are supported. add_unique checks if
- *   the node key already exists in the hash table. It ensures no key
- *   duplicata exists.
- * - The resize operation executes concurrently with add/remove/lookup.
- * - Hash table nodes are contained within a split-ordered list. This
- *   list is ordered by incrementing reversed-bits-hash value.
- * - An index of dummy nodes is kept. These dummy nodes are the hash
- *   table "buckets", and they are also chained together in the
- *   split-ordered list, which allows recursive expansion.
- * - The resize operation for small tables only allows expanding the hash table.
- *   It is triggered automatically by detecting long chains in the add
- *   operation.
- * - The resize operation for larger tables (and available through an
- *   API) allows both expanding and shrinking the hash table.
- * - Split-counters are used to keep track of the number of
- *   nodes within the hash table for automatic resize triggering.
- * - Resize operation initiated by long chain detection is executed by a
- *   call_rcu thread, which keeps lock-freedom of add and remove.
- * - Resize operations are protected by a mutex.
- * - The removal operation is split in two parts: first, a "removed"
- *   flag is set in the next pointer within the node to remove. Then,
- *   a "garbage collection" is performed in the bucket containing the
- *   removed node (from the start of the bucket up to the removed node).
- *   All encountered nodes with "removed" flag set in their next
- *   pointers are removed from the linked-list. If the cmpxchg used for
- *   removal fails (due to concurrent garbage-collection or concurrent
- *   add), we retry from the beginning of the bucket. This ensures that
- *   the node with "removed" flag set is removed from the hash table
- *   (not visible to lookups anymore) before the RCU read-side critical
- *   section held across removal ends. Furthermore, this ensures that
- *   the node with "removed" flag set is removed from the linked-list
- *   before its memory is reclaimed. Only the thread which removal
- *   successfully set the "removed" flag (with a cmpxchg) into a node's
- *   next pointer is considered to have succeeded its removal (and thus
- *   owns the node to reclaim). Because we garbage-collect starting from
- *   an invariant node (the start-of-bucket dummy node) up to the
- *   "removed" node (or find a reverse-hash that is higher), we are sure
- *   that a successful traversal of the chain leads to a chain that is
- *   present in the linked-list (the start node is never removed) and
- *   that is does not contain the "removed" node anymore, even if
- *   concurrent delete/add operations are changing the structure of the
- *   list concurrently.
- * - The add operation performs gargage collection of buckets if it
- *   encounters nodes with removed flag set in the bucket where it wants
- *   to add its new node. This ensures lock-freedom of add operation by
- *   helping the remover unlink nodes from the list rather than to wait
- *   for it do to so.
- * - A RCU "order table" indexed by log2(hash index) is copied and
- *   expanded by the resize operation. This order table allows finding
- *   the "dummy node" tables.
- * - There is one dummy node table per hash index order. The size of
- *   each dummy node table is half the number of hashes contained in
- *   this order (except for order 0).
- * - synchronzie_rcu is used to garbage-collect the old dummy node table.
- * - The per-order dummy node tables contain a compact version of the
- *   hash table nodes. These tables are invariant after they are
- *   populated into the hash table.
- *
- * Dummy node tables:
- *
- * hash table  hash table      the last        all dummy node tables
- * order       size            dummy node      0   1   2   3   4   5   6(index)
- *                             table size
- * 0           1               1               1
- * 1           2               1               1   1
- * 2           4               2               1   1   2
- * 3           8               4               1   1   2   4
- * 4           16              8               1   1   2   4   8
- * 5           32              16              1   1   2   4   8  16
- * 6           64              32              1   1   2   4   8  16  32
- *
- * When growing/shrinking, we only focus on the last dummy node table
- * which size is (!order ? 1 : (1 << (order -1))).
- *
- * Example for growing/shrinking:
- * grow hash table from order 5 to 6: init the index=6 dummy node table
- * shrink hash table from order 6 to 5: fini the index=6 dummy node table
- *
- * A bit of ascii art explanation:
- * 
- * Order index is the off-by-one compare to the actual power of 2 because 
- * we use index 0 to deal with the 0 special-case.
- * 
- * This shows the nodes for a small table ordered by reversed bits:
- * 
- *    bits   reverse
- * 0  000        000
- * 4  100        001
- * 2  010        010
- * 6  110        011
- * 1  001        100
- * 5  101        101
- * 3  011        110
- * 7  111        111
- * 
- * This shows the nodes in order of non-reversed bits, linked by 
- * reversed-bit order.
- * 
- * order              bits       reverse
- * 0               0  000        000
- * 1               |  1  001        100             <-
- * 2               |  |  2  010        010    <-     |
- *                 |  |  |  3  011        110  | <-  |
- * 3               -> |  |  |  4  100        001  |  |
- *                    -> |  |     5  101        101  |
- *                       -> |        6  110        011
- *                          ->          7  111        111
- */
-
-#define _LGPL_SOURCE
-#include <stdlib.h>
-#include <errno.h>
-#include <assert.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
-
-#include "config.h"
-#include <urcu.h>
-#include <urcu-call-rcu.h>
-#include <urcu/arch.h>
-#include <urcu/uatomic.h>
-#include <urcu/compiler.h>
-#include <stdio.h>
-#include <pthread.h>
-
-#include "rculfhash.h"
-
-#ifdef DEBUG
-#define dbg_printf(fmt, args...)     printf("[debug rculfhash] " fmt, ## args)
-#else
-#define dbg_printf(fmt, args...)
-#endif
-
-/*
- * Split-counters lazily update the global counter each 1024
- * addition/removal. It automatically keeps track of resize required.
- * We use the bucket length as indicator for need to expand for small
- * tables and machines lacking per-cpu data suppport.
- */
-#define COUNT_COMMIT_ORDER             10
-#define DEFAULT_SPLIT_COUNT_MASK       0xFUL
-#define CHAIN_LEN_TARGET               1
-#define CHAIN_LEN_RESIZE_THRESHOLD     3
-
-/*
- * Define the minimum table size.
- */
-#define MIN_TABLE_SIZE                 1
-
-#if (CAA_BITS_PER_LONG == 32)
-#define MAX_TABLE_ORDER                        32
-#else
-#define MAX_TABLE_ORDER                        64
-#endif
-
-/*
- * Minimum number of dummy nodes to touch per thread to parallelize grow/shrink.
- */
-#define MIN_PARTITION_PER_THREAD_ORDER 12
-#define MIN_PARTITION_PER_THREAD       (1UL << MIN_PARTITION_PER_THREAD_ORDER)
-
-#ifndef min
-#define min(a, b)      ((a) < (b) ? (a) : (b))
-#endif
-
-#ifndef max
-#define max(a, b)      ((a) > (b) ? (a) : (b))
-#endif
-
-/*
- * The removed flag needs to be updated atomically with the pointer.
- * It indicates that no node must attach to the node scheduled for
- * removal, and that node garbage collection must be performed.
- * The dummy flag does not require to be updated atomically with the
- * pointer, but it is added as a pointer low bit flag to save space.
- */
-#define REMOVED_FLAG           (1UL << 0)
-#define DUMMY_FLAG             (1UL << 1)
-#define FLAGS_MASK             ((1UL << 2) - 1)
-
-/* Value of the end pointer. Should not interact with flags. */
-#define END_VALUE              NULL
-
-/*
- * ht_items_count: Split-counters counting the number of node addition
- * and removal in the table. Only used if the CDS_LFHT_ACCOUNTING flag
- * is set at hash table creation.
- *
- * These are free-running counters, never reset to zero. They count the
- * number of add/remove, and trigger every (1 << COUNT_COMMIT_ORDER)
- * operations to update the global counter. We choose a power-of-2 value
- * for the trigger to deal with 32 or 64-bit overflow of the counter.
- */
-struct ht_items_count {
-       unsigned long add, del;
-} __attribute__((aligned(CAA_CACHE_LINE_SIZE)));
-
-/*
- * rcu_level: Contains the per order-index-level dummy node table. The
- * size of each dummy node table is half the number of hashes contained
- * in this order (except for order 0). The minimum allocation size
- * parameter allows combining the dummy node arrays of the lowermost
- * levels to improve cache locality for small index orders.
- */
-struct rcu_level {
-       /* Note: manually update allocation length when adding a field */
-       struct _cds_lfht_node nodes[0];
-};
-
-/*
- * rcu_table: Contains the size and desired new size if a resize
- * operation is in progress, as well as the statically-sized array of
- * rcu_level pointers.
- */
-struct rcu_table {
-       unsigned long size;     /* always a power of 2, shared (RCU) */
-       unsigned long resize_target;
-       int resize_initiated;
-       struct rcu_level *tbl[MAX_TABLE_ORDER];
-};
-
-/*
- * cds_lfht: Top-level data structure representing a lock-free hash
- * table. Defined in the implementation file to make it be an opaque
- * cookie to users.
- */
-struct cds_lfht {
-       struct rcu_table t;
-       cds_lfht_hash_fct hash_fct;
-       cds_lfht_compare_fct compare_fct;
-       unsigned long min_alloc_order;
-       unsigned long min_alloc_size;
-       unsigned long hash_seed;
-       int flags;
-       /*
-        * We need to put the work threads offline (QSBR) when taking this
-        * mutex, because we use synchronize_rcu within this mutex critical
-        * section, which waits on read-side critical sections, and could
-        * therefore cause grace-period deadlock if we hold off RCU G.P.
-        * completion.
-        */
-       pthread_mutex_t resize_mutex;   /* resize mutex: add/del mutex */
-       unsigned int in_progress_resize, in_progress_destroy;
-       void (*cds_lfht_call_rcu)(struct rcu_head *head,
-                     void (*func)(struct rcu_head *head));
-       void (*cds_lfht_synchronize_rcu)(void);
-       void (*cds_lfht_rcu_read_lock)(void);
-       void (*cds_lfht_rcu_read_unlock)(void);
-       void (*cds_lfht_rcu_thread_offline)(void);
-       void (*cds_lfht_rcu_thread_online)(void);
-       void (*cds_lfht_rcu_register_thread)(void);
-       void (*cds_lfht_rcu_unregister_thread)(void);
-       pthread_attr_t *resize_attr;    /* Resize threads attributes */
-       long count;                     /* global approximate item count */
-       struct ht_items_count *split_count;     /* split item count */
-};
-
-/*
- * rcu_resize_work: Contains arguments passed to RCU worker thread
- * responsible for performing lazy resize.
- */
-struct rcu_resize_work {
-       struct rcu_head head;
-       struct cds_lfht *ht;
-};
-
-/*
- * partition_resize_work: Contains arguments passed to worker threads
- * executing the hash table resize on partitions of the hash table
- * assigned to each processor's worker thread.
- */
-struct partition_resize_work {
-       pthread_t thread_id;
-       struct cds_lfht *ht;
-       unsigned long i, start, len;
-       void (*fct)(struct cds_lfht *ht, unsigned long i,
-                   unsigned long start, unsigned long len);
-};
-
-static
-void _cds_lfht_add(struct cds_lfht *ht,
-               unsigned long size,
-               struct cds_lfht_node *node,
-               struct cds_lfht_iter *unique_ret,
-               int dummy);
-
-/*
- * Algorithm to reverse bits in a word by lookup table, extended to
- * 64-bit words.
- * Source:
- * http://graphics.stanford.edu/~seander/bithacks.html#BitReverseTable
- * Originally from Public Domain.
- */
-
-static const uint8_t BitReverseTable256[256] = 
-{
-#define R2(n) (n),   (n) + 2*64,     (n) + 1*64,     (n) + 3*64
-#define R4(n) R2(n), R2((n) + 2*16), R2((n) + 1*16), R2((n) + 3*16)
-#define R6(n) R4(n), R4((n) + 2*4 ), R4((n) + 1*4 ), R4((n) + 3*4 )
-       R6(0), R6(2), R6(1), R6(3)
-};
-#undef R2
-#undef R4
-#undef R6
-
-static
-uint8_t bit_reverse_u8(uint8_t v)
-{
-       return BitReverseTable256[v];
-}
-
-static __attribute__((unused))
-uint32_t bit_reverse_u32(uint32_t v)
-{
-       return ((uint32_t) bit_reverse_u8(v) << 24) | 
-               ((uint32_t) bit_reverse_u8(v >> 8) << 16) | 
-               ((uint32_t) bit_reverse_u8(v >> 16) << 8) | 
-               ((uint32_t) bit_reverse_u8(v >> 24));
-}
-
-static __attribute__((unused))
-uint64_t bit_reverse_u64(uint64_t v)
-{
-       return ((uint64_t) bit_reverse_u8(v) << 56) | 
-               ((uint64_t) bit_reverse_u8(v >> 8)  << 48) | 
-               ((uint64_t) bit_reverse_u8(v >> 16) << 40) |
-               ((uint64_t) bit_reverse_u8(v >> 24) << 32) |
-               ((uint64_t) bit_reverse_u8(v >> 32) << 24) | 
-               ((uint64_t) bit_reverse_u8(v >> 40) << 16) | 
-               ((uint64_t) bit_reverse_u8(v >> 48) << 8) |
-               ((uint64_t) bit_reverse_u8(v >> 56));
-}
-
-static
-unsigned long bit_reverse_ulong(unsigned long v)
-{
-#if (CAA_BITS_PER_LONG == 32)
-       return bit_reverse_u32(v);
-#else
-       return bit_reverse_u64(v);
-#endif
-}
-
-/*
- * fls: returns the position of the most significant bit.
- * Returns 0 if no bit is set, else returns the position of the most
- * significant bit (from 1 to 32 on 32-bit, from 1 to 64 on 64-bit).
- */
-#if defined(__i386) || defined(__x86_64)
-static inline
-unsigned int fls_u32(uint32_t x)
-{
-       int r;
-
-       asm("bsrl %1,%0\n\t"
-           "jnz 1f\n\t"
-           "movl $-1,%0\n\t"
-           "1:\n\t"
-           : "=r" (r) : "rm" (x));
-       return r + 1;
-}
-#define HAS_FLS_U32
-#endif
-
-#if defined(__x86_64)
-static inline
-unsigned int fls_u64(uint64_t x)
-{
-       long r;
-
-       asm("bsrq %1,%0\n\t"
-           "jnz 1f\n\t"
-           "movq $-1,%0\n\t"
-           "1:\n\t"
-           : "=r" (r) : "rm" (x));
-       return r + 1;
-}
-#define HAS_FLS_U64
-#endif
-
-#ifndef HAS_FLS_U64
-static __attribute__((unused))
-unsigned int fls_u64(uint64_t x)
-{
-       unsigned int r = 64;
-
-       if (!x)
-               return 0;
-
-       if (!(x & 0xFFFFFFFF00000000ULL)) {
-               x <<= 32;
-               r -= 32;
-       }
-       if (!(x & 0xFFFF000000000000ULL)) {
-               x <<= 16;
-               r -= 16;
-       }
-       if (!(x & 0xFF00000000000000ULL)) {
-               x <<= 8;
-               r -= 8;
-       }
-       if (!(x & 0xF000000000000000ULL)) {
-               x <<= 4;
-               r -= 4;
-       }
-       if (!(x & 0xC000000000000000ULL)) {
-               x <<= 2;
-               r -= 2;
-       }
-       if (!(x & 0x8000000000000000ULL)) {
-               x <<= 1;
-               r -= 1;
-       }
-       return r;
-}
-#endif
-
-#ifndef HAS_FLS_U32
-static __attribute__((unused))
-unsigned int fls_u32(uint32_t x)
-{
-       unsigned int r = 32;
-
-       if (!x)
-               return 0;
-       if (!(x & 0xFFFF0000U)) {
-               x <<= 16;
-               r -= 16;
-       }
-       if (!(x & 0xFF000000U)) {
-               x <<= 8;
-               r -= 8;
-       }
-       if (!(x & 0xF0000000U)) {
-               x <<= 4;
-               r -= 4;
-       }
-       if (!(x & 0xC0000000U)) {
-               x <<= 2;
-               r -= 2;
-       }
-       if (!(x & 0x80000000U)) {
-               x <<= 1;
-               r -= 1;
-       }
-       return r;
-}
-#endif
-
-unsigned int fls_ulong(unsigned long x)
-{
-#if (CAA_BITS_PER_LONG == 32)
-       return fls_u32(x);
-#else
-       return fls_u64(x);
-#endif
-}
-
-/*
- * Return the minimum order for which x <= (1UL << order).
- * Return -1 if x is 0.
- */
-int get_count_order_u32(uint32_t x)
-{
-       if (!x)
-               return -1;
-
-       return fls_u32(x - 1);
-}
-
-/*
- * Return the minimum order for which x <= (1UL << order).
- * Return -1 if x is 0.
- */
-int get_count_order_ulong(unsigned long x)
-{
-       if (!x)
-               return -1;
-
-       return fls_ulong(x - 1);
-}
-
-#ifdef POISON_FREE
-#define poison_free(ptr)                                       \
-       do {                                                    \
-               if (ptr) {                                      \
-                       memset(ptr, 0x42, sizeof(*(ptr)));      \
-                       free(ptr);                              \
-               }                                               \
-       } while (0)
-#else
-#define poison_free(ptr)       free(ptr)
-#endif
-
-static
-void cds_lfht_resize_lazy(struct cds_lfht *ht, unsigned long size, int growth);
-
-static
-void cds_lfht_resize_lazy_count(struct cds_lfht *ht, unsigned long size,
-                               unsigned long count);
-
-static long nr_cpus_mask = -1;
-static long split_count_mask = -1;
-
-#if defined(HAVE_SYSCONF)
-static void ht_init_nr_cpus_mask(void)
-{
-       long maxcpus;
-
-       maxcpus = sysconf(_SC_NPROCESSORS_CONF);
-       if (maxcpus <= 0) {
-               nr_cpus_mask = -2;
-               return;
-       }
-       /*
-        * round up number of CPUs to next power of two, so we
-        * can use & for modulo.
-        */
-       maxcpus = 1UL << get_count_order_ulong(maxcpus);
-       nr_cpus_mask = maxcpus - 1;
-}
-#else /* #if defined(HAVE_SYSCONF) */
-static void ht_init_nr_cpus_mask(void)
-{
-       nr_cpus_mask = -2;
-}
-#endif /* #else #if defined(HAVE_SYSCONF) */
-
-static
-void alloc_split_items_count(struct cds_lfht *ht)
-{
-       struct ht_items_count *count;
-
-       if (nr_cpus_mask == -1) {
-               ht_init_nr_cpus_mask();
-               if (nr_cpus_mask < 0)
-                       split_count_mask = DEFAULT_SPLIT_COUNT_MASK;
-               else
-                       split_count_mask = nr_cpus_mask;
-       }
-
-       assert(split_count_mask >= 0);
-
-       if (ht->flags & CDS_LFHT_ACCOUNTING) {
-               ht->split_count = calloc(split_count_mask + 1, sizeof(*count));
-               assert(ht->split_count);
-       } else {
-               ht->split_count = NULL;
-       }
-}
-
-static
-void free_split_items_count(struct cds_lfht *ht)
-{
-       poison_free(ht->split_count);
-}
-
-#if defined(HAVE_SCHED_GETCPU)
-static
-int ht_get_split_count_index(unsigned long hash)
-{
-       int cpu;
-
-       assert(split_count_mask >= 0);
-       cpu = sched_getcpu();
-       if (caa_unlikely(cpu < 0))
-               return hash & split_count_mask;
-       else
-               return cpu & split_count_mask;
-}
-#else /* #if defined(HAVE_SCHED_GETCPU) */
-static
-int ht_get_split_count_index(unsigned long hash)
-{
-       return hash & split_count_mask;
-}
-#endif /* #else #if defined(HAVE_SCHED_GETCPU) */
-
-static
-void ht_count_add(struct cds_lfht *ht, unsigned long size, unsigned long hash)
-{
-       unsigned long split_count;
-       int index;
-
-       if (caa_unlikely(!ht->split_count))
-               return;
-       index = ht_get_split_count_index(hash);
-       split_count = uatomic_add_return(&ht->split_count[index].add, 1);
-       if (caa_unlikely(!(split_count & ((1UL << COUNT_COMMIT_ORDER) - 1)))) {
-               long count;
-
-               dbg_printf("add split count %lu\n", split_count);
-               count = uatomic_add_return(&ht->count,
-                                          1UL << COUNT_COMMIT_ORDER);
-               /* If power of 2 */
-               if (!(count & (count - 1))) {
-                       if ((count >> CHAIN_LEN_RESIZE_THRESHOLD) < size)
-                               return;
-                       dbg_printf("add set global %ld\n", count);
-                       cds_lfht_resize_lazy_count(ht, size,
-                               count >> (CHAIN_LEN_TARGET - 1));
-               }
-       }
-}
-
-static
-void ht_count_del(struct cds_lfht *ht, unsigned long size, unsigned long hash)
-{
-       unsigned long split_count;
-       int index;
-
-       if (caa_unlikely(!ht->split_count))
-               return;
-       index = ht_get_split_count_index(hash);
-       split_count = uatomic_add_return(&ht->split_count[index].del, 1);
-       if (caa_unlikely(!(split_count & ((1UL << COUNT_COMMIT_ORDER) - 1)))) {
-               long count;
-
-               dbg_printf("del split count %lu\n", split_count);
-               count = uatomic_add_return(&ht->count,
-                                          -(1UL << COUNT_COMMIT_ORDER));
-               /* If power of 2 */
-               if (!(count & (count - 1))) {
-                       if ((count >> CHAIN_LEN_RESIZE_THRESHOLD) >= size)
-                               return;
-                       dbg_printf("del set global %ld\n", count);
-                       /*
-                        * Don't shrink table if the number of nodes is below a
-                        * certain threshold.
-                        */
-                       if (count < (1UL << COUNT_COMMIT_ORDER) * (split_count_mask + 1))
-                               return;
-                       cds_lfht_resize_lazy_count(ht, size,
-                               count >> (CHAIN_LEN_TARGET - 1));
-               }
-       }
-}
-
-static
-void check_resize(struct cds_lfht *ht, unsigned long size, uint32_t chain_len)
-{
-       unsigned long count;
-
-       if (!(ht->flags & CDS_LFHT_AUTO_RESIZE))
-               return;
-       count = uatomic_read(&ht->count);
-       /*
-        * Use bucket-local length for small table expand and for
-        * environments lacking per-cpu data support.
-        */
-       if (count >= (1UL << COUNT_COMMIT_ORDER))
-               return;
-       if (chain_len > 100)
-               dbg_printf("WARNING: large chain length: %u.\n",
-                          chain_len);
-       if (chain_len >= CHAIN_LEN_RESIZE_THRESHOLD)
-               cds_lfht_resize_lazy(ht, size,
-                       get_count_order_u32(chain_len - (CHAIN_LEN_TARGET - 1)));
-}
-
-static
-struct cds_lfht_node *clear_flag(struct cds_lfht_node *node)
-{
-       return (struct cds_lfht_node *) (((unsigned long) node) & ~FLAGS_MASK);
-}
-
-static
-int is_removed(struct cds_lfht_node *node)
-{
-       return ((unsigned long) node) & REMOVED_FLAG;
-}
-
-static
-struct cds_lfht_node *flag_removed(struct cds_lfht_node *node)
-{
-       return (struct cds_lfht_node *) (((unsigned long) node) | REMOVED_FLAG);
-}
-
-static
-int is_dummy(struct cds_lfht_node *node)
-{
-       return ((unsigned long) node) & DUMMY_FLAG;
-}
-
-static
-struct cds_lfht_node *flag_dummy(struct cds_lfht_node *node)
-{
-       return (struct cds_lfht_node *) (((unsigned long) node) | DUMMY_FLAG);
-}
-
-static
-struct cds_lfht_node *get_end(void)
-{
-       return (struct cds_lfht_node *) END_VALUE;
-}
-
-static
-int is_end(struct cds_lfht_node *node)
-{
-       return clear_flag(node) == (struct cds_lfht_node *) END_VALUE;
-}
-
-static
-unsigned long _uatomic_max(unsigned long *ptr, unsigned long v)
-{
-       unsigned long old1, old2;
-
-       old1 = uatomic_read(ptr);
-       do {
-               old2 = old1;
-               if (old2 >= v)
-                       return old2;
-       } while ((old1 = uatomic_cmpxchg(ptr, old2, v)) != old2);
-       return v;
-}
-
-static
-struct _cds_lfht_node *lookup_bucket(struct cds_lfht *ht, unsigned long size,
-               unsigned long hash)
-{
-       unsigned long index, order;
-
-       assert(size > 0);
-       index = hash & (size - 1);
-
-       if (index < ht->min_alloc_size) {
-               dbg_printf("lookup hash %lu index %lu order 0 aridx 0\n",
-                          hash, index);
-               return &ht->t.tbl[0]->nodes[index];
-       }
-       /*
-        * equivalent to get_count_order_ulong(index + 1), but optimizes
-        * away the non-existing 0 special-case for
-        * get_count_order_ulong.
-        */
-       order = fls_ulong(index);
-       dbg_printf("lookup hash %lu index %lu order %lu aridx %lu\n",
-                  hash, index, order, index & ((1UL << (order - 1)) - 1));
-       return &ht->t.tbl[order]->nodes[index & ((1UL << (order - 1)) - 1)];
-}
-
-/*
- * Remove all logically deleted nodes from a bucket up to a certain node key.
- */
-static
-void _cds_lfht_gc_bucket(struct cds_lfht_node *dummy, struct cds_lfht_node *node)
-{
-       struct cds_lfht_node *iter_prev, *iter, *next, *new_next;
-
-       assert(!is_dummy(dummy));
-       assert(!is_removed(dummy));
-       assert(!is_dummy(node));
-       assert(!is_removed(node));
-       for (;;) {
-               iter_prev = dummy;
-               /* We can always skip the dummy node initially */
-               iter = rcu_dereference(iter_prev->p.next);
-               assert(!is_removed(iter));
-               assert(iter_prev->p.reverse_hash <= node->p.reverse_hash);
-               /*
-                * We should never be called with dummy (start of chain)
-                * and logically removed node (end of path compression
-                * marker) being the actual same node. This would be a
-                * bug in the algorithm implementation.
-                */
-               assert(dummy != node);
-               for (;;) {
-                       if (caa_unlikely(is_end(iter)))
-                               return;
-                       if (caa_likely(clear_flag(iter)->p.reverse_hash > node->p.reverse_hash))
-                               return;
-                       next = rcu_dereference(clear_flag(iter)->p.next);
-                       if (caa_likely(is_removed(next)))
-                               break;
-                       iter_prev = clear_flag(iter);
-                       iter = next;
-               }
-               assert(!is_removed(iter));
-               if (is_dummy(iter))
-                       new_next = flag_dummy(clear_flag(next));
-               else
-                       new_next = clear_flag(next);
-               (void) uatomic_cmpxchg(&iter_prev->p.next, iter, new_next);
-       }
-       return;
-}
-
-static
-int _cds_lfht_replace(struct cds_lfht *ht, unsigned long size,
-               struct cds_lfht_node *old_node,
-               struct cds_lfht_node *old_next,
-               struct cds_lfht_node *new_node)
-{
-       struct cds_lfht_node *dummy, *ret_next;
-       struct _cds_lfht_node *lookup;
-
-       if (!old_node)  /* Return -ENOENT if asked to replace NULL node */
-               return -ENOENT;
-
-       assert(!is_removed(old_node));
-       assert(!is_dummy(old_node));
-       assert(!is_removed(new_node));
-       assert(!is_dummy(new_node));
-       assert(new_node != old_node);
-       for (;;) {
-               /* Insert after node to be replaced */
-               if (is_removed(old_next)) {
-                       /*
-                        * Too late, the old node has been removed under us
-                        * between lookup and replace. Fail.
-                        */
-                       return -ENOENT;
-               }
-               assert(!is_dummy(old_next));
-               assert(new_node != clear_flag(old_next));
-               new_node->p.next = clear_flag(old_next);
-               /*
-                * Here is the whole trick for lock-free replace: we add
-                * the replacement node _after_ the node we want to
-                * replace by atomically setting its next pointer at the
-                * same time we set its removal flag. Given that
-                * the lookups/get next use an iterator aware of the
-                * next pointer, they will either skip the old node due
-                * to the removal flag and see the new node, or use
-                * the old node, but will not see the new one.
-                */
-               ret_next = uatomic_cmpxchg(&old_node->p.next,
-                             old_next, flag_removed(new_node));
-               if (ret_next == old_next)
-                       break;          /* We performed the replacement. */
-               old_next = ret_next;
-       }
-
-       /*
-        * Ensure that the old node is not visible to readers anymore:
-        * lookup for the node, and remove it (along with any other
-        * logically removed node) if found.
-        */
-       lookup = lookup_bucket(ht, size, bit_reverse_ulong(old_node->p.reverse_hash));
-       dummy = (struct cds_lfht_node *) lookup;
-       _cds_lfht_gc_bucket(dummy, new_node);
-
-       assert(is_removed(rcu_dereference(old_node->p.next)));
-       return 0;
-}
-
-/*
- * A non-NULL unique_ret pointer uses the "add unique" (or uniquify) add
- * mode. A NULL unique_ret allows creation of duplicate keys.
- */
-static
-void _cds_lfht_add(struct cds_lfht *ht,
-               unsigned long size,
-               struct cds_lfht_node *node,
-               struct cds_lfht_iter *unique_ret,
-               int dummy)
-{
-       struct cds_lfht_node *iter_prev, *iter, *next, *new_node, *new_next,
-                       *return_node;
-       struct _cds_lfht_node *lookup;
-
-       assert(!is_dummy(node));
-       assert(!is_removed(node));
-       lookup = lookup_bucket(ht, size, bit_reverse_ulong(node->p.reverse_hash));
-       for (;;) {
-               uint32_t chain_len = 0;
-
-               /*
-                * iter_prev points to the non-removed node prior to the
-                * insert location.
-                */
-               iter_prev = (struct cds_lfht_node *) lookup;
-               /* We can always skip the dummy node initially */
-               iter = rcu_dereference(iter_prev->p.next);
-               assert(iter_prev->p.reverse_hash <= node->p.reverse_hash);
-               for (;;) {
-                       if (caa_unlikely(is_end(iter)))
-                               goto insert;
-                       if (caa_likely(clear_flag(iter)->p.reverse_hash > node->p.reverse_hash))
-                               goto insert;
-
-                       /* dummy node is the first node of the identical-hash-value chain */
-                       if (dummy && clear_flag(iter)->p.reverse_hash == node->p.reverse_hash)
-                               goto insert;
-
-                       next = rcu_dereference(clear_flag(iter)->p.next);
-                       if (caa_unlikely(is_removed(next)))
-                               goto gc_node;
-
-                       /* uniquely add */
-                       if (unique_ret
-                           && !is_dummy(next)
-                           && clear_flag(iter)->p.reverse_hash == node->p.reverse_hash) {
-                               struct cds_lfht_iter d_iter = { .node = node, .next = iter, };
-
-                               /*
-                                * uniquely adding inserts the node as the first
-                                * node of the identical-hash-value node chain.
-                                *
-                                * This semantic ensures no duplicated keys
-                                * should ever be observable in the table
-                                * (including observe one node by one node
-                                * by forward iterations)
-                                */
-                               cds_lfht_next_duplicate(ht, &d_iter);
-                               if (!d_iter.node)
-                                       goto insert;
-
-                               *unique_ret = d_iter;
-                               return;
-                       }
-
-                       /* Only account for identical reverse hash once */
-                       if (iter_prev->p.reverse_hash != clear_flag(iter)->p.reverse_hash
-                           && !is_dummy(next))
-                               check_resize(ht, size, ++chain_len);
-                       iter_prev = clear_flag(iter);
-                       iter = next;
-               }
-
-       insert:
-               assert(node != clear_flag(iter));
-               assert(!is_removed(iter_prev));
-               assert(!is_removed(iter));
-               assert(iter_prev != node);
-               if (!dummy)
-                       node->p.next = clear_flag(iter);
-               else
-                       node->p.next = flag_dummy(clear_flag(iter));
-               if (is_dummy(iter))
-                       new_node = flag_dummy(node);
-               else
-                       new_node = node;
-               if (uatomic_cmpxchg(&iter_prev->p.next, iter,
-                                   new_node) != iter) {
-                       continue;       /* retry */
-               } else {
-                       return_node = node;
-                       goto end;
-               }
-
-       gc_node:
-               assert(!is_removed(iter));
-               if (is_dummy(iter))
-                       new_next = flag_dummy(clear_flag(next));
-               else
-                       new_next = clear_flag(next);
-               (void) uatomic_cmpxchg(&iter_prev->p.next, iter, new_next);
-               /* retry */
-       }
-end:
-       if (unique_ret) {
-               unique_ret->node = return_node;
-               /* unique_ret->next left unset, never used. */
-       }
-}
-
-static
-int _cds_lfht_del(struct cds_lfht *ht, unsigned long size,
-               struct cds_lfht_node *node,
-               int dummy_removal)
-{
-       struct cds_lfht_node *dummy, *next, *old;
-       struct _cds_lfht_node *lookup;
-
-       if (!node)      /* Return -ENOENT if asked to delete NULL node */
-               return -ENOENT;
-
-       /* logically delete the node */
-       assert(!is_dummy(node));
-       assert(!is_removed(node));
-       old = rcu_dereference(node->p.next);
-       do {
-               struct cds_lfht_node *new_next;
-
-               next = old;
-               if (caa_unlikely(is_removed(next)))
-                       return -ENOENT;
-               if (dummy_removal)
-                       assert(is_dummy(next));
-               else
-                       assert(!is_dummy(next));
-               new_next = flag_removed(next);
-               old = uatomic_cmpxchg(&node->p.next, next, new_next);
-       } while (old != next);
-       /* We performed the (logical) deletion. */
-
-       /*
-        * Ensure that the node is not visible to readers anymore: lookup for
-        * the node, and remove it (along with any other logically removed node)
-        * if found.
-        */
-       lookup = lookup_bucket(ht, size, bit_reverse_ulong(node->p.reverse_hash));
-       dummy = (struct cds_lfht_node *) lookup;
-       _cds_lfht_gc_bucket(dummy, node);
-
-       assert(is_removed(rcu_dereference(node->p.next)));
-       return 0;
-}
-
-static
-void *partition_resize_thread(void *arg)
-{
-       struct partition_resize_work *work = arg;
-
-       work->ht->cds_lfht_rcu_register_thread();
-       work->fct(work->ht, work->i, work->start, work->len);
-       work->ht->cds_lfht_rcu_unregister_thread();
-       return NULL;
-}
-
-static
-void partition_resize_helper(struct cds_lfht *ht, unsigned long i,
-               unsigned long len,
-               void (*fct)(struct cds_lfht *ht, unsigned long i,
-                       unsigned long start, unsigned long len))
-{
-       unsigned long partition_len;
-       struct partition_resize_work *work;
-       int thread, ret;
-       unsigned long nr_threads;
-
-       /*
-        * Note: nr_cpus_mask + 1 is always power of 2.
-        * We spawn just the number of threads we need to satisfy the minimum
-        * partition size, up to the number of CPUs in the system.
-        */
-       if (nr_cpus_mask > 0) {
-               nr_threads = min(nr_cpus_mask + 1,
-                                len >> MIN_PARTITION_PER_THREAD_ORDER);
-       } else {
-               nr_threads = 1;
-       }
-       partition_len = len >> get_count_order_ulong(nr_threads);
-       work = calloc(nr_threads, sizeof(*work));
-       assert(work);
-       for (thread = 0; thread < nr_threads; thread++) {
-               work[thread].ht = ht;
-               work[thread].i = i;
-               work[thread].len = partition_len;
-               work[thread].start = thread * partition_len;
-               work[thread].fct = fct;
-               ret = pthread_create(&(work[thread].thread_id), ht->resize_attr,
-                       partition_resize_thread, &work[thread]);
-               assert(!ret);
-       }
-       for (thread = 0; thread < nr_threads; thread++) {
-               ret = pthread_join(work[thread].thread_id, NULL);
-               assert(!ret);
-       }
-       free(work);
-}
-
-/*
- * Holding RCU read lock to protect _cds_lfht_add against memory
- * reclaim that could be performed by other call_rcu worker threads (ABA
- * problem).
- *
- * When we reach a certain length, we can split this population phase over
- * many worker threads, based on the number of CPUs available in the system.
- * This should therefore take care of not having the expand lagging behind too
- * many concurrent insertion threads by using the scheduler's ability to
- * schedule dummy node population fairly with insertions.
- */
-static
-void init_table_populate_partition(struct cds_lfht *ht, unsigned long i,
-                                  unsigned long start, unsigned long len)
-{
-       unsigned long j;
-
-       assert(i > ht->min_alloc_order);
-       ht->cds_lfht_rcu_read_lock();
-       for (j = start; j < start + len; j++) {
-               struct cds_lfht_node *new_node =
-                       (struct cds_lfht_node *) &ht->t.tbl[i]->nodes[j];
-
-               dbg_printf("init populate: i %lu j %lu hash %lu\n",
-                          i, j, (1UL << (i - 1)) + j);
-               new_node->p.reverse_hash =
-                               bit_reverse_ulong((1UL << (i - 1)) + j);
-               _cds_lfht_add(ht, 1UL << (i - 1),
-                               new_node, NULL, 1);
-       }
-       ht->cds_lfht_rcu_read_unlock();
-}
-
-static
-void init_table_populate(struct cds_lfht *ht, unsigned long i,
-                        unsigned long len)
-{
-       assert(nr_cpus_mask != -1);
-       if (nr_cpus_mask < 0 || len < 2 * MIN_PARTITION_PER_THREAD) {
-               ht->cds_lfht_rcu_thread_online();
-               init_table_populate_partition(ht, i, 0, len);
-               ht->cds_lfht_rcu_thread_offline();
-               return;
-       }
-       partition_resize_helper(ht, i, len, init_table_populate_partition);
-}
-
-static
-void init_table(struct cds_lfht *ht,
-               unsigned long first_order, unsigned long last_order)
-{
-       unsigned long i;
-
-       dbg_printf("init table: first_order %lu last_order %lu\n",
-                  first_order, last_order);
-       assert(first_order > ht->min_alloc_order);
-       for (i = first_order; i <= last_order; i++) {
-               unsigned long len;
-
-               len = 1UL << (i - 1);
-               dbg_printf("init order %lu len: %lu\n", i, len);
-
-               /* Stop expand if the resize target changes under us */
-               if (CMM_LOAD_SHARED(ht->t.resize_target) < (1UL << i))
-                       break;
-
-               ht->t.tbl[i] = calloc(1, len * sizeof(struct _cds_lfht_node));
-               assert(ht->t.tbl[i]);
-
-               /*
-                * Set all dummy nodes reverse hash values for a level and
-                * link all dummy nodes into the table.
-                */
-               init_table_populate(ht, i, len);
-
-               /*
-                * Update table size.
-                */
-               cmm_smp_wmb();  /* populate data before RCU size */
-               CMM_STORE_SHARED(ht->t.size, 1UL << i);
-
-               dbg_printf("init new size: %lu\n", 1UL << i);
-               if (CMM_LOAD_SHARED(ht->in_progress_destroy))
-                       break;
-       }
-}
-
-/*
- * Holding RCU read lock to protect _cds_lfht_remove against memory
- * reclaim that could be performed by other call_rcu worker threads (ABA
- * problem).
- * For a single level, we logically remove and garbage collect each node.
- *
- * As a design choice, we perform logical removal and garbage collection on a
- * node-per-node basis to simplify this algorithm. We also assume keeping good
- * cache locality of the operation would overweight possible performance gain
- * that could be achieved by batching garbage collection for multiple levels.
- * However, this would have to be justified by benchmarks.
- *
- * Concurrent removal and add operations are helping us perform garbage
- * collection of logically removed nodes. We guarantee that all logically
- * removed nodes have been garbage-collected (unlinked) before call_rcu is
- * invoked to free a hole level of dummy nodes (after a grace period).
- *
- * Logical removal and garbage collection can therefore be done in batch or on a
- * node-per-node basis, as long as the guarantee above holds.
- *
- * When we reach a certain length, we can split this removal over many worker
- * threads, based on the number of CPUs available in the system. This should
- * take care of not letting resize process lag behind too many concurrent
- * updater threads actively inserting into the hash table.
- */
-static
-void remove_table_partition(struct cds_lfht *ht, unsigned long i,
-                           unsigned long start, unsigned long len)
-{
-       unsigned long j;
-
-       assert(i > ht->min_alloc_order);
-       ht->cds_lfht_rcu_read_lock();
-       for (j = start; j < start + len; j++) {
-               struct cds_lfht_node *fini_node =
-                       (struct cds_lfht_node *) &ht->t.tbl[i]->nodes[j];
-
-               dbg_printf("remove entry: i %lu j %lu hash %lu\n",
-                          i, j, (1UL << (i - 1)) + j);
-               fini_node->p.reverse_hash =
-                       bit_reverse_ulong((1UL << (i - 1)) + j);
-               (void) _cds_lfht_del(ht, 1UL << (i - 1), fini_node, 1);
-       }
-       ht->cds_lfht_rcu_read_unlock();
-}
-
-static
-void remove_table(struct cds_lfht *ht, unsigned long i, unsigned long len)
-{
-
-       assert(nr_cpus_mask != -1);
-       if (nr_cpus_mask < 0 || len < 2 * MIN_PARTITION_PER_THREAD) {
-               ht->cds_lfht_rcu_thread_online();
-               remove_table_partition(ht, i, 0, len);
-               ht->cds_lfht_rcu_thread_offline();
-               return;
-       }
-       partition_resize_helper(ht, i, len, remove_table_partition);
-}
-
-static
-void fini_table(struct cds_lfht *ht,
-               unsigned long first_order, unsigned long last_order)
-{
-       long i;
-       void *free_by_rcu = NULL;
-
-       dbg_printf("fini table: first_order %lu last_order %lu\n",
-                  first_order, last_order);
-       assert(first_order > ht->min_alloc_order);
-       for (i = last_order; i >= first_order; i--) {
-               unsigned long len;
-
-               len = 1UL << (i - 1);
-               dbg_printf("fini order %lu len: %lu\n", i, len);
-
-               /* Stop shrink if the resize target changes under us */
-               if (CMM_LOAD_SHARED(ht->t.resize_target) > (1UL << (i - 1)))
-                       break;
-
-               cmm_smp_wmb();  /* populate data before RCU size */
-               CMM_STORE_SHARED(ht->t.size, 1UL << (i - 1));
-
-               /*
-                * We need to wait for all add operations to reach Q.S. (and
-                * thus use the new table for lookups) before we can start
-                * releasing the old dummy nodes. Otherwise their lookup will
-                * return a logically removed node as insert position.
-                */
-               ht->cds_lfht_synchronize_rcu();
-               if (free_by_rcu)
-                       free(free_by_rcu);
-
-               /*
-                * Set "removed" flag in dummy nodes about to be removed.
-                * Unlink all now-logically-removed dummy node pointers.
-                * Concurrent add/remove operation are helping us doing
-                * the gc.
-                */
-               remove_table(ht, i, len);
-
-               free_by_rcu = ht->t.tbl[i];
-
-               dbg_printf("fini new size: %lu\n", 1UL << i);
-               if (CMM_LOAD_SHARED(ht->in_progress_destroy))
-                       break;
-       }
-
-       if (free_by_rcu) {
-               ht->cds_lfht_synchronize_rcu();
-               free(free_by_rcu);
-       }
-}
-
-static
-void cds_lfht_create_dummy(struct cds_lfht *ht, unsigned long size)
-{
-       struct _cds_lfht_node *prev, *node;
-       unsigned long order, len, i, j;
-
-       ht->t.tbl[0] = calloc(1, ht->min_alloc_size * sizeof(struct _cds_lfht_node));
-       assert(ht->t.tbl[0]);
-
-       dbg_printf("create dummy: order %lu index %lu hash %lu\n", 0, 0, 0);
-       ht->t.tbl[0]->nodes[0].next = flag_dummy(get_end());
-       ht->t.tbl[0]->nodes[0].reverse_hash = 0;
-
-       for (order = 1; order < get_count_order_ulong(size) + 1; order++) {
-               len = 1UL << (order - 1);
-               if (order <= ht->min_alloc_order) {
-                       ht->t.tbl[order] = (struct rcu_level *) (ht->t.tbl[0]->nodes + len);
-               } else {
-                       ht->t.tbl[order] = calloc(1, len * sizeof(struct _cds_lfht_node));
-                       assert(ht->t.tbl[order]);
-               }
-
-               i = 0;
-               prev = ht->t.tbl[i]->nodes;
-               for (j = 0; j < len; j++) {
-                       if (j & (j - 1)) {      /* Between power of 2 */
-                               prev++;
-                       } else if (j) {         /* At each power of 2 */
-                               i++;
-                               prev = ht->t.tbl[i]->nodes;
-                       }
-
-                       node = &ht->t.tbl[order]->nodes[j];
-                       dbg_printf("create dummy: order %lu index %lu hash %lu\n",
-                                  order, j, j + len);
-                       node->next = prev->next;
-                       assert(is_dummy(node->next));
-                       node->reverse_hash = bit_reverse_ulong(j + len);
-                       prev->next = flag_dummy((struct cds_lfht_node *)node);
-               }
-       }
-}
-
-struct cds_lfht *_cds_lfht_new(cds_lfht_hash_fct hash_fct,
-                       cds_lfht_compare_fct compare_fct,
-                       unsigned long hash_seed,
-                       unsigned long init_size,
-                       unsigned long min_alloc_size,
-                       int flags,
-                       void (*cds_lfht_call_rcu)(struct rcu_head *head,
-                                       void (*func)(struct rcu_head *head)),
-                       void (*cds_lfht_synchronize_rcu)(void),
-                       void (*cds_lfht_rcu_read_lock)(void),
-                       void (*cds_lfht_rcu_read_unlock)(void),
-                       void (*cds_lfht_rcu_thread_offline)(void),
-                       void (*cds_lfht_rcu_thread_online)(void),
-                       void (*cds_lfht_rcu_register_thread)(void),
-                       void (*cds_lfht_rcu_unregister_thread)(void),
-                       pthread_attr_t *attr)
-{
-       struct cds_lfht *ht;
-       unsigned long order;
-
-       /* min_alloc_size must be power of two */
-       if (!min_alloc_size || (min_alloc_size & (min_alloc_size - 1)))
-               return NULL;
-       /* init_size must be power of two */
-       if (!init_size || (init_size & (init_size - 1)))
-               return NULL;
-       min_alloc_size = max(min_alloc_size, MIN_TABLE_SIZE);
-       init_size = max(init_size, min_alloc_size);
-       ht = calloc(1, sizeof(struct cds_lfht));
-       assert(ht);
-       ht->flags = flags;
-       ht->hash_fct = hash_fct;
-       ht->compare_fct = compare_fct;
-       ht->hash_seed = hash_seed;
-       ht->cds_lfht_call_rcu = cds_lfht_call_rcu;
-       ht->cds_lfht_synchronize_rcu = cds_lfht_synchronize_rcu;
-       ht->cds_lfht_rcu_read_lock = cds_lfht_rcu_read_lock;
-       ht->cds_lfht_rcu_read_unlock = cds_lfht_rcu_read_unlock;
-       ht->cds_lfht_rcu_thread_offline = cds_lfht_rcu_thread_offline;
-       ht->cds_lfht_rcu_thread_online = cds_lfht_rcu_thread_online;
-       ht->cds_lfht_rcu_register_thread = cds_lfht_rcu_register_thread;
-       ht->cds_lfht_rcu_unregister_thread = cds_lfht_rcu_unregister_thread;
-       ht->resize_attr = attr;
-       alloc_split_items_count(ht);
-       /* this mutex should not nest in read-side C.S. */
-       pthread_mutex_init(&ht->resize_mutex, NULL);
-       order = get_count_order_ulong(init_size);
-       ht->t.resize_target = 1UL << order;
-       ht->min_alloc_size = min_alloc_size;
-       ht->min_alloc_order = get_count_order_ulong(min_alloc_size);
-       cds_lfht_create_dummy(ht, 1UL << order);
-       ht->t.size = 1UL << order;
-       return ht;
-}
-
-void cds_lfht_lookup(struct cds_lfht *ht, void *key, size_t key_len,
-               struct cds_lfht_iter *iter)
-{
-       struct cds_lfht_node *node, *next, *dummy_node;
-       struct _cds_lfht_node *lookup;
-       unsigned long hash, reverse_hash, size;
-
-       hash = ht->hash_fct(key, key_len, ht->hash_seed);
-       reverse_hash = bit_reverse_ulong(hash);
-
-       size = rcu_dereference(ht->t.size);
-       lookup = lookup_bucket(ht, size, hash);
-       dummy_node = (struct cds_lfht_node *) lookup;
-       /* We can always skip the dummy node initially */
-       node = rcu_dereference(dummy_node->p.next);
-       node = clear_flag(node);
-       for (;;) {
-               if (caa_unlikely(is_end(node))) {
-                       node = next = NULL;
-                       break;
-               }
-               if (caa_unlikely(node->p.reverse_hash > reverse_hash)) {
-                       node = next = NULL;
-                       break;
-               }
-               next = rcu_dereference(node->p.next);
-               assert(node == clear_flag(node));
-               if (caa_likely(!is_removed(next))
-                   && !is_dummy(next)
-                   && node->p.reverse_hash == reverse_hash
-                   && caa_likely(!ht->compare_fct(node->key, node->key_len, key, key_len))) {
-                               break;
-               }
-               node = clear_flag(next);
-       }
-       assert(!node || !is_dummy(rcu_dereference(node->p.next)));
-       iter->node = node;
-       iter->next = next;
-}
-
-void cds_lfht_next_duplicate(struct cds_lfht *ht, struct cds_lfht_iter *iter)
-{
-       struct cds_lfht_node *node, *next;
-       unsigned long reverse_hash;
-       void *key;
-       size_t key_len;
-
-       node = iter->node;
-       reverse_hash = node->p.reverse_hash;
-       key = node->key;
-       key_len = node->key_len;
-       next = iter->next;
-       node = clear_flag(next);
-
-       for (;;) {
-               if (caa_unlikely(is_end(node))) {
-                       node = next = NULL;
-                       break;
-               }
-               if (caa_unlikely(node->p.reverse_hash > reverse_hash)) {
-                       node = next = NULL;
-                       break;
-               }
-               next = rcu_dereference(node->p.next);
-               if (caa_likely(!is_removed(next))
-                   && !is_dummy(next)
-                   && caa_likely(!ht->compare_fct(node->key, node->key_len, key, key_len))) {
-                               break;
-               }
-               node = clear_flag(next);
-       }
-       assert(!node || !is_dummy(rcu_dereference(node->p.next)));
-       iter->node = node;
-       iter->next = next;
-}
-
-void cds_lfht_next(struct cds_lfht *ht, struct cds_lfht_iter *iter)
-{
-       struct cds_lfht_node *node, *next;
-
-       node = clear_flag(iter->next);
-       for (;;) {
-               if (caa_unlikely(is_end(node))) {
-                       node = next = NULL;
-                       break;
-               }
-               next = rcu_dereference(node->p.next);
-               if (caa_likely(!is_removed(next))
-                   && !is_dummy(next)) {
-                               break;
-               }
-               node = clear_flag(next);
-       }
-       assert(!node || !is_dummy(rcu_dereference(node->p.next)));
-       iter->node = node;
-       iter->next = next;
-}
-
-void cds_lfht_first(struct cds_lfht *ht, struct cds_lfht_iter *iter)
-{
-       struct _cds_lfht_node *lookup;
-
-       /*
-        * Get next after first dummy node. The first dummy node is the
-        * first node of the linked list.
-        */
-       lookup = &ht->t.tbl[0]->nodes[0];
-       iter->next = lookup->next;
-       cds_lfht_next(ht, iter);
-}
-
-void cds_lfht_add(struct cds_lfht *ht, struct cds_lfht_node *node)
-{
-       unsigned long hash, size;
-
-       hash = ht->hash_fct(node->key, node->key_len, ht->hash_seed);
-       node->p.reverse_hash = bit_reverse_ulong((unsigned long) hash);
-
-       size = rcu_dereference(ht->t.size);
-       _cds_lfht_add(ht, size, node, NULL, 0);
-       ht_count_add(ht, size, hash);
-}
-
-struct cds_lfht_node *cds_lfht_add_unique(struct cds_lfht *ht,
-                               struct cds_lfht_node *node)
-{
-       unsigned long hash, size;
-       struct cds_lfht_iter iter;
-
-       hash = ht->hash_fct(node->key, node->key_len, ht->hash_seed);
-       node->p.reverse_hash = bit_reverse_ulong((unsigned long) hash);
-
-       size = rcu_dereference(ht->t.size);
-       _cds_lfht_add(ht, size, node, &iter, 0);
-       if (iter.node == node)
-               ht_count_add(ht, size, hash);
-       return iter.node;
-}
-
-struct cds_lfht_node *cds_lfht_add_replace(struct cds_lfht *ht,
-                               struct cds_lfht_node *node)
-{
-       unsigned long hash, size;
-       struct cds_lfht_iter iter;
-
-       hash = ht->hash_fct(node->key, node->key_len, ht->hash_seed);
-       node->p.reverse_hash = bit_reverse_ulong((unsigned long) hash);
-
-       size = rcu_dereference(ht->t.size);
-       for (;;) {
-               _cds_lfht_add(ht, size, node, &iter, 0);
-               if (iter.node == node) {
-                       ht_count_add(ht, size, hash);
-                       return NULL;
-               }
-
-               if (!_cds_lfht_replace(ht, size, iter.node, iter.next, node))
-                       return iter.node;
-       }
-}
-
-int cds_lfht_replace(struct cds_lfht *ht, struct cds_lfht_iter *old_iter,
-               struct cds_lfht_node *new_node)
-{
-       unsigned long size;
-
-       size = rcu_dereference(ht->t.size);
-       return _cds_lfht_replace(ht, size, old_iter->node, old_iter->next,
-                       new_node);
-}
-
-int cds_lfht_del(struct cds_lfht *ht, struct cds_lfht_iter *iter)
-{
-       unsigned long size, hash;
-       int ret;
-
-       size = rcu_dereference(ht->t.size);
-       ret = _cds_lfht_del(ht, size, iter->node, 0);
-       if (!ret) {
-               hash = bit_reverse_ulong(iter->node->p.reverse_hash);
-               ht_count_del(ht, size, hash);
-       }
-       return ret;
-}
-
-static
-int cds_lfht_delete_dummy(struct cds_lfht *ht)
-{
-       struct cds_lfht_node *node;
-       struct _cds_lfht_node *lookup;
-       unsigned long order, i, size;
-
-       /* Check that the table is empty */
-       lookup = &ht->t.tbl[0]->nodes[0];
-       node = (struct cds_lfht_node *) lookup;
-       do {
-               node = clear_flag(node)->p.next;
-               if (!is_dummy(node))
-                       return -EPERM;
-               assert(!is_removed(node));
-       } while (!is_end(node));
-       /*
-        * size accessed without rcu_dereference because hash table is
-        * being destroyed.
-        */
-       size = ht->t.size;
-       /* Internal sanity check: all nodes left should be dummy */
-       for (order = 0; order < get_count_order_ulong(size) + 1; order++) {
-               unsigned long len;
-
-               len = !order ? 1 : 1UL << (order - 1);
-               for (i = 0; i < len; i++) {
-                       dbg_printf("delete order %lu i %lu hash %lu\n",
-                               order, i,
-                               bit_reverse_ulong(ht->t.tbl[order]->nodes[i].reverse_hash));
-                       assert(is_dummy(ht->t.tbl[order]->nodes[i].next));
-               }
-
-               if (order == ht->min_alloc_order)
-                       poison_free(ht->t.tbl[0]);
-               else if (order > ht->min_alloc_order)
-                       poison_free(ht->t.tbl[order]);
-               /* Nothing to delete for order < ht->min_alloc_order */
-       }
-       return 0;
-}
-
-/*
- * Should only be called when no more concurrent readers nor writers can
- * possibly access the table.
- */
-int cds_lfht_destroy(struct cds_lfht *ht, pthread_attr_t **attr)
-{
-       int ret;
-
-       /* Wait for in-flight resize operations to complete */
-       _CMM_STORE_SHARED(ht->in_progress_destroy, 1);
-       cmm_smp_mb();   /* Store destroy before load resize */
-       while (uatomic_read(&ht->in_progress_resize))
-               poll(NULL, 0, 100);     /* wait for 100ms */
-       ret = cds_lfht_delete_dummy(ht);
-       if (ret)
-               return ret;
-       free_split_items_count(ht);
-       if (attr)
-               *attr = ht->resize_attr;
-       poison_free(ht);
-       return ret;
-}
-
-void cds_lfht_count_nodes(struct cds_lfht *ht,
-               long *approx_before,
-               unsigned long *count,
-               unsigned long *removed,
-               long *approx_after)
-{
-       struct cds_lfht_node *node, *next;
-       struct _cds_lfht_node *lookup;
-       unsigned long nr_dummy = 0;
-
-       *approx_before = 0;
-       if (ht->split_count) {
-               int i;
-
-               for (i = 0; i < split_count_mask + 1; i++) {
-                       *approx_before += uatomic_read(&ht->split_count[i].add);
-                       *approx_before -= uatomic_read(&ht->split_count[i].del);
-               }
-       }
-
-       *count = 0;
-       *removed = 0;
-
-       /* Count non-dummy nodes in the table */
-       lookup = &ht->t.tbl[0]->nodes[0];
-       node = (struct cds_lfht_node *) lookup;
-       do {
-               next = rcu_dereference(node->p.next);
-               if (is_removed(next)) {
-                       if (!is_dummy(next))
-                               (*removed)++;
-                       else
-                               (nr_dummy)++;
-               } else if (!is_dummy(next))
-                       (*count)++;
-               else
-                       (nr_dummy)++;
-               node = clear_flag(next);
-       } while (!is_end(node));
-       dbg_printf("number of dummy nodes: %lu\n", nr_dummy);
-       *approx_after = 0;
-       if (ht->split_count) {
-               int i;
-
-               for (i = 0; i < split_count_mask + 1; i++) {
-                       *approx_after += uatomic_read(&ht->split_count[i].add);
-                       *approx_after -= uatomic_read(&ht->split_count[i].del);
-               }
-       }
-}
-
-/* called with resize mutex held */
-static
-void _do_cds_lfht_grow(struct cds_lfht *ht,
-               unsigned long old_size, unsigned long new_size)
-{
-       unsigned long old_order, new_order;
-
-       old_order = get_count_order_ulong(old_size);
-       new_order = get_count_order_ulong(new_size);
-       dbg_printf("resize from %lu (order %lu) to %lu (order %lu) buckets\n",
-                  old_size, old_order, new_size, new_order);
-       assert(new_size > old_size);
-       init_table(ht, old_order + 1, new_order);
-}
-
-/* called with resize mutex held */
-static
-void _do_cds_lfht_shrink(struct cds_lfht *ht,
-               unsigned long old_size, unsigned long new_size)
-{
-       unsigned long old_order, new_order;
-
-       new_size = max(new_size, ht->min_alloc_size);
-       old_order = get_count_order_ulong(old_size);
-       new_order = get_count_order_ulong(new_size);
-       dbg_printf("resize from %lu (order %lu) to %lu (order %lu) buckets\n",
-                  old_size, old_order, new_size, new_order);
-       assert(new_size < old_size);
-
-       /* Remove and unlink all dummy nodes to remove. */
-       fini_table(ht, new_order + 1, old_order);
-}
-
-
-/* called with resize mutex held */
-static
-void _do_cds_lfht_resize(struct cds_lfht *ht)
-{
-       unsigned long new_size, old_size;
-
-       /*
-        * Resize table, re-do if the target size has changed under us.
-        */
-       do {
-               assert(uatomic_read(&ht->in_progress_resize));
-               if (CMM_LOAD_SHARED(ht->in_progress_destroy))
-                       break;
-               ht->t.resize_initiated = 1;
-               old_size = ht->t.size;
-               new_size = CMM_LOAD_SHARED(ht->t.resize_target);
-               if (old_size < new_size)
-                       _do_cds_lfht_grow(ht, old_size, new_size);
-               else if (old_size > new_size)
-                       _do_cds_lfht_shrink(ht, old_size, new_size);
-               ht->t.resize_initiated = 0;
-               /* write resize_initiated before read resize_target */
-               cmm_smp_mb();
-       } while (ht->t.size != CMM_LOAD_SHARED(ht->t.resize_target));
-}
-
-static
-unsigned long resize_target_update(struct cds_lfht *ht, unsigned long size,
-                                  int growth_order)
-{
-       return _uatomic_max(&ht->t.resize_target,
-                           size << growth_order);
-}
-
-static
-void resize_target_update_count(struct cds_lfht *ht,
-                               unsigned long count)
-{
-       count = max(count, ht->min_alloc_size);
-       uatomic_set(&ht->t.resize_target, count);
-}
-
-void cds_lfht_resize(struct cds_lfht *ht, unsigned long new_size)
-{
-       resize_target_update_count(ht, new_size);
-       CMM_STORE_SHARED(ht->t.resize_initiated, 1);
-       ht->cds_lfht_rcu_thread_offline();
-       pthread_mutex_lock(&ht->resize_mutex);
-       _do_cds_lfht_resize(ht);
-       pthread_mutex_unlock(&ht->resize_mutex);
-       ht->cds_lfht_rcu_thread_online();
-}
-
-static
-void do_resize_cb(struct rcu_head *head)
-{
-       struct rcu_resize_work *work =
-               caa_container_of(head, struct rcu_resize_work, head);
-       struct cds_lfht *ht = work->ht;
-
-       ht->cds_lfht_rcu_thread_offline();
-       pthread_mutex_lock(&ht->resize_mutex);
-       _do_cds_lfht_resize(ht);
-       pthread_mutex_unlock(&ht->resize_mutex);
-       ht->cds_lfht_rcu_thread_online();
-       poison_free(work);
-       cmm_smp_mb();   /* finish resize before decrement */
-       uatomic_dec(&ht->in_progress_resize);
-}
-
-static
-void cds_lfht_resize_lazy(struct cds_lfht *ht, unsigned long size, int growth)
-{
-       struct rcu_resize_work *work;
-       unsigned long target_size;
-
-       target_size = resize_target_update(ht, size, growth);
-       /* Store resize_target before read resize_initiated */
-       cmm_smp_mb();
-       if (!CMM_LOAD_SHARED(ht->t.resize_initiated) && size < target_size) {
-               uatomic_inc(&ht->in_progress_resize);
-               cmm_smp_mb();   /* increment resize count before load destroy */
-               if (CMM_LOAD_SHARED(ht->in_progress_destroy)) {
-                       uatomic_dec(&ht->in_progress_resize);
-                       return;
-               }
-               work = malloc(sizeof(*work));
-               work->ht = ht;
-               ht->cds_lfht_call_rcu(&work->head, do_resize_cb);
-               CMM_STORE_SHARED(ht->t.resize_initiated, 1);
-       }
-}
-
-static
-void cds_lfht_resize_lazy_count(struct cds_lfht *ht, unsigned long size,
-                               unsigned long count)
-{
-       struct rcu_resize_work *work;
-
-       if (!(ht->flags & CDS_LFHT_AUTO_RESIZE))
-               return;
-       resize_target_update_count(ht, count);
-       /* Store resize_target before read resize_initiated */
-       cmm_smp_mb();
-       if (!CMM_LOAD_SHARED(ht->t.resize_initiated)) {
-               uatomic_inc(&ht->in_progress_resize);
-               cmm_smp_mb();   /* increment resize count before load destroy */
-               if (CMM_LOAD_SHARED(ht->in_progress_destroy)) {
-                       uatomic_dec(&ht->in_progress_resize);
-                       return;
-               }
-               work = malloc(sizeof(*work));
-               work->ht = ht;
-               ht->cds_lfht_call_rcu(&work->head, do_resize_cb);
-               CMM_STORE_SHARED(ht->t.resize_initiated, 1);
-       }
-}
diff --git a/hashtable/rculfhash.h b/hashtable/rculfhash.h
deleted file mode 100644 (file)
index 719cd58..0000000
+++ /dev/null
@@ -1,389 +0,0 @@
-#ifndef _URCU_RCULFHASH_H
-#define _URCU_RCULFHASH_H
-
-/*
- * urcu/rculfhash.h
- *
- * Userspace RCU library - Lock-Free RCU Hash Table
- *
- * Copyright 2011 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Include this file _after_ including your URCU flavor.
- */
-
-#include <stdint.h>
-#include <urcu-call-rcu.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*
- * struct cds_lfht_node and struct _cds_lfht_node should be aligned on
- * 4-bytes boundaries because the two lower bits are used as flags.
- */
-
-/*
- * _cds_lfht_node: Contains the internal pointers and reverse-hash
- * value required for lookup and traversal of the hash table.
- */
-struct _cds_lfht_node {
-       struct cds_lfht_node *next;     /* ptr | DUMMY_FLAG | REMOVED_FLAG */
-       unsigned long reverse_hash;
-} __attribute__((aligned(4)));
-
-/*
- * cds_lfht_node: Contains the full key and length required to check for
- * an actual match, and also contains an rcu_head structure that is used
- * by RCU to track a node through a given RCU grace period.  There is an
- * instance of _cds_lfht_node enclosed as a field within each
- * _cds_lfht_node structure.
- *
- * struct cds_lfht_node can be embedded into a structure (as a field).
- * caa_container_of() can be used to get the structure from the struct
- * cds_lfht_node after a lookup.
- */
-struct cds_lfht_node {
-       /* cache-hot for iteration */
-       struct _cds_lfht_node p;          /* needs to be first field */
-       void *key;
-       unsigned int key_len;
-       /* cache-cold for iteration */
-       struct rcu_head head;
-};
-
-/* cds_lfht_iter: Used to track state while traversing a hash chain. */
-struct cds_lfht_iter {
-       struct cds_lfht_node *node, *next;
-};
-
-static inline
-struct cds_lfht_node *cds_lfht_iter_get_node(struct cds_lfht_iter *iter)
-{
-       return iter->node;
-}
-
-struct cds_lfht;
-
-/*
- * Caution !
- * Ensure reader and writer threads are registered as urcu readers.
- */
-
-typedef unsigned long (*cds_lfht_hash_fct)(void *key, size_t length,
-                                       unsigned long seed);
-typedef unsigned long (*cds_lfht_compare_fct)(void *key1, size_t key1_len,
-                                       void *key2, size_t key2_len);
-
-/*
- * cds_lfht_node_init - initialize a hash table node
- */
-static inline
-void cds_lfht_node_init(struct cds_lfht_node *node, void *key,
-                       size_t key_len)
-{
-       node->key = key;
-       node->key_len = key_len;
-}
-
-/*
- * Hash table creation flags.
- */
-enum {
-       CDS_LFHT_AUTO_RESIZE = (1U << 0),
-       CDS_LFHT_ACCOUNTING = (1U << 1),
-};
-
-/*
- * _cds_lfht_new - API used by cds_lfht_new wrapper. Do not use directly.
- */
-struct cds_lfht *_cds_lfht_new(cds_lfht_hash_fct hash_fct,
-                       cds_lfht_compare_fct compare_fct,
-                       unsigned long hash_seed,
-                       unsigned long init_size,
-                       unsigned long min_alloc_size,
-                       int flags,
-                       void (*cds_lfht_call_rcu)(struct rcu_head *head,
-                               void (*func)(struct rcu_head *head)),
-                       void (*cds_lfht_synchronize_rcu)(void),
-                       void (*cds_lfht_rcu_read_lock)(void),
-                       void (*cds_lfht_rcu_read_unlock)(void),
-                       void (*cds_lfht_rcu_thread_offline)(void),
-                       void (*cds_lfht_rcu_thread_online)(void),
-                       void (*cds_lfht_rcu_register_thread)(void),
-                       void (*cds_lfht_rcu_unregister_thread)(void),
-                       pthread_attr_t *attr);
-
-/*
- * cds_lfht_new - allocate a hash table.
- * @hash_fct: the hashing function.
- * @compare_fct: the key comparison function.
- * @hash_seed: the seed for hash function.
- * @init_size: number of nodes to allocate initially. Must be power of two.
- * @min_alloc_size: the smallest allocation size to use. Must be power of two.
- * @flags: hash table creation flags (can be combined with bitwise or: '|').
- *           0: no flags.
- *           CDS_LFHT_AUTO_RESIZE: automatically resize hash table.
- * @attr: optional resize worker thread attributes. NULL for default.
- *
- * Return NULL on error.
- * Note: the RCU flavor must be already included before the hash table header.
- *
- * The programmer is responsible for ensuring that resize operation has a
- * priority equal to hash table updater threads. It should be performed by
- * specifying the appropriate priority in the pthread "attr" argument, and,
- * for CDS_LFHT_AUTO_RESIZE, by ensuring that call_rcu worker threads also have
- * this priority level. Having lower priority for call_rcu and resize threads
- * does not pose any correctness issue, but the resize operations could be
- * starved by updates, thus leading to long hash table bucket chains.
- * Threads calling this API need to be registered RCU read-side threads.
- */
-static inline
-struct cds_lfht *cds_lfht_new(cds_lfht_hash_fct hash_fct,
-                       cds_lfht_compare_fct compare_fct,
-                       unsigned long hash_seed,
-                       unsigned long init_size,
-                       unsigned long min_alloc_size,
-                       int flags,
-                       pthread_attr_t *attr)
-{
-       return _cds_lfht_new(hash_fct, compare_fct, hash_seed,
-                       init_size, min_alloc_size, flags,
-                       call_rcu, synchronize_rcu, rcu_read_lock,
-                       rcu_read_unlock, rcu_thread_offline,
-                       rcu_thread_online, rcu_register_thread,
-                       rcu_unregister_thread, attr);
-}
-
-/*
- * cds_lfht_destroy - destroy a hash table.
- * @ht: the hash table to destroy.
- * @attr: (output) resize worker thread attributes, as received by cds_lfht_new.
- *        The caller will typically want to free this pointer if dynamically
- *        allocated. The attr point can be NULL if the caller does not
- *        need to be informed of the value passed to cds_lfht_new().
- *
- * Return 0 on success, negative error value on error.
- * Threads calling this API need to be registered RCU read-side threads.
- */
-int cds_lfht_destroy(struct cds_lfht *ht, pthread_attr_t **attr);
-
-/*
- * cds_lfht_count_nodes - count the number of nodes in the hash table.
- * @ht: the hash table.
- * @split_count_before: Sample the node count split-counter before traversal.
- * @count: Traverse the hash table, count the number of nodes observed.
- * @removed: Number of logically removed nodes observed during traversal.
- * @split_count_after: Sample the node count split-counter after traversal.
- * Call with rcu_read_lock held.
- * Threads calling this API need to be registered RCU read-side threads.
- */
-void cds_lfht_count_nodes(struct cds_lfht *ht,
-               long *split_count_before,
-               unsigned long *count,
-               unsigned long *removed,
-               long *split_count_after);
-
-/*
- * cds_lfht_lookup - lookup a node by key.
- *
- * Output in "*iter". *iter->node set to NULL if not found.
- * Call with rcu_read_lock held.
- * Threads calling this API need to be registered RCU read-side threads.
- */
-void cds_lfht_lookup(struct cds_lfht *ht, void *key, size_t key_len,
-               struct cds_lfht_iter *iter);
-
-/*
- * cds_lfht_next_duplicate - get the next item with same key (after a lookup).
- *
- * Uses an iterator initialized by a lookup.
- * Sets *iter-node to the following node with same key.
- * Sets *iter->node to NULL if no following node exists with same key.
- * RCU read-side lock must be held across cds_lfht_lookup and
- * cds_lfht_next calls, and also between cds_lfht_next calls using the
- * node returned by a previous cds_lfht_next.
- * Call with rcu_read_lock held.
- * Threads calling this API need to be registered RCU read-side threads.
- */
-void cds_lfht_next_duplicate(struct cds_lfht *ht, struct cds_lfht_iter *iter);
-
-/*
- * cds_lfht_first - get the first node in the table.
- *
- * Output in "*iter". *iter->node set to NULL if table is empty.
- * Call with rcu_read_lock held.
- * Threads calling this API need to be registered RCU read-side threads.
- */
-void cds_lfht_first(struct cds_lfht *ht, struct cds_lfht_iter *iter);
-
-/*
- * cds_lfht_next - get the next node in the table.
- *
- * Input/Output in "*iter". *iter->node set to NULL if *iter was
- * pointing to the last table node.
- * Call with rcu_read_lock held.
- * Threads calling this API need to be registered RCU read-side threads.
- */
-void cds_lfht_next(struct cds_lfht *ht, struct cds_lfht_iter *iter);
-
-/*
- * cds_lfht_add - add a node to the hash table.
- *
- * This function supports adding redundant keys into the table.
- * Call with rcu_read_lock held.
- * Threads calling this API need to be registered RCU read-side threads.
- */
-void cds_lfht_add(struct cds_lfht *ht, struct cds_lfht_node *node);
-
-/*
- * cds_lfht_add_unique - add a node to hash table, if key is not present.
- *
- * Return the node added upon success.
- * Return the unique node already present upon failure. If
- * cds_lfht_add_unique fails, the node passed as parameter should be
- * freed by the caller.
- * Call with rcu_read_lock held.
- * Threads calling this API need to be registered RCU read-side threads.
- *
- * The semantic of this function is that if only this function is used
- * to add keys into the table, no duplicated keys should ever be
- * observable in the table. The same guarantee apply for combination of
- * add_unique and add_replace (see below).
- */
-struct cds_lfht_node *cds_lfht_add_unique(struct cds_lfht *ht,
-               struct cds_lfht_node *node);
-
-/*
- * cds_lfht_add_replace - replace or add a node within hash table.
- *
- * Return the node replaced upon success. If no node matching the key
- * was present, return NULL, which also means the operation succeeded.
- * This replacement operation should never fail.
- * Call with rcu_read_lock held.
- * Threads calling this API need to be registered RCU read-side threads.
- * After successful replacement, a grace period must be waited for before
- * freeing the memory reserved for the returned node.
- *
- * The semantic of replacement vs lookups is the following: if lookups
- * are performed between a key unique insertion and its removal, we
- * guarantee that the lookups and get next will always find exactly one
- * instance of the key if it is replaced concurrently with the lookups.
- *
- * Providing this semantic allows us to ensure that replacement-only
- * schemes will never generate duplicated keys. It also allows us to
- * guarantee that a combination of add_replace and add_unique updates
- * will never generate duplicated keys.
- */
-struct cds_lfht_node *cds_lfht_add_replace(struct cds_lfht *ht,
-               struct cds_lfht_node *node);
-
-/*
- * cds_lfht_replace - replace a node pointer to by iter within hash table.
- *
- * Return 0 if replacement is successful, negative value otherwise.
- * Replacing a NULL old node or an already removed node will fail with a
- * negative value.
- * Old node can be looked up with cds_lfht_lookup and cds_lfht_next.
- * RCU read-side lock must be held between lookup and replacement.
- * Call with rcu_read_lock held.
- * Threads calling this API need to be registered RCU read-side threads.
- * After successful replacement, a grace period must be waited for before
- * freeing the memory reserved for the old node (which can be accessed
- * with cds_lfht_iter_get_node).
- *
- * The semantic of replacement vs lookups is the following: if lookups
- * are performed between a key unique insertion and its removal, we
- * guarantee that the lookups and get next will always find exactly one
- * instance of the key if it is replaced concurrently with the lookups.
- *
- * Providing this semantic allows us to ensure that replacement-only
- * schemes will never generate duplicated keys. It also allows us to
- * guarantee that a combination of add_replace and add_unique updates
- * will never generate duplicated keys.
- */
-int cds_lfht_replace(struct cds_lfht *ht, struct cds_lfht_iter *old_iter,
-               struct cds_lfht_node *new_node);
-
-/*
- * cds_lfht_del - remove node pointed to by iterator from hash table.
- *
- * Return 0 if the node is successfully removed, negative value
- * otherwise.
- * Replacing a NULL node or an already removed node will fail with a
- * negative value.
- * Node can be looked up with cds_lfht_lookup and cds_lfht_next.
- * cds_lfht_iter_get_node.
- * RCU read-side lock must be held between lookup and removal.
- * Call with rcu_read_lock held.
- * Threads calling this API need to be registered RCU read-side threads.
- * After successful removal, a grace period must be waited for before
- * freeing the memory reserved for old node (which can be accessed with
- * cds_lfht_iter_get_node).
- */
-int cds_lfht_del(struct cds_lfht *ht, struct cds_lfht_iter *iter);
-
-/*
- * cds_lfht_resize - Force a hash table resize
- * @new_size: update to this hash table size.
- *
- * Threads calling this API need to be registered RCU read-side threads.
- */
-void cds_lfht_resize(struct cds_lfht *ht, unsigned long new_size);
-
-/*
- * Note: cds_lfht_for_each are safe for element removal during
- * iteration.
- */
-#define cds_lfht_for_each(ht, iter, node)                              \
-       for (cds_lfht_first(ht, iter),                                  \
-                       node = cds_lfht_iter_get_node(iter);            \
-                       node != NULL;                                           \
-                       cds_lfht_next(ht, iter),                                \
-                       node = cds_lfht_iter_get_node(iter))
-
-#define cds_lfht_for_each_duplicate(ht, match, hash, key, iter, node)  \
-       for (cds_lfht_lookup(ht, match, hash, key, iter),               \
-                       node = cds_lfht_iter_get_node(iter);            \
-                       node != NULL;                                           \
-                       cds_lfht_next_duplicate(ht, match, key, iter),          \
-                       node = cds_lfht_iter_get_node(iter))
-
-#define cds_lfht_for_each_entry(ht, iter, pos, member)                 \
-       for (cds_lfht_first(ht, iter),                                  \
-                       pos = caa_container_of(cds_lfht_iter_get_node(iter), \
-                               typeof(*(pos)), member);        \
-                       &(pos)->member != NULL;                                 \
-                       cds_lfht_next(ht, iter),                                \
-                       pos = caa_container_of(cds_lfht_iter_get_node(iter), \
-                               typeof(*(pos)), member))
-
-#define cds_lfht_for_each_entry_duplicate(ht, match, hash, key,                \
-               iter, pos, member)                      \
-for (cds_lfht_lookup(ht, match, hash, key, iter),              \
-               pos = caa_container_of(cds_lfht_iter_get_node(iter), \
-                       typeof(*(pos)), member);        \
-               &(pos)->member != NULL;                                 \
-               cds_lfht_next_duplicate(ht, match, key, iter),          \
-               pos = caa_container_of(cds_lfht_iter_get_node(iter), \
-                       typeof(*(pos)), member))
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _URCU_RCULFHASH_H */
index d53444f4e11d8de351d0ebb6ef72e8e8279af115..00120a6a051dd423980b9d7b71b5db19504a4f6f 100644 (file)
@@ -1,5 +1,5 @@
 lttnginclude_HEADERS = lttng/lttng.h lttng/lttng-kconsumer.h \
-                       lttng/lttng-ustconsumer.h lttng/lttng-consumer.h
+                                          lttng/lttng-ustconsumer.h lttng/lttng-consumer.h
 
 noinst_HEADERS = lttngerr.h lttng-kernel.h lttng-consumerd.h lttng-share.h \
-                       lttng-sessiond-comm.h lttng-kernel-ctl.h
+                                lttng-sessiond-comm.h lttng-kernel-ctl.h lttng-ht.h runas.h
diff --git a/include/lttng-ht.h b/include/lttng-ht.h
new file mode 100644 (file)
index 0000000..649ffcf
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; only version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifndef _LTT_HT_H
+#define _LTT_HT_H
+
+#include <urcu.h>
+#include "../liblttng-ht/rculfhash.h"
+#include "../liblttng-ht/rculfhash-internal.h"
+
+typedef unsigned long (*hash_fct)(void *_key, unsigned long seed);
+typedef cds_lfht_match_fct hash_match_fct;
+
+enum lttng_ht_type {
+       LTTNG_HT_TYPE_STRING,
+       LTTNG_HT_TYPE_ULONG,
+};
+
+struct lttng_ht {
+       struct cds_lfht *ht;
+       cds_lfht_match_fct match_fct;
+       hash_fct hash_fct;
+};
+
+struct lttng_ht_iter {
+       struct cds_lfht_iter iter;
+};
+
+struct lttng_ht_node_str {
+       char *key;
+       struct cds_lfht_node node;
+       struct rcu_head head;
+};
+
+struct lttng_ht_node_ulong {
+       unsigned long key;
+       struct cds_lfht_node node;
+       struct rcu_head head;
+};
+
+/* Hashtable new and destroy */
+extern struct lttng_ht *lttng_ht_new(unsigned long size, int type);
+extern void lttng_ht_destroy(struct lttng_ht *ht);
+
+/* Specialized node init and free functions */
+extern void lttng_ht_node_init_str(struct lttng_ht_node_str *node, char *key);
+extern void lttng_ht_node_init_ulong(struct lttng_ht_node_ulong *node,
+               unsigned long key);
+extern void lttng_ht_node_free_str(struct lttng_ht_node_str *node);
+extern void lttng_ht_node_free_ulong(struct lttng_ht_node_ulong *node);
+
+extern void lttng_ht_lookup(struct lttng_ht *ht, void *key,
+               struct lttng_ht_iter *iter);
+
+/* Specialized add unique functions */
+extern void lttng_ht_add_unique_str(struct lttng_ht *ht,
+               struct lttng_ht_node_str *node);
+extern void lttng_ht_add_unique_ulong(struct lttng_ht *ht,
+               struct lttng_ht_node_ulong *node);
+
+extern int lttng_ht_del(struct lttng_ht *ht, struct lttng_ht_iter *iter);
+
+extern void lttng_ht_get_first(struct lttng_ht *ht,
+               struct lttng_ht_iter *iter);
+extern void lttng_ht_get_next(struct lttng_ht *ht, struct lttng_ht_iter *iter);
+
+extern unsigned long lttng_ht_get_count(struct lttng_ht *ht);
+
+extern struct lttng_ht_node_str *lttng_ht_iter_get_node_str(
+               struct lttng_ht_iter *iter);
+extern struct lttng_ht_node_ulong *lttng_ht_iter_get_node_ulong(
+               struct lttng_ht_iter *iter);
+
+#endif /* _LTT_HT_H */
index 74d7dc99cef1385c518e13286f6085313292da3a..48cf933216c431ece31185b7fc55bb4ee5816f8b 100644 (file)
  * These declarations should NOT be considered stable API.
  */
 
+#define _GNU_SOURCE
 #include <limits.h>
 #include <lttng/lttng.h>
+#include <sys/socket.h>
 
 #define LTTNG_RUNDIR                        "/var/run/lttng"
 #define LTTNG_HOME_RUNDIR                   "%s/.lttng"
@@ -95,6 +97,7 @@ enum lttcomm_return_code {
        LTTCOMM_NO_EVENT,                               /* No event found */
        LTTCOMM_CONNECT_FAIL,           /* Unable to connect to unix socket */
        LTTCOMM_APP_NOT_FOUND,          /* App not found in traceable app list */
+       LTTCOMM_EPERM,                  /* Permission denied */
        LTTCOMM_KERN_NA,                                /* Kernel tracer unavalable */
        LTTCOMM_KERN_EVENT_EXIST,       /* Kernel event already exists */
        LTTCOMM_KERN_SESS_FAIL,                 /* Kernel create session failed */
@@ -227,6 +230,8 @@ struct lttcomm_consumer_msg {
                        uint32_t state;    /* enum lttcomm_consumer_fd_state */
                        enum lttng_event_output output; /* use splice or mmap to consume this fd */
                        uint64_t mmap_len;
+                       uid_t uid;         /* User ID owning the session */
+                       gid_t gid;         /* Group ID owning the session */
                        char path_name[PATH_MAX];
                } stream;
        } u;
@@ -287,6 +292,12 @@ extern ssize_t lttcomm_recv_fds_unix_sock(int sock, int *fds, size_t nb_fd);
 
 extern ssize_t lttcomm_recv_unix_sock(int sock, void *buf, size_t len);
 extern ssize_t lttcomm_send_unix_sock(int sock, void *buf, size_t len);
+
+extern ssize_t lttcomm_send_creds_unix_sock(int sock, void *buf, size_t len);
+extern ssize_t lttcomm_recv_creds_unix_sock(int sock, void *buf, size_t len,
+               struct ucred *creds);
+
 extern const char *lttcomm_get_readable_code(enum lttcomm_return_code code);
+extern int lttcomm_setsockopt_creds_unix_sock(int sock);
 
 #endif /* _LTTNG_SESSIOND_COMM_H */
index 708a1f009cf3727d54c42ccf2da62557de1d7db2..41d659b6f49cacdd917970c75f5226b61a918729 100644 (file)
@@ -24,7 +24,7 @@
 #include <lttng/lttng.h>
 
 /* Default size of a hash table */
-#define DEFAULT_HT_SIZE                 32
+#define DEFAULT_HT_SIZE                 4
 
 /* Default channel attributes */
 #define DEFAULT_CHANNEL_NAME            "channel0"
index e5672d7b78ef81f45d7ea8134bb288f78253f8dc..81fd83e0ff4845574f4ddf3762129a2935bf7138 100644 (file)
@@ -22,6 +22,7 @@
 
 #include <limits.h>
 #include <poll.h>
+#include <unistd.h>
 #include <urcu/list.h>
 #include <lttng/lttng.h>
 
@@ -120,6 +121,9 @@ struct lttng_consumer_stream {
        struct lttng_ust_lib_ring_buffer *buf;
        int cpu;
        int hangup_flush_done;
+       /* UID/GID of the user owning the session to which stream belongs */
+       uid_t uid;
+       gid_t gid;
 };
 
 /*
@@ -265,7 +269,9 @@ extern struct lttng_consumer_stream *consumer_allocate_stream(
                enum lttng_consumer_stream_state state,
                uint64_t mmap_len,
                enum lttng_event_output output,
-               const char *path_name);
+               const char *path_name,
+               uid_t uid,
+               gid_t gid);
 extern int consumer_add_stream(struct lttng_consumer_stream *stream);
 extern void consumer_del_stream(struct lttng_consumer_stream *stream);
 extern void consumer_change_stream_state(int stream_key,
diff --git a/include/runas.h b/include/runas.h
new file mode 100644 (file)
index 0000000..544653d
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef _RUNAS_H
+#define _RUNAS_H
+
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *                      Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; only verion 2
+ * of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <unistd.h>
+
+int mkdir_recursive_run_as(const char *path, mode_t mode, uid_t uid, gid_t gid);
+int mkdir_run_as(const char *path, mode_t mode, uid_t uid, gid_t gid);
+int open_run_as(const char *path, int flags, mode_t mode, uid_t uid, gid_t gid);
+
+#endif /* _RUNAS_H */
index 893df720882b7afd6798e789ca7fd90c967836fd..0811e68ca8e8368c7695f92f37a44c906b42b7c0 100644 (file)
@@ -174,7 +174,9 @@ struct lttng_consumer_stream *consumer_allocate_stream(
                enum lttng_consumer_stream_state state,
                uint64_t mmap_len,
                enum lttng_event_output output,
-               const char *path_name)
+               const char *path_name,
+               uid_t uid,
+               gid_t gid)
 {
        struct lttng_consumer_stream *stream;
        int ret;
@@ -199,6 +201,8 @@ struct lttng_consumer_stream *consumer_allocate_stream(
        stream->mmap_len = mmap_len;
        stream->mmap_base = NULL;
        stream->output = output;
+       stream->uid = uid;
+       stream->gid = gid;
        strncpy(stream->path_name, path_name, PATH_MAX - 1);
        stream->path_name[PATH_MAX - 1] = '\0';
 
diff --git a/liblttng-ht/Makefile.am b/liblttng-ht/Makefile.am
new file mode 100644 (file)
index 0000000..bd85018
--- /dev/null
@@ -0,0 +1,13 @@
+AM_CPPFLAGS = -I$(top_srcdir)/include
+
+noinst_LTLIBRARIES = liblttng-ht.la
+
+liblttng_ht_la_SOURCES = lttng-ht.c \
+                         utils.c utils.h \
+                         rculfhash-internal.h urcu-flavor.h \
+                         rculfhash.h rculfhash.c \
+                         rculfhash-mm-chunk.c \
+                         rculfhash-mm-mmap.c \
+                         rculfhash-mm-order.c
+
+liblttng_ht_la_LIBADD = -lurcu-common -lurcu
diff --git a/liblttng-ht/lttng-ht.c b/liblttng-ht/lttng-ht.c
new file mode 100644 (file)
index 0000000..608b3af
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; only version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#define _GNU_SOURCE
+#include <assert.h>
+#include <string.h>
+#include <urcu.h>
+#include <urcu/compiler.h>
+
+#include <lttng-ht.h>
+#include <lttng-share.h>
+#include <lttngerr.h>
+
+#include "utils.h"
+
+#define HASH_SEED            0x42UL            /* The answer to life */
+
+static unsigned long min_hash_alloc_size = 1;
+static unsigned long max_hash_buckets_size = (1UL << 20);
+
+/*
+ * Match function for string node.
+ */
+static int match_str(struct cds_lfht_node *node, const void *key)
+{
+       struct lttng_ht_node_str *match_node =
+               caa_container_of(node, struct lttng_ht_node_str, node);
+
+       return hash_match_key_str(match_node->key, (void *) key);
+}
+
+/*
+ * Match function for ulong node.
+ */
+static int match_ulong(struct cds_lfht_node *node, const void *key)
+{
+       struct lttng_ht_node_ulong *match_node =
+               caa_container_of(node, struct lttng_ht_node_ulong, node);
+
+       return hash_match_key_ulong((void *) match_node->key, (void *) key);
+}
+
+/*
+ * Return an allocated lttng hashtable.
+ */
+struct lttng_ht *lttng_ht_new(unsigned long size, int type)
+{
+       struct lttng_ht *ht;
+
+       /* Test size */
+       size != 0 ? : (size = DEFAULT_HT_SIZE);
+
+       ht = zmalloc(sizeof(*ht));
+       if (ht == NULL) {
+               PERROR("zmalloc lttng_ht");
+               goto error;
+       }
+
+       ht->ht = cds_lfht_new(size, min_hash_alloc_size, max_hash_buckets_size,
+                       CDS_LFHT_AUTO_RESIZE, NULL);
+       /*
+        * There is already an assert in the RCU hashtable code so if the ht is
+        * NULL here there is a *huge* problem.
+        */
+       assert(ht->ht);
+
+       switch (type) {
+       case LTTNG_HT_TYPE_STRING:
+               ht->match_fct = match_str;
+               ht->hash_fct = hash_key_str;
+               break;
+       case LTTNG_HT_TYPE_ULONG:
+               ht->match_fct = match_ulong;
+               ht->hash_fct = hash_key_ulong;
+               break;
+       default:
+               ERR("Unknown lttng hashtable type %d", type);
+               goto error;
+       }
+
+       DBG3("Created hashtable size %lu at %p of type %d", size, ht->ht, type);
+
+       return ht;
+
+error:
+       return NULL;
+}
+
+/*
+ * Free a lttng hashtable.
+ */
+void lttng_ht_destroy(struct lttng_ht *ht)
+{
+       int ret;
+
+       ret = cds_lfht_destroy(ht->ht, NULL);
+       assert(!ret);
+}
+
+/*
+ * Init lttng ht node string.
+ */
+void lttng_ht_node_init_str(struct lttng_ht_node_str *node, char *key)
+{
+       assert(node);
+
+       node->key = key;
+       cds_lfht_node_init(&node->node);
+}
+
+/*
+ * Init lttng ht node unsigned long.
+ */
+void lttng_ht_node_init_ulong(struct lttng_ht_node_ulong *node,
+               unsigned long key)
+{
+       assert(node);
+
+       node->key = key;
+       cds_lfht_node_init(&node->node);
+}
+
+/*
+ * Free lttng ht node string.
+ */
+void lttng_ht_node_free_str(struct lttng_ht_node_str *node)
+{
+       assert(node);
+       free(node);
+}
+
+/*
+ * Free lttng ht node unsigned long.
+ */
+void lttng_ht_node_free_ulong(struct lttng_ht_node_ulong *node)
+{
+       assert(node);
+       free(node);
+}
+
+/*
+ * Lookup function in hashtable.
+ */
+void lttng_ht_lookup(struct lttng_ht *ht, void *key,
+               struct lttng_ht_iter *iter)
+{
+       assert(ht);
+       assert(ht->ht);
+       assert(key);
+
+       cds_lfht_lookup(ht->ht, ht->hash_fct(key, HASH_SEED),
+                       ht->match_fct, key, &iter->iter);
+}
+
+/*
+ * Add unique string node to hashtable.
+ */
+void lttng_ht_add_unique_str(struct lttng_ht *ht,
+               struct lttng_ht_node_str *node)
+{
+       struct cds_lfht_node *node_ptr;
+       assert(ht);
+       assert(ht->ht);
+       assert(node);
+
+       node_ptr = cds_lfht_add_unique(ht->ht, ht->hash_fct(node->key, HASH_SEED),
+                       ht->match_fct, node->key, &node->node);
+       assert(node_ptr == &node->node);
+}
+
+/*
+ * Add unique unsigned long node to hashtable.
+ */
+void lttng_ht_add_unique_ulong(struct lttng_ht *ht,
+               struct lttng_ht_node_ulong *node)
+{
+       struct cds_lfht_node *node_ptr;
+       assert(ht);
+       assert(ht->ht);
+       assert(node);
+
+       node_ptr = cds_lfht_add_unique(ht->ht,
+                       ht->hash_fct((void *) node->key, HASH_SEED), ht->match_fct,
+                       (void *) node->key, &node->node);
+       assert(node_ptr == &node->node);
+}
+
+/*
+ * Delete node from hashtable.
+ */
+int lttng_ht_del(struct lttng_ht *ht, struct lttng_ht_iter *iter)
+{
+       assert(ht);
+       assert(ht->ht);
+       assert(iter);
+
+       return cds_lfht_del(ht->ht, iter->iter.node);
+}
+
+/*
+ * Get first node in the hashtable.
+ */
+void lttng_ht_get_first(struct lttng_ht *ht, struct lttng_ht_iter *iter)
+{
+       assert(ht);
+       assert(ht->ht);
+       assert(iter);
+
+       cds_lfht_first(ht->ht, &iter->iter);
+}
+
+/*
+ * Get next node in the hashtable.
+ */
+void lttng_ht_get_next(struct lttng_ht *ht, struct lttng_ht_iter *iter)
+{
+       assert(ht);
+       assert(ht->ht);
+       assert(iter);
+
+       cds_lfht_next(ht->ht, &iter->iter);
+}
+
+/*
+ * Return the number of nodes in the hashtable.
+ */
+unsigned long lttng_ht_get_count(struct lttng_ht *ht)
+{
+       long scb, sca;
+       unsigned long count;
+
+       assert(ht);
+       assert(ht->ht);
+
+       cds_lfht_count_nodes(ht->ht, &scb, &count, &sca);
+
+       return count;
+}
+
+/*
+ * Return lttng ht string node from iterator.
+ */
+struct lttng_ht_node_str *lttng_ht_iter_get_node_str(
+               struct lttng_ht_iter *iter)
+{
+       struct cds_lfht_node *node;
+
+       assert(iter);
+       node = cds_lfht_iter_get_node(&iter->iter);
+       if (!node) {
+               return NULL;
+       }
+       return caa_container_of(node, struct lttng_ht_node_str, node);
+}
+
+/*
+ * Return lttng ht unsigned long node from iterator.
+ */
+struct lttng_ht_node_ulong *lttng_ht_iter_get_node_ulong(
+               struct lttng_ht_iter *iter)
+{
+       struct cds_lfht_node *node;
+
+       assert(iter);
+       node = cds_lfht_iter_get_node(&iter->iter);
+       if (!node) {
+               return NULL;
+       }
+       return caa_container_of(node, struct lttng_ht_node_ulong, node);
+}
diff --git a/liblttng-ht/rculfhash-internal.h b/liblttng-ht/rculfhash-internal.h
new file mode 100644 (file)
index 0000000..cb13ffa
--- /dev/null
@@ -0,0 +1,177 @@
+#ifndef _URCU_RCULFHASH_INTERNAL_H
+#define _URCU_RCULFHASH_INTERNAL_H
+
+/*
+ * urcu/rculfhash-internal.h
+ *
+ * Internal header for Lock-Free RCU Hash Table
+ *
+ * Copyright 2011 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * Copyright 2011 - Lai Jiangshan <laijs@cn.fujitsu.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "rculfhash.h"
+
+#ifdef DEBUG
+#define dbg_printf(fmt, args...)     printf("[debug rculfhash] " fmt, ## args)
+#else
+#define dbg_printf(fmt, args...)
+#endif
+
+#if (CAA_BITS_PER_LONG == 32)
+#define MAX_TABLE_ORDER                        32
+#else
+#define MAX_TABLE_ORDER                        64
+#endif
+
+#define MAX_CHUNK_TABLE                        (1UL << 10)
+
+#ifndef min
+#define min(a, b)      ((a) < (b) ? (a) : (b))
+#endif
+
+#ifndef max
+#define max(a, b)      ((a) > (b) ? (a) : (b))
+#endif
+
+struct ht_items_count;
+
+/*
+ * cds_lfht: Top-level data structure representing a lock-free hash
+ * table. Defined in the implementation file to make it be an opaque
+ * cookie to users.
+ *
+ * The fields used in fast-paths are placed near the end of the
+ * structure, because we need to have a variable-sized union to contain
+ * the mm plugin fields, which are used in the fast path.
+ */
+struct cds_lfht {
+       /* Initial configuration items */
+       unsigned long max_nr_buckets;
+       const struct cds_lfht_mm_type *mm;      /* memory management plugin */
+       const struct rcu_flavor_struct *flavor; /* RCU flavor */
+
+       long count;                     /* global approximate item count */
+
+       /*
+        * We need to put the work threads offline (QSBR) when taking this
+        * mutex, because we use synchronize_rcu within this mutex critical
+        * section, which waits on read-side critical sections, and could
+        * therefore cause grace-period deadlock if we hold off RCU G.P.
+        * completion.
+        */
+       pthread_mutex_t resize_mutex;   /* resize mutex: add/del mutex */
+       pthread_attr_t *resize_attr;    /* Resize threads attributes */
+       unsigned int in_progress_resize, in_progress_destroy;
+       unsigned long resize_target;
+       int resize_initiated;
+
+       /*
+        * Variables needed for add and remove fast-paths.
+        */
+       int flags;
+       unsigned long min_alloc_buckets_order;
+       unsigned long min_nr_alloc_buckets;
+       struct ht_items_count *split_count;     /* split item count */
+
+       /*
+        * Variables needed for the lookup, add and remove fast-paths.
+        */
+       unsigned long size;     /* always a power of 2, shared (RCU) */
+       /*
+        * bucket_at pointer is kept here to skip the extra level of
+        * dereference needed to get to "mm" (this is a fast-path).
+        */
+       struct cds_lfht_node *(*bucket_at)(struct cds_lfht *ht,
+                       unsigned long index);
+       /*
+        * Dynamic length "tbl_chunk" needs to be at the end of
+        * cds_lfht.
+        */
+       union {
+               /*
+                * Contains the per order-index-level bucket node table.
+                * The size of each bucket node table is half the number
+                * of hashes contained in this order (except for order 0).
+                * The minimum allocation buckets size parameter allows
+                * combining the bucket node arrays of the lowermost
+                * levels to improve cache locality for small index orders.
+                */
+               struct cds_lfht_node *tbl_order[MAX_TABLE_ORDER];
+
+               /*
+                * Contains the bucket node chunks. The size of each
+                * bucket node chunk is ->min_alloc_size (we avoid to
+                * allocate chunks with different size). Chunks improve
+                * cache locality for small index orders, and are more
+                * friendly with environments where allocation of large
+                * contiguous memory areas is challenging due to memory
+                * fragmentation concerns or inability to use virtual
+                * memory addressing.
+                */
+               struct cds_lfht_node *tbl_chunk[0];
+
+               /*
+                * Memory mapping with room for all possible buckets.
+                * Their memory is allocated when needed.
+                */
+               struct cds_lfht_node *tbl_mmap;
+       };
+       /*
+        * End of variables needed for the lookup, add and remove
+        * fast-paths.
+        */
+};
+
+extern unsigned int cds_lfht_fls_ulong(unsigned long x);
+extern int cds_lfht_get_count_order_ulong(unsigned long x);
+
+#ifdef POISON_FREE
+#define poison_free(ptr)                                       \
+       do {                                                    \
+               if (ptr) {                                      \
+                       memset(ptr, 0x42, sizeof(*(ptr)));      \
+                       free(ptr);                              \
+               }                                               \
+       } while (0)
+#else
+#define poison_free(ptr)       free(ptr)
+#endif
+
+static inline
+struct cds_lfht *__default_alloc_cds_lfht(
+               const struct cds_lfht_mm_type *mm,
+               unsigned long cds_lfht_size,
+               unsigned long min_nr_alloc_buckets,
+               unsigned long max_nr_buckets)
+{
+       struct cds_lfht *ht;
+
+       ht = calloc(1, cds_lfht_size);
+       assert(ht);
+
+       ht->mm = mm;
+       ht->bucket_at = mm->bucket_at;
+       ht->min_nr_alloc_buckets = min_nr_alloc_buckets;
+       ht->min_alloc_buckets_order =
+               cds_lfht_get_count_order_ulong(min_nr_alloc_buckets);
+       ht->max_nr_buckets = max_nr_buckets;
+
+       return ht;
+}
+
+#endif /* _URCU_RCULFHASH_INTERNAL_H */
diff --git a/liblttng-ht/rculfhash-mm-chunk.c b/liblttng-ht/rculfhash-mm-chunk.c
new file mode 100644 (file)
index 0000000..7204831
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * rculfhash-mm-chunk.c
+ *
+ * Chunk based memory management for Lock-Free RCU Hash Table
+ *
+ * Copyright 2011 - Lai Jiangshan <laijs@cn.fujitsu.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stddef.h>
+#include "rculfhash-internal.h"
+
+static
+void cds_lfht_alloc_bucket_table(struct cds_lfht *ht, unsigned long order)
+{
+       if (order == 0) {
+               ht->tbl_chunk[0] = calloc(ht->min_nr_alloc_buckets,
+                       sizeof(struct cds_lfht_node));
+               assert(ht->tbl_chunk[0]);
+       } else if (order > ht->min_alloc_buckets_order) {
+               unsigned long i, len = 1UL << (order - 1 - ht->min_alloc_buckets_order);
+
+               for (i = len; i < 2 * len; i++) {
+                       ht->tbl_chunk[i] = calloc(ht->min_nr_alloc_buckets,
+                               sizeof(struct cds_lfht_node));
+                       assert(ht->tbl_chunk[i]);
+               }
+       }
+       /* Nothing to do for 0 < order && order <= ht->min_alloc_buckets_order */
+}
+
+/*
+ * cds_lfht_free_bucket_table() should be called with decreasing order.
+ * When cds_lfht_free_bucket_table(0) is called, it means the whole
+ * lfht is destroyed.
+ */
+static
+void cds_lfht_free_bucket_table(struct cds_lfht *ht, unsigned long order)
+{
+       if (order == 0)
+               poison_free(ht->tbl_chunk[0]);
+       else if (order > ht->min_alloc_buckets_order) {
+               unsigned long i, len = 1UL << (order - 1 - ht->min_alloc_buckets_order);
+
+               for (i = len; i < 2 * len; i++)
+                       poison_free(ht->tbl_chunk[i]);
+       }
+       /* Nothing to do for 0 < order && order <= ht->min_alloc_buckets_order */
+}
+
+static
+struct cds_lfht_node *bucket_at(struct cds_lfht *ht, unsigned long index)
+{
+       unsigned long chunk, offset;
+
+       chunk = index >> ht->min_alloc_buckets_order;
+       offset = index & (ht->min_nr_alloc_buckets - 1);
+       return &ht->tbl_chunk[chunk][offset];
+}
+
+static
+struct cds_lfht *alloc_cds_lfht(unsigned long min_nr_alloc_buckets,
+               unsigned long max_nr_buckets)
+{
+       unsigned long nr_chunks, cds_lfht_size;
+
+       min_nr_alloc_buckets = max(min_nr_alloc_buckets,
+                               max_nr_buckets / MAX_CHUNK_TABLE);
+       nr_chunks = max_nr_buckets / min_nr_alloc_buckets;
+       cds_lfht_size = offsetof(struct cds_lfht, tbl_chunk) +
+                       sizeof(struct cds_lfht_node *) * nr_chunks;
+       cds_lfht_size = max(cds_lfht_size, sizeof(struct cds_lfht));
+
+       return __default_alloc_cds_lfht(
+                       &cds_lfht_mm_chunk, cds_lfht_size,
+                       min_nr_alloc_buckets, max_nr_buckets);
+}
+
+const struct cds_lfht_mm_type cds_lfht_mm_chunk = {
+       .alloc_cds_lfht = alloc_cds_lfht,
+       .alloc_bucket_table = cds_lfht_alloc_bucket_table,
+       .free_bucket_table = cds_lfht_free_bucket_table,
+       .bucket_at = bucket_at,
+};
diff --git a/liblttng-ht/rculfhash-mm-mmap.c b/liblttng-ht/rculfhash-mm-mmap.c
new file mode 100644 (file)
index 0000000..4554ed6
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * rculfhash-mm-mmap.c
+ *
+ * mmap/reservation based memory management for Lock-Free RCU Hash Table
+ *
+ * Copyright 2011 - Lai Jiangshan <laijs@cn.fujitsu.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <unistd.h>
+#include <sys/mman.h>
+#include "rculfhash-internal.h"
+
+/* reserve inaccessible memory space without allocation any memory */
+static void *memory_map(size_t length)
+{
+       void *ret = mmap(NULL, length, PROT_NONE,
+                       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+
+       assert(ret != MAP_FAILED);
+       return ret;
+}
+
+static void memory_unmap(void *ptr, size_t length)
+{
+       int ret __attribute__((unused));
+
+       ret = munmap(ptr, length);
+
+       assert(ret == 0);
+}
+
+static void memory_populate(void *ptr, size_t length)
+{
+       void *ret __attribute__((unused));
+
+       ret = mmap(ptr, length, PROT_READ | PROT_WRITE,
+                       MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+
+       assert(ret == ptr);
+}
+
+/*
+ * Discard garbage memory and avoid system save it when try to swap it out.
+ * Make it still reserved, inaccessible.
+ */
+static void memory_discard(void *ptr, size_t length)
+{
+       void *ret __attribute__((unused));
+
+       ret = mmap(ptr, length, PROT_NONE,
+                       MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+
+       assert(ret == ptr);
+}
+
+static
+void cds_lfht_alloc_bucket_table(struct cds_lfht *ht, unsigned long order)
+{
+       if (order == 0) {
+               if (ht->min_nr_alloc_buckets == ht->max_nr_buckets) {
+                       /* small table */
+                       ht->tbl_mmap = calloc(ht->max_nr_buckets,
+                                       sizeof(*ht->tbl_mmap));
+                       assert(ht->tbl_mmap);
+                       return;
+               }
+               /* large table */
+               ht->tbl_mmap = memory_map(ht->max_nr_buckets
+                       * sizeof(*ht->tbl_mmap));
+               memory_populate(ht->tbl_mmap,
+                       ht->min_nr_alloc_buckets * sizeof(*ht->tbl_mmap));
+       } else if (order > ht->min_alloc_buckets_order) {
+               /* large table */
+               unsigned long len = 1UL << (order - 1);
+
+               assert(ht->min_nr_alloc_buckets < ht->max_nr_buckets);
+               memory_populate(ht->tbl_mmap + len,
+                               len * sizeof(*ht->tbl_mmap));
+       }
+       /* Nothing to do for 0 < order && order <= ht->min_alloc_buckets_order */
+}
+
+/*
+ * cds_lfht_free_bucket_table() should be called with decreasing order.
+ * When cds_lfht_free_bucket_table(0) is called, it means the whole
+ * lfht is destroyed.
+ */
+static
+void cds_lfht_free_bucket_table(struct cds_lfht *ht, unsigned long order)
+{
+       if (order == 0) {
+               if (ht->min_nr_alloc_buckets == ht->max_nr_buckets) {
+                       /* small table */
+                       poison_free(ht->tbl_mmap);
+                       return;
+               }
+               /* large table */
+               memory_unmap(ht->tbl_mmap,
+                       ht->max_nr_buckets * sizeof(*ht->tbl_mmap));
+       } else if (order > ht->min_alloc_buckets_order) {
+               /* large table */
+               unsigned long len = 1UL << (order - 1);
+
+               assert(ht->min_nr_alloc_buckets < ht->max_nr_buckets);
+               memory_discard(ht->tbl_mmap + len, len * sizeof(*ht->tbl_mmap));
+       }
+       /* Nothing to do for 0 < order && order <= ht->min_alloc_buckets_order */
+}
+
+static
+struct cds_lfht_node *bucket_at(struct cds_lfht *ht, unsigned long index)
+{
+       return &ht->tbl_mmap[index];
+}
+
+static
+struct cds_lfht *alloc_cds_lfht(unsigned long min_nr_alloc_buckets,
+               unsigned long max_nr_buckets)
+{
+       unsigned long page_bucket_size;
+
+       page_bucket_size = getpagesize() / sizeof(struct cds_lfht_node);
+       if (max_nr_buckets <= page_bucket_size) {
+               /* small table */
+               min_nr_alloc_buckets = max_nr_buckets;
+       } else {
+               /* large table */
+               min_nr_alloc_buckets = max(min_nr_alloc_buckets,
+                                       page_bucket_size);
+       }
+
+       return __default_alloc_cds_lfht(
+                       &cds_lfht_mm_mmap, sizeof(struct cds_lfht),
+                       min_nr_alloc_buckets, max_nr_buckets);
+}
+
+const struct cds_lfht_mm_type cds_lfht_mm_mmap = {
+       .alloc_cds_lfht = alloc_cds_lfht,
+       .alloc_bucket_table = cds_lfht_alloc_bucket_table,
+       .free_bucket_table = cds_lfht_free_bucket_table,
+       .bucket_at = bucket_at,
+};
diff --git a/liblttng-ht/rculfhash-mm-order.c b/liblttng-ht/rculfhash-mm-order.c
new file mode 100644 (file)
index 0000000..6e3d29b
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * rculfhash-mm-order.c
+ *
+ * Order based memory management for Lock-Free RCU Hash Table
+ *
+ * Copyright 2011 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * Copyright 2011 - Lai Jiangshan <laijs@cn.fujitsu.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "rculfhash-internal.h"
+
+static
+void cds_lfht_alloc_bucket_table(struct cds_lfht *ht, unsigned long order)
+{
+       if (order == 0) {
+               ht->tbl_order[0] = calloc(ht->min_nr_alloc_buckets,
+                       sizeof(struct cds_lfht_node));
+               assert(ht->tbl_order[0]);
+       } else if (order > ht->min_alloc_buckets_order) {
+               ht->tbl_order[order] = calloc(1UL << (order -1),
+                       sizeof(struct cds_lfht_node));
+               assert(ht->tbl_order[order]);
+       }
+       /* Nothing to do for 0 < order && order <= ht->min_alloc_buckets_order */
+}
+
+/*
+ * cds_lfht_free_bucket_table() should be called with decreasing order.
+ * When cds_lfht_free_bucket_table(0) is called, it means the whole
+ * lfht is destroyed.
+ */
+static
+void cds_lfht_free_bucket_table(struct cds_lfht *ht, unsigned long order)
+{
+       if (order == 0)
+               poison_free(ht->tbl_order[0]);
+       else if (order > ht->min_alloc_buckets_order)
+               poison_free(ht->tbl_order[order]);
+       /* Nothing to do for 0 < order && order <= ht->min_alloc_buckets_order */
+}
+
+static
+struct cds_lfht_node *bucket_at(struct cds_lfht *ht, unsigned long index)
+{
+       unsigned long order;
+
+       if (index < ht->min_nr_alloc_buckets) {
+               dbg_printf("bucket index %lu order 0 aridx 0\n", index);
+               return &ht->tbl_order[0][index];
+       }
+       /*
+        * equivalent to cds_lfht_get_count_order_ulong(index + 1), but
+        * optimizes away the non-existing 0 special-case for
+        * cds_lfht_get_count_order_ulong.
+        */
+       order = cds_lfht_fls_ulong(index);
+       dbg_printf("bucket index %lu order %lu aridx %lu\n",
+                  index, order, index & ((1UL << (order - 1)) - 1));
+       return &ht->tbl_order[order][index & ((1UL << (order - 1)) - 1)];
+}
+
+static
+struct cds_lfht *alloc_cds_lfht(unsigned long min_nr_alloc_buckets,
+               unsigned long max_nr_buckets)
+{
+       return __default_alloc_cds_lfht(
+                       &cds_lfht_mm_order, sizeof(struct cds_lfht),
+                       min_nr_alloc_buckets, max_nr_buckets);
+}
+
+const struct cds_lfht_mm_type cds_lfht_mm_order = {
+       .alloc_cds_lfht = alloc_cds_lfht,
+       .alloc_bucket_table = cds_lfht_alloc_bucket_table,
+       .free_bucket_table = cds_lfht_free_bucket_table,
+       .bucket_at = bucket_at,
+};
diff --git a/liblttng-ht/rculfhash.c b/liblttng-ht/rculfhash.c
new file mode 100644 (file)
index 0000000..840de35
--- /dev/null
@@ -0,0 +1,1832 @@
+/*
+ * rculfhash.c
+ *
+ * Userspace RCU library - Lock-Free Resizable RCU Hash Table
+ *
+ * Copyright 2010-2011 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * Copyright 2011 - Lai Jiangshan <laijs@cn.fujitsu.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * Based on the following articles:
+ * - Ori Shalev and Nir Shavit. Split-ordered lists: Lock-free
+ *   extensible hash tables. J. ACM 53, 3 (May 2006), 379-405.
+ * - Michael, M. M. High performance dynamic lock-free hash tables
+ *   and list-based sets. In Proceedings of the fourteenth annual ACM
+ *   symposium on Parallel algorithms and architectures, ACM Press,
+ *   (2002), 73-82.
+ *
+ * Some specificities of this Lock-Free Resizable RCU Hash Table
+ * implementation:
+ *
+ * - RCU read-side critical section allows readers to perform hash
+ *   table lookups and use the returned objects safely by delaying
+ *   memory reclaim of a grace period.
+ * - Add and remove operations are lock-free, and do not need to
+ *   allocate memory. They need to be executed within RCU read-side
+ *   critical section to ensure the objects they read are valid and to
+ *   deal with the cmpxchg ABA problem.
+ * - add and add_unique operations are supported. add_unique checks if
+ *   the node key already exists in the hash table. It ensures no key
+ *   duplicata exists.
+ * - The resize operation executes concurrently with add/remove/lookup.
+ * - Hash table nodes are contained within a split-ordered list. This
+ *   list is ordered by incrementing reversed-bits-hash value.
+ * - An index of bucket nodes is kept. These bucket nodes are the hash
+ *   table "buckets", and they are also chained together in the
+ *   split-ordered list, which allows recursive expansion.
+ * - The resize operation for small tables only allows expanding the hash table.
+ *   It is triggered automatically by detecting long chains in the add
+ *   operation.
+ * - The resize operation for larger tables (and available through an
+ *   API) allows both expanding and shrinking the hash table.
+ * - Split-counters are used to keep track of the number of
+ *   nodes within the hash table for automatic resize triggering.
+ * - Resize operation initiated by long chain detection is executed by a
+ *   call_rcu thread, which keeps lock-freedom of add and remove.
+ * - Resize operations are protected by a mutex.
+ * - The removal operation is split in two parts: first, a "removed"
+ *   flag is set in the next pointer within the node to remove. Then,
+ *   a "garbage collection" is performed in the bucket containing the
+ *   removed node (from the start of the bucket up to the removed node).
+ *   All encountered nodes with "removed" flag set in their next
+ *   pointers are removed from the linked-list. If the cmpxchg used for
+ *   removal fails (due to concurrent garbage-collection or concurrent
+ *   add), we retry from the beginning of the bucket. This ensures that
+ *   the node with "removed" flag set is removed from the hash table
+ *   (not visible to lookups anymore) before the RCU read-side critical
+ *   section held across removal ends. Furthermore, this ensures that
+ *   the node with "removed" flag set is removed from the linked-list
+ *   before its memory is reclaimed. Only the thread which removal
+ *   successfully set the "removed" flag (with a cmpxchg) into a node's
+ *   next pointer is considered to have succeeded its removal (and thus
+ *   owns the node to reclaim). Because we garbage-collect starting from
+ *   an invariant node (the start-of-bucket bucket node) up to the
+ *   "removed" node (or find a reverse-hash that is higher), we are sure
+ *   that a successful traversal of the chain leads to a chain that is
+ *   present in the linked-list (the start node is never removed) and
+ *   that is does not contain the "removed" node anymore, even if
+ *   concurrent delete/add operations are changing the structure of the
+ *   list concurrently.
+ * - The add operation performs gargage collection of buckets if it
+ *   encounters nodes with removed flag set in the bucket where it wants
+ *   to add its new node. This ensures lock-freedom of add operation by
+ *   helping the remover unlink nodes from the list rather than to wait
+ *   for it do to so.
+ * - A RCU "order table" indexed by log2(hash index) is copied and
+ *   expanded by the resize operation. This order table allows finding
+ *   the "bucket node" tables.
+ * - There is one bucket node table per hash index order. The size of
+ *   each bucket node table is half the number of hashes contained in
+ *   this order (except for order 0).
+ * - synchronzie_rcu is used to garbage-collect the old bucket node table.
+ * - The per-order bucket node tables contain a compact version of the
+ *   hash table nodes. These tables are invariant after they are
+ *   populated into the hash table.
+ *
+ * Bucket node tables:
+ *
+ * hash table  hash table      the last        all bucket node tables
+ * order       size            bucket node     0   1   2   3   4   5   6(index)
+ *                             table size
+ * 0           1               1               1
+ * 1           2               1               1   1
+ * 2           4               2               1   1   2
+ * 3           8               4               1   1   2   4
+ * 4           16              8               1   1   2   4   8
+ * 5           32              16              1   1   2   4   8  16
+ * 6           64              32              1   1   2   4   8  16  32
+ *
+ * When growing/shrinking, we only focus on the last bucket node table
+ * which size is (!order ? 1 : (1 << (order -1))).
+ *
+ * Example for growing/shrinking:
+ * grow hash table from order 5 to 6: init the index=6 bucket node table
+ * shrink hash table from order 6 to 5: fini the index=6 bucket node table
+ *
+ * A bit of ascii art explanation:
+ *
+ * Order index is the off-by-one compare to the actual power of 2 because
+ * we use index 0 to deal with the 0 special-case.
+ *
+ * This shows the nodes for a small table ordered by reversed bits:
+ *
+ *    bits   reverse
+ * 0  000        000
+ * 4  100        001
+ * 2  010        010
+ * 6  110        011
+ * 1  001        100
+ * 5  101        101
+ * 3  011        110
+ * 7  111        111
+ *
+ * This shows the nodes in order of non-reversed bits, linked by
+ * reversed-bit order.
+ *
+ * order              bits       reverse
+ * 0               0  000        000
+ * 1               |  1  001        100             <-
+ * 2               |  |  2  010        010    <-     |
+ *                 |  |  |  3  011        110  | <-  |
+ * 3               -> |  |  |  4  100        001  |  |
+ *                    -> |  |     5  101        101  |
+ *                       -> |        6  110        011
+ *                          ->          7  111        111
+ */
+
+#define _LGPL_SOURCE
+#include <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "config.h"
+#include <urcu.h>
+#include <urcu-call-rcu.h>
+#include <urcu/arch.h>
+#include <urcu/uatomic.h>
+#include <urcu/compiler.h>
+#include <stdio.h>
+#include <pthread.h>
+
+#include "rculfhash.h"
+#include "rculfhash-internal.h"
+#include "urcu-flavor.h"
+
+/*
+ * Split-counters lazily update the global counter each 1024
+ * addition/removal. It automatically keeps track of resize required.
+ * We use the bucket length as indicator for need to expand for small
+ * tables and machines lacking per-cpu data suppport.
+ */
+#define COUNT_COMMIT_ORDER             10
+#define DEFAULT_SPLIT_COUNT_MASK       0xFUL
+#define CHAIN_LEN_TARGET               1
+#define CHAIN_LEN_RESIZE_THRESHOLD     3
+
+/*
+ * Define the minimum table size.
+ */
+#define MIN_TABLE_ORDER                        0
+#define MIN_TABLE_SIZE                 (1UL << MIN_TABLE_ORDER)
+
+/*
+ * Minimum number of bucket nodes to touch per thread to parallelize grow/shrink.
+ */
+#define MIN_PARTITION_PER_THREAD_ORDER 12
+#define MIN_PARTITION_PER_THREAD       (1UL << MIN_PARTITION_PER_THREAD_ORDER)
+
+/*
+ * The removed flag needs to be updated atomically with the pointer.
+ * It indicates that no node must attach to the node scheduled for
+ * removal, and that node garbage collection must be performed.
+ * The bucket flag does not require to be updated atomically with the
+ * pointer, but it is added as a pointer low bit flag to save space.
+ */
+#define REMOVED_FLAG           (1UL << 0)
+#define BUCKET_FLAG            (1UL << 1)
+#define REMOVAL_OWNER_FLAG     (1UL << 2)
+#define FLAGS_MASK             ((1UL << 3) - 1)
+
+/* Value of the end pointer. Should not interact with flags. */
+#define END_VALUE              NULL
+
+DEFINE_RCU_FLAVOR(rcu_flavor);
+
+/*
+ * ht_items_count: Split-counters counting the number of node addition
+ * and removal in the table. Only used if the CDS_LFHT_ACCOUNTING flag
+ * is set at hash table creation.
+ *
+ * These are free-running counters, never reset to zero. They count the
+ * number of add/remove, and trigger every (1 << COUNT_COMMIT_ORDER)
+ * operations to update the global counter. We choose a power-of-2 value
+ * for the trigger to deal with 32 or 64-bit overflow of the counter.
+ */
+struct ht_items_count {
+       unsigned long add, del;
+} __attribute__((aligned(CAA_CACHE_LINE_SIZE)));
+
+/*
+ * rcu_resize_work: Contains arguments passed to RCU worker thread
+ * responsible for performing lazy resize.
+ */
+struct rcu_resize_work {
+       struct rcu_head head;
+       struct cds_lfht *ht;
+};
+
+/*
+ * partition_resize_work: Contains arguments passed to worker threads
+ * executing the hash table resize on partitions of the hash table
+ * assigned to each processor's worker thread.
+ */
+struct partition_resize_work {
+       pthread_t thread_id;
+       struct cds_lfht *ht;
+       unsigned long i, start, len;
+       void (*fct)(struct cds_lfht *ht, unsigned long i,
+                   unsigned long start, unsigned long len);
+};
+
+/*
+ * Algorithm to reverse bits in a word by lookup table, extended to
+ * 64-bit words.
+ * Source:
+ * http://graphics.stanford.edu/~seander/bithacks.html#BitReverseTable
+ * Originally from Public Domain.
+ */
+
+static const uint8_t BitReverseTable256[256] =
+{
+#define R2(n) (n),   (n) + 2*64,     (n) + 1*64,     (n) + 3*64
+#define R4(n) R2(n), R2((n) + 2*16), R2((n) + 1*16), R2((n) + 3*16)
+#define R6(n) R4(n), R4((n) + 2*4 ), R4((n) + 1*4 ), R4((n) + 3*4 )
+       R6(0), R6(2), R6(1), R6(3)
+};
+#undef R2
+#undef R4
+#undef R6
+
+static
+uint8_t bit_reverse_u8(uint8_t v)
+{
+       return BitReverseTable256[v];
+}
+
+static __attribute__((unused))
+uint32_t bit_reverse_u32(uint32_t v)
+{
+       return ((uint32_t) bit_reverse_u8(v) << 24) |
+               ((uint32_t) bit_reverse_u8(v >> 8) << 16) |
+               ((uint32_t) bit_reverse_u8(v >> 16) << 8) |
+               ((uint32_t) bit_reverse_u8(v >> 24));
+}
+
+static __attribute__((unused))
+uint64_t bit_reverse_u64(uint64_t v)
+{
+       return ((uint64_t) bit_reverse_u8(v) << 56) |
+               ((uint64_t) bit_reverse_u8(v >> 8)  << 48) |
+               ((uint64_t) bit_reverse_u8(v >> 16) << 40) |
+               ((uint64_t) bit_reverse_u8(v >> 24) << 32) |
+               ((uint64_t) bit_reverse_u8(v >> 32) << 24) |
+               ((uint64_t) bit_reverse_u8(v >> 40) << 16) |
+               ((uint64_t) bit_reverse_u8(v >> 48) << 8) |
+               ((uint64_t) bit_reverse_u8(v >> 56));
+}
+
+static
+unsigned long bit_reverse_ulong(unsigned long v)
+{
+#if (CAA_BITS_PER_LONG == 32)
+       return bit_reverse_u32(v);
+#else
+       return bit_reverse_u64(v);
+#endif
+}
+
+/*
+ * fls: returns the position of the most significant bit.
+ * Returns 0 if no bit is set, else returns the position of the most
+ * significant bit (from 1 to 32 on 32-bit, from 1 to 64 on 64-bit).
+ */
+#if defined(__i386) || defined(__x86_64)
+static inline
+unsigned int fls_u32(uint32_t x)
+{
+       int r;
+
+       asm("bsrl %1,%0\n\t"
+           "jnz 1f\n\t"
+           "movl $-1,%0\n\t"
+           "1:\n\t"
+           : "=r" (r) : "rm" (x));
+       return r + 1;
+}
+#define HAS_FLS_U32
+#endif
+
+#if defined(__x86_64)
+static inline
+unsigned int fls_u64(uint64_t x)
+{
+       long r;
+
+       asm("bsrq %1,%0\n\t"
+           "jnz 1f\n\t"
+           "movq $-1,%0\n\t"
+           "1:\n\t"
+           : "=r" (r) : "rm" (x));
+       return r + 1;
+}
+#define HAS_FLS_U64
+#endif
+
+#ifndef HAS_FLS_U64
+static __attribute__((unused))
+unsigned int fls_u64(uint64_t x)
+{
+       unsigned int r = 64;
+
+       if (!x)
+               return 0;
+
+       if (!(x & 0xFFFFFFFF00000000ULL)) {
+               x <<= 32;
+               r -= 32;
+       }
+       if (!(x & 0xFFFF000000000000ULL)) {
+               x <<= 16;
+               r -= 16;
+       }
+       if (!(x & 0xFF00000000000000ULL)) {
+               x <<= 8;
+               r -= 8;
+       }
+       if (!(x & 0xF000000000000000ULL)) {
+               x <<= 4;
+               r -= 4;
+       }
+       if (!(x & 0xC000000000000000ULL)) {
+               x <<= 2;
+               r -= 2;
+       }
+       if (!(x & 0x8000000000000000ULL)) {
+               x <<= 1;
+               r -= 1;
+       }
+       return r;
+}
+#endif
+
+#ifndef HAS_FLS_U32
+static __attribute__((unused))
+unsigned int fls_u32(uint32_t x)
+{
+       unsigned int r = 32;
+
+       if (!x)
+               return 0;
+       if (!(x & 0xFFFF0000U)) {
+               x <<= 16;
+               r -= 16;
+       }
+       if (!(x & 0xFF000000U)) {
+               x <<= 8;
+               r -= 8;
+       }
+       if (!(x & 0xF0000000U)) {
+               x <<= 4;
+               r -= 4;
+       }
+       if (!(x & 0xC0000000U)) {
+               x <<= 2;
+               r -= 2;
+       }
+       if (!(x & 0x80000000U)) {
+               x <<= 1;
+               r -= 1;
+       }
+       return r;
+}
+#endif
+
+unsigned int cds_lfht_fls_ulong(unsigned long x)
+{
+#if (CAA_BITS_PER_LONG == 32)
+       return fls_u32(x);
+#else
+       return fls_u64(x);
+#endif
+}
+
+/*
+ * Return the minimum order for which x <= (1UL << order).
+ * Return -1 if x is 0.
+ */
+int cds_lfht_get_count_order_u32(uint32_t x)
+{
+       if (!x)
+               return -1;
+
+       return fls_u32(x - 1);
+}
+
+/*
+ * Return the minimum order for which x <= (1UL << order).
+ * Return -1 if x is 0.
+ */
+int cds_lfht_get_count_order_ulong(unsigned long x)
+{
+       if (!x)
+               return -1;
+
+       return cds_lfht_fls_ulong(x - 1);
+}
+
+static
+void cds_lfht_resize_lazy_grow(struct cds_lfht *ht, unsigned long size, int growth);
+
+static
+void cds_lfht_resize_lazy_count(struct cds_lfht *ht, unsigned long size,
+                               unsigned long count);
+
+static long nr_cpus_mask = -1;
+static long split_count_mask = -1;
+
+#if defined(HAVE_SYSCONF)
+static void ht_init_nr_cpus_mask(void)
+{
+       long maxcpus;
+
+       maxcpus = sysconf(_SC_NPROCESSORS_CONF);
+       if (maxcpus <= 0) {
+               nr_cpus_mask = -2;
+               return;
+       }
+       /*
+        * round up number of CPUs to next power of two, so we
+        * can use & for modulo.
+        */
+       maxcpus = 1UL << cds_lfht_get_count_order_ulong(maxcpus);
+       nr_cpus_mask = maxcpus - 1;
+}
+#else /* #if defined(HAVE_SYSCONF) */
+static void ht_init_nr_cpus_mask(void)
+{
+       nr_cpus_mask = -2;
+}
+#endif /* #else #if defined(HAVE_SYSCONF) */
+
+static
+void alloc_split_items_count(struct cds_lfht *ht)
+{
+       struct ht_items_count *count;
+
+       if (nr_cpus_mask == -1) {
+               ht_init_nr_cpus_mask();
+               if (nr_cpus_mask < 0)
+                       split_count_mask = DEFAULT_SPLIT_COUNT_MASK;
+               else
+                       split_count_mask = nr_cpus_mask;
+       }
+
+       assert(split_count_mask >= 0);
+
+       if (ht->flags & CDS_LFHT_ACCOUNTING) {
+               ht->split_count = calloc(split_count_mask + 1, sizeof(*count));
+               assert(ht->split_count);
+       } else {
+               ht->split_count = NULL;
+       }
+}
+
+static
+void free_split_items_count(struct cds_lfht *ht)
+{
+       poison_free(ht->split_count);
+}
+
+#if defined(HAVE_SCHED_GETCPU)
+static
+int ht_get_split_count_index(unsigned long hash)
+{
+       int cpu;
+
+       assert(split_count_mask >= 0);
+       cpu = sched_getcpu();
+       if (caa_unlikely(cpu < 0))
+               return hash & split_count_mask;
+       else
+               return cpu & split_count_mask;
+}
+#else /* #if defined(HAVE_SCHED_GETCPU) */
+static
+int ht_get_split_count_index(unsigned long hash)
+{
+       return hash & split_count_mask;
+}
+#endif /* #else #if defined(HAVE_SCHED_GETCPU) */
+
+static
+void ht_count_add(struct cds_lfht *ht, unsigned long size, unsigned long hash)
+{
+       unsigned long split_count;
+       int index;
+       long count;
+
+       if (caa_unlikely(!ht->split_count))
+               return;
+       index = ht_get_split_count_index(hash);
+       split_count = uatomic_add_return(&ht->split_count[index].add, 1);
+       if (caa_likely(split_count & ((1UL << COUNT_COMMIT_ORDER) - 1)))
+               return;
+       /* Only if number of add multiple of 1UL << COUNT_COMMIT_ORDER */
+
+       dbg_printf("add split count %lu\n", split_count);
+       count = uatomic_add_return(&ht->count,
+                                  1UL << COUNT_COMMIT_ORDER);
+       if (caa_likely(count & (count - 1)))
+               return;
+       /* Only if global count is power of 2 */
+
+       if ((count >> CHAIN_LEN_RESIZE_THRESHOLD) < size)
+               return;
+       dbg_printf("add set global %ld\n", count);
+       cds_lfht_resize_lazy_count(ht, size,
+               count >> (CHAIN_LEN_TARGET - 1));
+}
+
+static
+void ht_count_del(struct cds_lfht *ht, unsigned long size, unsigned long hash)
+{
+       unsigned long split_count;
+       int index;
+       long count;
+
+       if (caa_unlikely(!ht->split_count))
+               return;
+       index = ht_get_split_count_index(hash);
+       split_count = uatomic_add_return(&ht->split_count[index].del, 1);
+       if (caa_likely(split_count & ((1UL << COUNT_COMMIT_ORDER) - 1)))
+               return;
+       /* Only if number of deletes multiple of 1UL << COUNT_COMMIT_ORDER */
+
+       dbg_printf("del split count %lu\n", split_count);
+       count = uatomic_add_return(&ht->count,
+                                  -(1UL << COUNT_COMMIT_ORDER));
+       if (caa_likely(count & (count - 1)))
+               return;
+       /* Only if global count is power of 2 */
+
+       if ((count >> CHAIN_LEN_RESIZE_THRESHOLD) >= size)
+               return;
+       dbg_printf("del set global %ld\n", count);
+       /*
+        * Don't shrink table if the number of nodes is below a
+        * certain threshold.
+        */
+       if (count < (1UL << COUNT_COMMIT_ORDER) * (split_count_mask + 1))
+               return;
+       cds_lfht_resize_lazy_count(ht, size,
+               count >> (CHAIN_LEN_TARGET - 1));
+}
+
+static
+void check_resize(struct cds_lfht *ht, unsigned long size, uint32_t chain_len)
+{
+       unsigned long count;
+
+       if (!(ht->flags & CDS_LFHT_AUTO_RESIZE))
+               return;
+       count = uatomic_read(&ht->count);
+       /*
+        * Use bucket-local length for small table expand and for
+        * environments lacking per-cpu data support.
+        */
+       if (count >= (1UL << COUNT_COMMIT_ORDER))
+               return;
+       if (chain_len > 100)
+               dbg_printf("WARNING: large chain length: %u.\n",
+                          chain_len);
+       if (chain_len >= CHAIN_LEN_RESIZE_THRESHOLD)
+               cds_lfht_resize_lazy_grow(ht, size,
+                       cds_lfht_get_count_order_u32(chain_len - (CHAIN_LEN_TARGET - 1)));
+}
+
+static
+struct cds_lfht_node *clear_flag(struct cds_lfht_node *node)
+{
+       return (struct cds_lfht_node *) (((unsigned long) node) & ~FLAGS_MASK);
+}
+
+static
+int is_removed(struct cds_lfht_node *node)
+{
+       return ((unsigned long) node) & REMOVED_FLAG;
+}
+
+static
+struct cds_lfht_node *flag_removed(struct cds_lfht_node *node)
+{
+       return (struct cds_lfht_node *) (((unsigned long) node) | REMOVED_FLAG);
+}
+
+static
+int is_bucket(struct cds_lfht_node *node)
+{
+       return ((unsigned long) node) & BUCKET_FLAG;
+}
+
+static
+struct cds_lfht_node *flag_bucket(struct cds_lfht_node *node)
+{
+       return (struct cds_lfht_node *) (((unsigned long) node) | BUCKET_FLAG);
+}
+
+static
+int is_removal_owner(struct cds_lfht_node *node)
+{
+       return ((unsigned long) node) & REMOVAL_OWNER_FLAG;
+}
+
+static
+struct cds_lfht_node *flag_removal_owner(struct cds_lfht_node *node)
+{
+       return (struct cds_lfht_node *) (((unsigned long) node) | REMOVAL_OWNER_FLAG);
+}
+
+static
+struct cds_lfht_node *get_end(void)
+{
+       return (struct cds_lfht_node *) END_VALUE;
+}
+
+static
+int is_end(struct cds_lfht_node *node)
+{
+       return clear_flag(node) == (struct cds_lfht_node *) END_VALUE;
+}
+
+static
+unsigned long _uatomic_xchg_monotonic_increase(unsigned long *ptr,
+               unsigned long v)
+{
+       unsigned long old1, old2;
+
+       old1 = uatomic_read(ptr);
+       do {
+               old2 = old1;
+               if (old2 >= v)
+                       return old2;
+       } while ((old1 = uatomic_cmpxchg(ptr, old2, v)) != old2);
+       return old2;
+}
+
+static
+void cds_lfht_alloc_bucket_table(struct cds_lfht *ht, unsigned long order)
+{
+       return ht->mm->alloc_bucket_table(ht, order);
+}
+
+/*
+ * cds_lfht_free_bucket_table() should be called with decreasing order.
+ * When cds_lfht_free_bucket_table(0) is called, it means the whole
+ * lfht is destroyed.
+ */
+static
+void cds_lfht_free_bucket_table(struct cds_lfht *ht, unsigned long order)
+{
+       return ht->mm->free_bucket_table(ht, order);
+}
+
+static inline
+struct cds_lfht_node *bucket_at(struct cds_lfht *ht, unsigned long index)
+{
+       return ht->bucket_at(ht, index);
+}
+
+static inline
+struct cds_lfht_node *lookup_bucket(struct cds_lfht *ht, unsigned long size,
+               unsigned long hash)
+{
+       assert(size > 0);
+       return bucket_at(ht, hash & (size - 1));
+}
+
+/*
+ * Remove all logically deleted nodes from a bucket up to a certain node key.
+ */
+static
+void _cds_lfht_gc_bucket(struct cds_lfht_node *bucket, struct cds_lfht_node *node)
+{
+       struct cds_lfht_node *iter_prev, *iter, *next, *new_next;
+
+       assert(!is_bucket(bucket));
+       assert(!is_removed(bucket));
+       assert(!is_bucket(node));
+       assert(!is_removed(node));
+       for (;;) {
+               iter_prev = bucket;
+               /* We can always skip the bucket node initially */
+               iter = rcu_dereference(iter_prev->next);
+               assert(!is_removed(iter));
+               assert(iter_prev->reverse_hash <= node->reverse_hash);
+               /*
+                * We should never be called with bucket (start of chain)
+                * and logically removed node (end of path compression
+                * marker) being the actual same node. This would be a
+                * bug in the algorithm implementation.
+                */
+               assert(bucket != node);
+               for (;;) {
+                       if (caa_unlikely(is_end(iter)))
+                               return;
+                       if (caa_likely(clear_flag(iter)->reverse_hash > node->reverse_hash))
+                               return;
+                       next = rcu_dereference(clear_flag(iter)->next);
+                       if (caa_likely(is_removed(next)))
+                               break;
+                       iter_prev = clear_flag(iter);
+                       iter = next;
+               }
+               assert(!is_removed(iter));
+               if (is_bucket(iter))
+                       new_next = flag_bucket(clear_flag(next));
+               else
+                       new_next = clear_flag(next);
+               (void) uatomic_cmpxchg(&iter_prev->next, iter, new_next);
+       }
+}
+
+static
+int _cds_lfht_replace(struct cds_lfht *ht, unsigned long size,
+               struct cds_lfht_node *old_node,
+               struct cds_lfht_node *old_next,
+               struct cds_lfht_node *new_node)
+{
+       struct cds_lfht_node *bucket, *ret_next;
+
+       if (!old_node)  /* Return -ENOENT if asked to replace NULL node */
+               return -ENOENT;
+
+       assert(!is_removed(old_node));
+       assert(!is_bucket(old_node));
+       assert(!is_removed(new_node));
+       assert(!is_bucket(new_node));
+       assert(new_node != old_node);
+       for (;;) {
+               /* Insert after node to be replaced */
+               if (is_removed(old_next)) {
+                       /*
+                        * Too late, the old node has been removed under us
+                        * between lookup and replace. Fail.
+                        */
+                       return -ENOENT;
+               }
+               assert(old_next == clear_flag(old_next));
+               assert(new_node != old_next);
+               new_node->next = old_next;
+               /*
+                * Here is the whole trick for lock-free replace: we add
+                * the replacement node _after_ the node we want to
+                * replace by atomically setting its next pointer at the
+                * same time we set its removal flag. Given that
+                * the lookups/get next use an iterator aware of the
+                * next pointer, they will either skip the old node due
+                * to the removal flag and see the new node, or use
+                * the old node, but will not see the new one.
+                * This is a replacement of a node with another node
+                * that has the same value: we are therefore not
+                * removing a value from the hash table.
+                */
+               ret_next = uatomic_cmpxchg(&old_node->next,
+                             old_next, flag_removed(new_node));
+               if (ret_next == old_next)
+                       break;          /* We performed the replacement. */
+               old_next = ret_next;
+       }
+
+       /*
+        * Ensure that the old node is not visible to readers anymore:
+        * lookup for the node, and remove it (along with any other
+        * logically removed node) if found.
+        */
+       bucket = lookup_bucket(ht, size, bit_reverse_ulong(old_node->reverse_hash));
+       _cds_lfht_gc_bucket(bucket, new_node);
+
+       assert(is_removed(rcu_dereference(old_node->next)));
+       return 0;
+}
+
+/*
+ * A non-NULL unique_ret pointer uses the "add unique" (or uniquify) add
+ * mode. A NULL unique_ret allows creation of duplicate keys.
+ */
+static
+void _cds_lfht_add(struct cds_lfht *ht,
+               unsigned long hash,
+               cds_lfht_match_fct match,
+               const void *key,
+               unsigned long size,
+               struct cds_lfht_node *node,
+               struct cds_lfht_iter *unique_ret,
+               int bucket_flag)
+{
+       struct cds_lfht_node *iter_prev, *iter, *next, *new_node, *new_next,
+                       *return_node;
+       struct cds_lfht_node *bucket;
+
+       assert(!is_bucket(node));
+       assert(!is_removed(node));
+       bucket = lookup_bucket(ht, size, hash);
+       for (;;) {
+               uint32_t chain_len = 0;
+
+               /*
+                * iter_prev points to the non-removed node prior to the
+                * insert location.
+                */
+               iter_prev = bucket;
+               /* We can always skip the bucket node initially */
+               iter = rcu_dereference(iter_prev->next);
+               assert(iter_prev->reverse_hash <= node->reverse_hash);
+               for (;;) {
+                       if (caa_unlikely(is_end(iter)))
+                               goto insert;
+                       if (caa_likely(clear_flag(iter)->reverse_hash > node->reverse_hash))
+                               goto insert;
+
+                       /* bucket node is the first node of the identical-hash-value chain */
+                       if (bucket_flag && clear_flag(iter)->reverse_hash == node->reverse_hash)
+                               goto insert;
+
+                       next = rcu_dereference(clear_flag(iter)->next);
+                       if (caa_unlikely(is_removed(next)))
+                               goto gc_node;
+
+                       /* uniquely add */
+                       if (unique_ret
+                           && !is_bucket(next)
+                           && clear_flag(iter)->reverse_hash == node->reverse_hash) {
+                               struct cds_lfht_iter d_iter = { .node = node, .next = iter, };
+
+                               /*
+                                * uniquely adding inserts the node as the first
+                                * node of the identical-hash-value node chain.
+                                *
+                                * This semantic ensures no duplicated keys
+                                * should ever be observable in the table
+                                * (including observe one node by one node
+                                * by forward iterations)
+                                */
+                               cds_lfht_next_duplicate(ht, match, key, &d_iter);
+                               if (!d_iter.node)
+                                       goto insert;
+
+                               *unique_ret = d_iter;
+                               return;
+                       }
+
+                       /* Only account for identical reverse hash once */
+                       if (iter_prev->reverse_hash != clear_flag(iter)->reverse_hash
+                           && !is_bucket(next))
+                               check_resize(ht, size, ++chain_len);
+                       iter_prev = clear_flag(iter);
+                       iter = next;
+               }
+
+       insert:
+               assert(node != clear_flag(iter));
+               assert(!is_removed(iter_prev));
+               assert(!is_removed(iter));
+               assert(iter_prev != node);
+               if (!bucket_flag)
+                       node->next = clear_flag(iter);
+               else
+                       node->next = flag_bucket(clear_flag(iter));
+               if (is_bucket(iter))
+                       new_node = flag_bucket(node);
+               else
+                       new_node = node;
+               if (uatomic_cmpxchg(&iter_prev->next, iter,
+                                   new_node) != iter) {
+                       continue;       /* retry */
+               } else {
+                       return_node = node;
+                       goto end;
+               }
+
+       gc_node:
+               assert(!is_removed(iter));
+               if (is_bucket(iter))
+                       new_next = flag_bucket(clear_flag(next));
+               else
+                       new_next = clear_flag(next);
+               (void) uatomic_cmpxchg(&iter_prev->next, iter, new_next);
+               /* retry */
+       }
+end:
+       if (unique_ret) {
+               unique_ret->node = return_node;
+               /* unique_ret->next left unset, never used. */
+       }
+}
+
+static
+int _cds_lfht_del(struct cds_lfht *ht, unsigned long size,
+               struct cds_lfht_node *node)
+{
+       struct cds_lfht_node *bucket, *next;
+
+       if (!node)      /* Return -ENOENT if asked to delete NULL node */
+               return -ENOENT;
+
+       /* logically delete the node */
+       assert(!is_bucket(node));
+       assert(!is_removed(node));
+       assert(!is_removal_owner(node));
+
+       /*
+        * We are first checking if the node had previously been
+        * logically removed (this check is not atomic with setting the
+        * logical removal flag). Return -ENOENT if the node had
+        * previously been removed.
+        */
+       next = rcu_dereference(node->next);
+       if (caa_unlikely(is_removed(next)))
+               return -ENOENT;
+       assert(!is_bucket(next));
+       /*
+        * We set the REMOVED_FLAG unconditionally. Note that there may
+        * be more than one concurrent thread setting this flag.
+        * Knowing which wins the race will be known after the garbage
+        * collection phase, stay tuned!
+        */
+       uatomic_or(&node->next, REMOVED_FLAG);
+       /* We performed the (logical) deletion. */
+
+       /*
+        * Ensure that the node is not visible to readers anymore: lookup for
+        * the node, and remove it (along with any other logically removed node)
+        * if found.
+        */
+       bucket = lookup_bucket(ht, size, bit_reverse_ulong(node->reverse_hash));
+       _cds_lfht_gc_bucket(bucket, node);
+
+       assert(is_removed(rcu_dereference(node->next)));
+       /*
+        * Last phase: atomically exchange node->next with a version
+        * having "REMOVAL_OWNER_FLAG" set. If the returned node->next
+        * pointer did _not_ have "REMOVAL_OWNER_FLAG" set, we now own
+        * the node and win the removal race.
+        * It is interesting to note that all "add" paths are forbidden
+        * to change the next pointer starting from the point where the
+        * REMOVED_FLAG is set, so here using a read, followed by a
+        * xchg() suffice to guarantee that the xchg() will ever only
+        * set the "REMOVAL_OWNER_FLAG" (or change nothing if the flag
+        * was already set).
+        */
+       if (!is_removal_owner(uatomic_xchg(&node->next,
+                       flag_removal_owner(node->next))))
+               return 0;
+       else
+               return -ENOENT;
+}
+
+static
+void *partition_resize_thread(void *arg)
+{
+       struct partition_resize_work *work = arg;
+
+       work->ht->flavor->register_thread();
+       work->fct(work->ht, work->i, work->start, work->len);
+       work->ht->flavor->unregister_thread();
+       return NULL;
+}
+
+static
+void partition_resize_helper(struct cds_lfht *ht, unsigned long i,
+               unsigned long len,
+               void (*fct)(struct cds_lfht *ht, unsigned long i,
+                       unsigned long start, unsigned long len))
+{
+       unsigned long partition_len;
+       struct partition_resize_work *work;
+       int thread, ret;
+       unsigned long nr_threads;
+
+       /*
+        * Note: nr_cpus_mask + 1 is always power of 2.
+        * We spawn just the number of threads we need to satisfy the minimum
+        * partition size, up to the number of CPUs in the system.
+        */
+       if (nr_cpus_mask > 0) {
+               nr_threads = min(nr_cpus_mask + 1,
+                                len >> MIN_PARTITION_PER_THREAD_ORDER);
+       } else {
+               nr_threads = 1;
+       }
+       partition_len = len >> cds_lfht_get_count_order_ulong(nr_threads);
+       work = calloc(nr_threads, sizeof(*work));
+       assert(work);
+       for (thread = 0; thread < nr_threads; thread++) {
+               work[thread].ht = ht;
+               work[thread].i = i;
+               work[thread].len = partition_len;
+               work[thread].start = thread * partition_len;
+               work[thread].fct = fct;
+               ret = pthread_create(&(work[thread].thread_id), ht->resize_attr,
+                       partition_resize_thread, &work[thread]);
+               assert(!ret);
+       }
+       for (thread = 0; thread < nr_threads; thread++) {
+               ret = pthread_join(work[thread].thread_id, NULL);
+               assert(!ret);
+       }
+       free(work);
+}
+
+/*
+ * Holding RCU read lock to protect _cds_lfht_add against memory
+ * reclaim that could be performed by other call_rcu worker threads (ABA
+ * problem).
+ *
+ * When we reach a certain length, we can split this population phase over
+ * many worker threads, based on the number of CPUs available in the system.
+ * This should therefore take care of not having the expand lagging behind too
+ * many concurrent insertion threads by using the scheduler's ability to
+ * schedule bucket node population fairly with insertions.
+ */
+static
+void init_table_populate_partition(struct cds_lfht *ht, unsigned long i,
+                                  unsigned long start, unsigned long len)
+{
+       unsigned long j, size = 1UL << (i - 1);
+
+       assert(i > MIN_TABLE_ORDER);
+       ht->flavor->read_lock();
+       for (j = size + start; j < size + start + len; j++) {
+               struct cds_lfht_node *new_node = bucket_at(ht, j);
+
+               assert(j >= size && j < (size << 1));
+               dbg_printf("init populate: order %lu index %lu hash %lu\n",
+                          i, j, j);
+               new_node->reverse_hash = bit_reverse_ulong(j);
+               _cds_lfht_add(ht, j, NULL, NULL, size, new_node, NULL, 1);
+       }
+       ht->flavor->read_unlock();
+}
+
+static
+void init_table_populate(struct cds_lfht *ht, unsigned long i,
+                        unsigned long len)
+{
+       assert(nr_cpus_mask != -1);
+       if (nr_cpus_mask < 0 || len < 2 * MIN_PARTITION_PER_THREAD) {
+               ht->flavor->thread_online();
+               init_table_populate_partition(ht, i, 0, len);
+               ht->flavor->thread_offline();
+               return;
+       }
+       partition_resize_helper(ht, i, len, init_table_populate_partition);
+}
+
+static
+void init_table(struct cds_lfht *ht,
+               unsigned long first_order, unsigned long last_order)
+{
+       unsigned long i;
+
+       dbg_printf("init table: first_order %lu last_order %lu\n",
+                  first_order, last_order);
+       assert(first_order > MIN_TABLE_ORDER);
+       for (i = first_order; i <= last_order; i++) {
+               unsigned long len;
+
+               len = 1UL << (i - 1);
+               dbg_printf("init order %lu len: %lu\n", i, len);
+
+               /* Stop expand if the resize target changes under us */
+               if (CMM_LOAD_SHARED(ht->resize_target) < (1UL << i))
+                       break;
+
+               cds_lfht_alloc_bucket_table(ht, i);
+
+               /*
+                * Set all bucket nodes reverse hash values for a level and
+                * link all bucket nodes into the table.
+                */
+               init_table_populate(ht, i, len);
+
+               /*
+                * Update table size.
+                */
+               cmm_smp_wmb();  /* populate data before RCU size */
+               CMM_STORE_SHARED(ht->size, 1UL << i);
+
+               dbg_printf("init new size: %lu\n", 1UL << i);
+               if (CMM_LOAD_SHARED(ht->in_progress_destroy))
+                       break;
+       }
+}
+
+/*
+ * Holding RCU read lock to protect _cds_lfht_remove against memory
+ * reclaim that could be performed by other call_rcu worker threads (ABA
+ * problem).
+ * For a single level, we logically remove and garbage collect each node.
+ *
+ * As a design choice, we perform logical removal and garbage collection on a
+ * node-per-node basis to simplify this algorithm. We also assume keeping good
+ * cache locality of the operation would overweight possible performance gain
+ * that could be achieved by batching garbage collection for multiple levels.
+ * However, this would have to be justified by benchmarks.
+ *
+ * Concurrent removal and add operations are helping us perform garbage
+ * collection of logically removed nodes. We guarantee that all logically
+ * removed nodes have been garbage-collected (unlinked) before call_rcu is
+ * invoked to free a hole level of bucket nodes (after a grace period).
+ *
+ * Logical removal and garbage collection can therefore be done in batch or on a
+ * node-per-node basis, as long as the guarantee above holds.
+ *
+ * When we reach a certain length, we can split this removal over many worker
+ * threads, based on the number of CPUs available in the system. This should
+ * take care of not letting resize process lag behind too many concurrent
+ * updater threads actively inserting into the hash table.
+ */
+static
+void remove_table_partition(struct cds_lfht *ht, unsigned long i,
+                           unsigned long start, unsigned long len)
+{
+       unsigned long j, size = 1UL << (i - 1);
+
+       assert(i > MIN_TABLE_ORDER);
+       ht->flavor->read_lock();
+       for (j = size + start; j < size + start + len; j++) {
+               struct cds_lfht_node *fini_bucket = bucket_at(ht, j);
+               struct cds_lfht_node *parent_bucket = bucket_at(ht, j - size);
+
+               assert(j >= size && j < (size << 1));
+               dbg_printf("remove entry: order %lu index %lu hash %lu\n",
+                          i, j, j);
+               /* Set the REMOVED_FLAG to freeze the ->next for gc */
+               uatomic_or(&fini_bucket->next, REMOVED_FLAG);
+               _cds_lfht_gc_bucket(parent_bucket, fini_bucket);
+       }
+       ht->flavor->read_unlock();
+}
+
+static
+void remove_table(struct cds_lfht *ht, unsigned long i, unsigned long len)
+{
+
+       assert(nr_cpus_mask != -1);
+       if (nr_cpus_mask < 0 || len < 2 * MIN_PARTITION_PER_THREAD) {
+               ht->flavor->thread_online();
+               remove_table_partition(ht, i, 0, len);
+               ht->flavor->thread_offline();
+               return;
+       }
+       partition_resize_helper(ht, i, len, remove_table_partition);
+}
+
+/*
+ * fini_table() is never called for first_order == 0, which is why
+ * free_by_rcu_order == 0 can be used as criterion to know if free must
+ * be called.
+ */
+static
+void fini_table(struct cds_lfht *ht,
+               unsigned long first_order, unsigned long last_order)
+{
+       long i;
+       unsigned long free_by_rcu_order = 0;
+
+       dbg_printf("fini table: first_order %lu last_order %lu\n",
+                  first_order, last_order);
+       assert(first_order > MIN_TABLE_ORDER);
+       for (i = last_order; i >= first_order; i--) {
+               unsigned long len;
+
+               len = 1UL << (i - 1);
+               dbg_printf("fini order %lu len: %lu\n", i, len);
+
+               /* Stop shrink if the resize target changes under us */
+               if (CMM_LOAD_SHARED(ht->resize_target) > (1UL << (i - 1)))
+                       break;
+
+               cmm_smp_wmb();  /* populate data before RCU size */
+               CMM_STORE_SHARED(ht->size, 1UL << (i - 1));
+
+               /*
+                * We need to wait for all add operations to reach Q.S. (and
+                * thus use the new table for lookups) before we can start
+                * releasing the old bucket nodes. Otherwise their lookup will
+                * return a logically removed node as insert position.
+                */
+               ht->flavor->update_synchronize_rcu();
+               if (free_by_rcu_order)
+                       cds_lfht_free_bucket_table(ht, free_by_rcu_order);
+
+               /*
+                * Set "removed" flag in bucket nodes about to be removed.
+                * Unlink all now-logically-removed bucket node pointers.
+                * Concurrent add/remove operation are helping us doing
+                * the gc.
+                */
+               remove_table(ht, i, len);
+
+               free_by_rcu_order = i;
+
+               dbg_printf("fini new size: %lu\n", 1UL << i);
+               if (CMM_LOAD_SHARED(ht->in_progress_destroy))
+                       break;
+       }
+
+       if (free_by_rcu_order) {
+               ht->flavor->update_synchronize_rcu();
+               cds_lfht_free_bucket_table(ht, free_by_rcu_order);
+       }
+}
+
+static
+void cds_lfht_create_bucket(struct cds_lfht *ht, unsigned long size)
+{
+       struct cds_lfht_node *prev, *node;
+       unsigned long order, len, i;
+
+       cds_lfht_alloc_bucket_table(ht, 0);
+
+       dbg_printf("create bucket: order 0 index 0 hash 0\n");
+       node = bucket_at(ht, 0);
+       node->next = flag_bucket(get_end());
+       node->reverse_hash = 0;
+
+       for (order = 1; order < cds_lfht_get_count_order_ulong(size) + 1; order++) {
+               len = 1UL << (order - 1);
+               cds_lfht_alloc_bucket_table(ht, order);
+
+               for (i = 0; i < len; i++) {
+                       /*
+                        * Now, we are trying to init the node with the
+                        * hash=(len+i) (which is also a bucket with the
+                        * index=(len+i)) and insert it into the hash table,
+                        * so this node has to be inserted after the bucket
+                        * with the index=(len+i)&(len-1)=i. And because there
+                        * is no other non-bucket node nor bucket node with
+                        * larger index/hash inserted, so the bucket node
+                        * being inserted should be inserted directly linked
+                        * after the bucket node with index=i.
+                        */
+                       prev = bucket_at(ht, i);
+                       node = bucket_at(ht, len + i);
+
+                       dbg_printf("create bucket: order %lu index %lu hash %lu\n",
+                                  order, len + i, len + i);
+                       node->reverse_hash = bit_reverse_ulong(len + i);
+
+                       /* insert after prev */
+                       assert(is_bucket(prev->next));
+                       node->next = prev->next;
+                       prev->next = flag_bucket(node);
+               }
+       }
+}
+
+struct cds_lfht *_cds_lfht_new(unsigned long init_size,
+                       unsigned long min_nr_alloc_buckets,
+                       unsigned long max_nr_buckets,
+                       int flags,
+                       const struct cds_lfht_mm_type *mm,
+                       const struct rcu_flavor_struct *flavor,
+                       pthread_attr_t *attr)
+{
+       struct cds_lfht *ht;
+       unsigned long order;
+
+       /* min_nr_alloc_buckets must be power of two */
+       if (!min_nr_alloc_buckets || (min_nr_alloc_buckets & (min_nr_alloc_buckets - 1)))
+               return NULL;
+
+       /* init_size must be power of two */
+       if (!init_size || (init_size & (init_size - 1)))
+               return NULL;
+
+       /*
+        * Memory management plugin default.
+        */
+       if (!mm) {
+               if (CAA_BITS_PER_LONG > 32
+                               && max_nr_buckets
+                               && max_nr_buckets <= (1ULL << 32)) {
+                       /*
+                        * For 64-bit architectures, with max number of
+                        * buckets small enough not to use the entire
+                        * 64-bit memory mapping space (and allowing a
+                        * fair number of hash table instances), use the
+                        * mmap allocator, which is faster than the
+                        * order allocator.
+                        */
+                       mm = &cds_lfht_mm_mmap;
+               } else {
+                       /*
+                        * The fallback is to use the order allocator.
+                        */
+                       mm = &cds_lfht_mm_order;
+               }
+       }
+
+       /* max_nr_buckets == 0 for order based mm means infinite */
+       if (mm == &cds_lfht_mm_order && !max_nr_buckets)
+               max_nr_buckets = 1UL << (MAX_TABLE_ORDER - 1);
+
+       /* max_nr_buckets must be power of two */
+       if (!max_nr_buckets || (max_nr_buckets & (max_nr_buckets - 1)))
+               return NULL;
+
+       min_nr_alloc_buckets = max(min_nr_alloc_buckets, MIN_TABLE_SIZE);
+       init_size = max(init_size, MIN_TABLE_SIZE);
+       max_nr_buckets = max(max_nr_buckets, min_nr_alloc_buckets);
+       init_size = min(init_size, max_nr_buckets);
+
+       ht = mm->alloc_cds_lfht(min_nr_alloc_buckets, max_nr_buckets);
+       assert(ht);
+       assert(ht->mm == mm);
+       assert(ht->bucket_at == mm->bucket_at);
+
+       ht->flags = flags;
+       ht->flavor = flavor;
+       ht->resize_attr = attr;
+       alloc_split_items_count(ht);
+       /* this mutex should not nest in read-side C.S. */
+       pthread_mutex_init(&ht->resize_mutex, NULL);
+       order = cds_lfht_get_count_order_ulong(init_size);
+       ht->resize_target = 1UL << order;
+       cds_lfht_create_bucket(ht, 1UL << order);
+       ht->size = 1UL << order;
+       return ht;
+}
+
+void cds_lfht_lookup(struct cds_lfht *ht, unsigned long hash,
+               cds_lfht_match_fct match, const void *key,
+               struct cds_lfht_iter *iter)
+{
+       struct cds_lfht_node *node, *next, *bucket;
+       unsigned long reverse_hash, size;
+
+       reverse_hash = bit_reverse_ulong(hash);
+
+       size = rcu_dereference(ht->size);
+       bucket = lookup_bucket(ht, size, hash);
+       /* We can always skip the bucket node initially */
+       node = rcu_dereference(bucket->next);
+       node = clear_flag(node);
+       for (;;) {
+               if (caa_unlikely(is_end(node))) {
+                       node = next = NULL;
+                       break;
+               }
+               if (caa_unlikely(node->reverse_hash > reverse_hash)) {
+                       node = next = NULL;
+                       break;
+               }
+               next = rcu_dereference(node->next);
+               assert(node == clear_flag(node));
+               if (caa_likely(!is_removed(next))
+                   && !is_bucket(next)
+                   && node->reverse_hash == reverse_hash
+                   && caa_likely(match(node, key))) {
+                               break;
+               }
+               node = clear_flag(next);
+       }
+       assert(!node || !is_bucket(rcu_dereference(node->next)));
+       iter->node = node;
+       iter->next = next;
+}
+
+void cds_lfht_next_duplicate(struct cds_lfht *ht, cds_lfht_match_fct match,
+               const void *key, struct cds_lfht_iter *iter)
+{
+       struct cds_lfht_node *node, *next;
+       unsigned long reverse_hash;
+
+       node = iter->node;
+       reverse_hash = node->reverse_hash;
+       next = iter->next;
+       node = clear_flag(next);
+
+       for (;;) {
+               if (caa_unlikely(is_end(node))) {
+                       node = next = NULL;
+                       break;
+               }
+               if (caa_unlikely(node->reverse_hash > reverse_hash)) {
+                       node = next = NULL;
+                       break;
+               }
+               next = rcu_dereference(node->next);
+               if (caa_likely(!is_removed(next))
+                   && !is_bucket(next)
+                   && caa_likely(match(node, key))) {
+                               break;
+               }
+               node = clear_flag(next);
+       }
+       assert(!node || !is_bucket(rcu_dereference(node->next)));
+       iter->node = node;
+       iter->next = next;
+}
+
+void cds_lfht_next(struct cds_lfht *ht, struct cds_lfht_iter *iter)
+{
+       struct cds_lfht_node *node, *next;
+
+       node = clear_flag(iter->next);
+       for (;;) {
+               if (caa_unlikely(is_end(node))) {
+                       node = next = NULL;
+                       break;
+               }
+               next = rcu_dereference(node->next);
+               if (caa_likely(!is_removed(next))
+                   && !is_bucket(next)) {
+                               break;
+               }
+               node = clear_flag(next);
+       }
+       assert(!node || !is_bucket(rcu_dereference(node->next)));
+       iter->node = node;
+       iter->next = next;
+}
+
+void cds_lfht_first(struct cds_lfht *ht, struct cds_lfht_iter *iter)
+{
+       /*
+        * Get next after first bucket node. The first bucket node is the
+        * first node of the linked list.
+        */
+       iter->next = bucket_at(ht, 0)->next;
+       cds_lfht_next(ht, iter);
+}
+
+void cds_lfht_add(struct cds_lfht *ht, unsigned long hash,
+               struct cds_lfht_node *node)
+{
+       unsigned long size;
+
+       node->reverse_hash = bit_reverse_ulong(hash);
+       size = rcu_dereference(ht->size);
+       _cds_lfht_add(ht, hash, NULL, NULL, size, node, NULL, 0);
+       ht_count_add(ht, size, hash);
+}
+
+struct cds_lfht_node *cds_lfht_add_unique(struct cds_lfht *ht,
+                               unsigned long hash,
+                               cds_lfht_match_fct match,
+                               const void *key,
+                               struct cds_lfht_node *node)
+{
+       unsigned long size;
+       struct cds_lfht_iter iter;
+
+       node->reverse_hash = bit_reverse_ulong(hash);
+       size = rcu_dereference(ht->size);
+       _cds_lfht_add(ht, hash, match, key, size, node, &iter, 0);
+       if (iter.node == node)
+               ht_count_add(ht, size, hash);
+       return iter.node;
+}
+
+struct cds_lfht_node *cds_lfht_add_replace(struct cds_lfht *ht,
+                               unsigned long hash,
+                               cds_lfht_match_fct match,
+                               const void *key,
+                               struct cds_lfht_node *node)
+{
+       unsigned long size;
+       struct cds_lfht_iter iter;
+
+       node->reverse_hash = bit_reverse_ulong(hash);
+       size = rcu_dereference(ht->size);
+       for (;;) {
+               _cds_lfht_add(ht, hash, match, key, size, node, &iter, 0);
+               if (iter.node == node) {
+                       ht_count_add(ht, size, hash);
+                       return NULL;
+               }
+
+               if (!_cds_lfht_replace(ht, size, iter.node, iter.next, node))
+                       return iter.node;
+       }
+}
+
+int cds_lfht_replace(struct cds_lfht *ht,
+               struct cds_lfht_iter *old_iter,
+               unsigned long hash,
+               cds_lfht_match_fct match,
+               const void *key,
+               struct cds_lfht_node *new_node)
+{
+       unsigned long size;
+
+       new_node->reverse_hash = bit_reverse_ulong(hash);
+       if (!old_iter->node)
+               return -ENOENT;
+       if (caa_unlikely(old_iter->node->reverse_hash != new_node->reverse_hash))
+               return -EINVAL;
+       if (caa_unlikely(!match(old_iter->node, key)))
+               return -EINVAL;
+       size = rcu_dereference(ht->size);
+       return _cds_lfht_replace(ht, size, old_iter->node, old_iter->next,
+                       new_node);
+}
+
+int cds_lfht_del(struct cds_lfht *ht, struct cds_lfht_node *node)
+{
+       unsigned long size, hash;
+       int ret;
+
+       size = rcu_dereference(ht->size);
+       ret = _cds_lfht_del(ht, size, node);
+       if (!ret) {
+               hash = bit_reverse_ulong(node->reverse_hash);
+               ht_count_del(ht, size, hash);
+       }
+       return ret;
+}
+
+static
+int cds_lfht_delete_bucket(struct cds_lfht *ht)
+{
+       struct cds_lfht_node *node;
+       unsigned long order, i, size;
+
+       /* Check that the table is empty */
+       node = bucket_at(ht, 0);
+       do {
+               node = clear_flag(node)->next;
+               if (!is_bucket(node))
+                       return -EPERM;
+               assert(!is_removed(node));
+       } while (!is_end(node));
+       /*
+        * size accessed without rcu_dereference because hash table is
+        * being destroyed.
+        */
+       size = ht->size;
+       /* Internal sanity check: all nodes left should be bucket */
+       for (i = 0; i < size; i++) {
+               node = bucket_at(ht, i);
+               dbg_printf("delete bucket: index %lu expected hash %lu hash %lu\n",
+                       i, i, bit_reverse_ulong(node->reverse_hash));
+               assert(is_bucket(node->next));
+       }
+
+       for (order = cds_lfht_get_count_order_ulong(size); (long)order >= 0; order--)
+               cds_lfht_free_bucket_table(ht, order);
+
+       return 0;
+}
+
+/*
+ * Should only be called when no more concurrent readers nor writers can
+ * possibly access the table.
+ */
+int cds_lfht_destroy(struct cds_lfht *ht, pthread_attr_t **attr)
+{
+       int ret;
+
+       /* Wait for in-flight resize operations to complete */
+       _CMM_STORE_SHARED(ht->in_progress_destroy, 1);
+       cmm_smp_mb();   /* Store destroy before load resize */
+       while (uatomic_read(&ht->in_progress_resize))
+               poll(NULL, 0, 100);     /* wait for 100ms */
+       ret = cds_lfht_delete_bucket(ht);
+       if (ret)
+               return ret;
+       free_split_items_count(ht);
+       if (attr)
+               *attr = ht->resize_attr;
+       poison_free(ht);
+       return ret;
+}
+
+void cds_lfht_count_nodes(struct cds_lfht *ht,
+               long *approx_before,
+               unsigned long *count,
+               long *approx_after)
+{
+       struct cds_lfht_node *node, *next;
+       unsigned long nr_bucket = 0, nr_removed = 0;
+
+       *approx_before = 0;
+       if (ht->split_count) {
+               int i;
+
+               for (i = 0; i < split_count_mask + 1; i++) {
+                       *approx_before += uatomic_read(&ht->split_count[i].add);
+                       *approx_before -= uatomic_read(&ht->split_count[i].del);
+               }
+       }
+
+       *count = 0;
+
+       /* Count non-bucket nodes in the table */
+       node = bucket_at(ht, 0);
+       do {
+               next = rcu_dereference(node->next);
+               if (is_removed(next)) {
+                       if (!is_bucket(next))
+                               (nr_removed)++;
+                       else
+                               (nr_bucket)++;
+               } else if (!is_bucket(next))
+                       (*count)++;
+               else
+                       (nr_bucket)++;
+               node = clear_flag(next);
+       } while (!is_end(node));
+       dbg_printf("number of logically removed nodes: %lu\n", nr_removed);
+       dbg_printf("number of bucket nodes: %lu\n", nr_bucket);
+       *approx_after = 0;
+       if (ht->split_count) {
+               int i;
+
+               for (i = 0; i < split_count_mask + 1; i++) {
+                       *approx_after += uatomic_read(&ht->split_count[i].add);
+                       *approx_after -= uatomic_read(&ht->split_count[i].del);
+               }
+       }
+}
+
+/* called with resize mutex held */
+static
+void _do_cds_lfht_grow(struct cds_lfht *ht,
+               unsigned long old_size, unsigned long new_size)
+{
+       unsigned long old_order, new_order;
+
+       old_order = cds_lfht_get_count_order_ulong(old_size);
+       new_order = cds_lfht_get_count_order_ulong(new_size);
+       dbg_printf("resize from %lu (order %lu) to %lu (order %lu) buckets\n",
+                  old_size, old_order, new_size, new_order);
+       assert(new_size > old_size);
+       init_table(ht, old_order + 1, new_order);
+}
+
+/* called with resize mutex held */
+static
+void _do_cds_lfht_shrink(struct cds_lfht *ht,
+               unsigned long old_size, unsigned long new_size)
+{
+       unsigned long old_order, new_order;
+
+       new_size = max(new_size, MIN_TABLE_SIZE);
+       old_order = cds_lfht_get_count_order_ulong(old_size);
+       new_order = cds_lfht_get_count_order_ulong(new_size);
+       dbg_printf("resize from %lu (order %lu) to %lu (order %lu) buckets\n",
+                  old_size, old_order, new_size, new_order);
+       assert(new_size < old_size);
+
+       /* Remove and unlink all bucket nodes to remove. */
+       fini_table(ht, new_order + 1, old_order);
+}
+
+
+/* called with resize mutex held */
+static
+void _do_cds_lfht_resize(struct cds_lfht *ht)
+{
+       unsigned long new_size, old_size;
+
+       /*
+        * Resize table, re-do if the target size has changed under us.
+        */
+       do {
+               assert(uatomic_read(&ht->in_progress_resize));
+               if (CMM_LOAD_SHARED(ht->in_progress_destroy))
+                       break;
+               ht->resize_initiated = 1;
+               old_size = ht->size;
+               new_size = CMM_LOAD_SHARED(ht->resize_target);
+               if (old_size < new_size)
+                       _do_cds_lfht_grow(ht, old_size, new_size);
+               else if (old_size > new_size)
+                       _do_cds_lfht_shrink(ht, old_size, new_size);
+               ht->resize_initiated = 0;
+               /* write resize_initiated before read resize_target */
+               cmm_smp_mb();
+       } while (ht->size != CMM_LOAD_SHARED(ht->resize_target));
+}
+
+static
+unsigned long resize_target_grow(struct cds_lfht *ht, unsigned long new_size)
+{
+       return _uatomic_xchg_monotonic_increase(&ht->resize_target, new_size);
+}
+
+static
+void resize_target_update_count(struct cds_lfht *ht,
+                               unsigned long count)
+{
+       count = max(count, MIN_TABLE_SIZE);
+       count = min(count, ht->max_nr_buckets);
+       uatomic_set(&ht->resize_target, count);
+}
+
+void cds_lfht_resize(struct cds_lfht *ht, unsigned long new_size)
+{
+       resize_target_update_count(ht, new_size);
+       CMM_STORE_SHARED(ht->resize_initiated, 1);
+       ht->flavor->thread_offline();
+       pthread_mutex_lock(&ht->resize_mutex);
+       _do_cds_lfht_resize(ht);
+       pthread_mutex_unlock(&ht->resize_mutex);
+       ht->flavor->thread_online();
+}
+
+static
+void do_resize_cb(struct rcu_head *head)
+{
+       struct rcu_resize_work *work =
+               caa_container_of(head, struct rcu_resize_work, head);
+       struct cds_lfht *ht = work->ht;
+
+       ht->flavor->thread_offline();
+       pthread_mutex_lock(&ht->resize_mutex);
+       _do_cds_lfht_resize(ht);
+       pthread_mutex_unlock(&ht->resize_mutex);
+       ht->flavor->thread_online();
+       poison_free(work);
+       cmm_smp_mb();   /* finish resize before decrement */
+       uatomic_dec(&ht->in_progress_resize);
+}
+
+static
+void __cds_lfht_resize_lazy_launch(struct cds_lfht *ht)
+{
+       struct rcu_resize_work *work;
+
+       /* Store resize_target before read resize_initiated */
+       cmm_smp_mb();
+       if (!CMM_LOAD_SHARED(ht->resize_initiated)) {
+               uatomic_inc(&ht->in_progress_resize);
+               cmm_smp_mb();   /* increment resize count before load destroy */
+               if (CMM_LOAD_SHARED(ht->in_progress_destroy)) {
+                       uatomic_dec(&ht->in_progress_resize);
+                       return;
+               }
+               work = malloc(sizeof(*work));
+               work->ht = ht;
+               ht->flavor->update_call_rcu(&work->head, do_resize_cb);
+               CMM_STORE_SHARED(ht->resize_initiated, 1);
+       }
+}
+
+static
+void cds_lfht_resize_lazy_grow(struct cds_lfht *ht, unsigned long size, int growth)
+{
+       unsigned long target_size = size << growth;
+
+       target_size = min(target_size, ht->max_nr_buckets);
+       if (resize_target_grow(ht, target_size) >= target_size)
+               return;
+
+       __cds_lfht_resize_lazy_launch(ht);
+}
+
+/*
+ * We favor grow operations over shrink. A shrink operation never occurs
+ * if a grow operation is queued for lazy execution. A grow operation
+ * cancels any pending shrink lazy execution.
+ */
+static
+void cds_lfht_resize_lazy_count(struct cds_lfht *ht, unsigned long size,
+                               unsigned long count)
+{
+       if (!(ht->flags & CDS_LFHT_AUTO_RESIZE))
+               return;
+       count = max(count, MIN_TABLE_SIZE);
+       count = min(count, ht->max_nr_buckets);
+       if (count == size)
+               return;         /* Already the right size, no resize needed */
+       if (count > size) {     /* lazy grow */
+               if (resize_target_grow(ht, count) >= count)
+                       return;
+       } else {                /* lazy shrink */
+               for (;;) {
+                       unsigned long s;
+
+                       s = uatomic_cmpxchg(&ht->resize_target, size, count);
+                       if (s == size)
+                               break;  /* no resize needed */
+                       if (s > size)
+                               return; /* growing is/(was just) in progress */
+                       if (s <= count)
+                               return; /* some other thread do shrink */
+                       size = s;
+               }
+       }
+       __cds_lfht_resize_lazy_launch(ht);
+}
diff --git a/liblttng-ht/rculfhash.h b/liblttng-ht/rculfhash.h
new file mode 100644 (file)
index 0000000..136c725
--- /dev/null
@@ -0,0 +1,434 @@
+#ifndef _URCU_RCULFHASH_H
+#define _URCU_RCULFHASH_H
+
+/*
+ * urcu/rculfhash.h
+ *
+ * Userspace RCU library - Lock-Free RCU Hash Table
+ *
+ * Copyright 2011 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * Copyright 2011 - Lai Jiangshan <laijs@cn.fujitsu.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Include this file _after_ including your URCU flavor.
+ */
+
+#include <stdint.h>
+#include <urcu/compiler.h>
+#include <urcu-call-rcu.h>
+
+#include "urcu-flavor.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * cds_lfht_node: Contains the next pointers and reverse-hash
+ * value required for lookup and traversal of the hash table.
+ *
+ * struct cds_lfht_node should be aligned on 8-bytes boundaries because
+ * the three lower bits are used as flags. It is worth noting that the
+ * information contained within these three bits could be represented on
+ * two bits by re-using the same bit for REMOVAL_OWNER_FLAG and
+ * BUCKET_FLAG. This can be done if we ensure that no iterator nor
+ * updater check the BUCKET_FLAG after it detects that the REMOVED_FLAG
+ * is set. Given the minimum size of struct cds_lfht_node is 8 bytes on
+ * 32-bit architectures, we choose to go for simplicity and reserve
+ * three bits.
+ *
+ * struct cds_lfht_node can be embedded into a structure (as a field).
+ * caa_container_of() can be used to get the structure from the struct
+ * cds_lfht_node after a lookup.
+ *
+ * The structure which embeds it typically holds the key (or key-value
+ * pair) of the object. The caller code is responsible for calculation
+ * of the hash value for cds_lfht APIs.
+ */
+struct cds_lfht_node {
+       struct cds_lfht_node *next;     /* ptr | REMOVAL_OWNER_FLAG | BUCKET_FLAG | REMOVED_FLAG */
+       unsigned long reverse_hash;
+} __attribute__((aligned(8)));
+
+/* cds_lfht_iter: Used to track state while traversing a hash chain. */
+struct cds_lfht_iter {
+       struct cds_lfht_node *node, *next;
+};
+
+static inline
+struct cds_lfht_node *cds_lfht_iter_get_node(struct cds_lfht_iter *iter)
+{
+       return iter->node;
+}
+
+struct cds_lfht;
+
+/*
+ * Caution !
+ * Ensure reader and writer threads are registered as urcu readers.
+ */
+
+typedef int (*cds_lfht_match_fct)(struct cds_lfht_node *node, const void *key);
+
+/*
+ * cds_lfht_node_init - initialize a hash table node
+ * @node: the node to initialize.
+ *
+ * This function is kept to be eventually used for debugging purposes
+ * (detection of memory corruption).
+ */
+static inline
+void cds_lfht_node_init(struct cds_lfht_node *node)
+{
+}
+
+/*
+ * Hash table creation flags.
+ */
+enum {
+       CDS_LFHT_AUTO_RESIZE = (1U << 0),
+       CDS_LFHT_ACCOUNTING = (1U << 1),
+};
+
+struct cds_lfht_mm_type {
+       struct cds_lfht *(*alloc_cds_lfht)(unsigned long min_nr_alloc_buckets,
+                       unsigned long max_nr_buckets);
+       void (*alloc_bucket_table)(struct cds_lfht *ht, unsigned long order);
+       void (*free_bucket_table)(struct cds_lfht *ht, unsigned long order);
+       struct cds_lfht_node *(*bucket_at)(struct cds_lfht *ht,
+                       unsigned long index);
+};
+
+extern const struct cds_lfht_mm_type cds_lfht_mm_order;
+extern const struct cds_lfht_mm_type cds_lfht_mm_chunk;
+extern const struct cds_lfht_mm_type cds_lfht_mm_mmap;
+
+/*
+ * _cds_lfht_new - API used by cds_lfht_new wrapper. Do not use directly.
+ */
+struct cds_lfht *_cds_lfht_new(unsigned long init_size,
+                       unsigned long min_nr_alloc_buckets,
+                       unsigned long max_nr_buckets,
+                       int flags,
+                       const struct cds_lfht_mm_type *mm,
+                       const struct rcu_flavor_struct *flavor,
+                       pthread_attr_t *attr);
+
+/*
+ * cds_lfht_new - allocate a hash table.
+ * @init_size: number of buckets to allocate initially. Must be power of two.
+ * @min_nr_alloc_buckets: the minimum number of allocated buckets.
+ *                        (must be power of two)
+ * @max_nr_buckets: the maximum number of hash table buckets allowed.
+ *                  (must be power of two)
+ * @flags: hash table creation flags (can be combined with bitwise or: '|').
+ *           0: no flags.
+ *           CDS_LFHT_AUTO_RESIZE: automatically resize hash table.
+ *           CDS_LFHT_ACCOUNTING: count the number of node addition
+ *                                and removal in the table
+ * @attr: optional resize worker thread attributes. NULL for default.
+ *
+ * Return NULL on error.
+ * Note: the RCU flavor must be already included before the hash table header.
+ *
+ * The programmer is responsible for ensuring that resize operation has a
+ * priority equal to hash table updater threads. It should be performed by
+ * specifying the appropriate priority in the pthread "attr" argument, and,
+ * for CDS_LFHT_AUTO_RESIZE, by ensuring that call_rcu worker threads also have
+ * this priority level. Having lower priority for call_rcu and resize threads
+ * does not pose any correctness issue, but the resize operations could be
+ * starved by updates, thus leading to long hash table bucket chains.
+ * Threads calling this API are NOT required to be registered RCU read-side
+ * threads. It can be called very early.(before rcu is initialized ...etc.)
+ */
+static inline
+struct cds_lfht *cds_lfht_new(unsigned long init_size,
+                       unsigned long min_nr_alloc_buckets,
+                       unsigned long max_nr_buckets,
+                       int flags,
+                       pthread_attr_t *attr)
+{
+       return _cds_lfht_new(init_size, min_nr_alloc_buckets, max_nr_buckets,
+                       flags, NULL, &rcu_flavor, attr);
+}
+
+/*
+ * cds_lfht_destroy - destroy a hash table.
+ * @ht: the hash table to destroy.
+ * @attr: (output) resize worker thread attributes, as received by cds_lfht_new.
+ *        The caller will typically want to free this pointer if dynamically
+ *        allocated. The attr point can be NULL if the caller does not
+ *        need to be informed of the value passed to cds_lfht_new().
+ *
+ * Return 0 on success, negative error value on error.
+ * Threads calling this API need to be registered RCU read-side threads.
+ */
+int cds_lfht_destroy(struct cds_lfht *ht, pthread_attr_t **attr);
+
+/*
+ * cds_lfht_count_nodes - count the number of nodes in the hash table.
+ * @ht: the hash table.
+ * @split_count_before: Sample the node count split-counter before traversal.
+ * @count: Traverse the hash table, count the number of nodes observed.
+ * @split_count_after: Sample the node count split-counter after traversal.
+ *
+ * Call with rcu_read_lock held.
+ * Threads calling this API need to be registered RCU read-side threads.
+ */
+void cds_lfht_count_nodes(struct cds_lfht *ht,
+               long *split_count_before,
+               unsigned long *count,
+               long *split_count_after);
+
+/*
+ * cds_lfht_lookup - lookup a node by key.
+ * @ht: the hash table.
+ * @hash: the key hash.
+ * @match: the key match function.
+ * @key: the current node key.
+ * @iter: Node, if found (output). *iter->node set to NULL if not found.
+ *
+ * Call with rcu_read_lock held.
+ * Threads calling this API need to be registered RCU read-side threads.
+ */
+void cds_lfht_lookup(struct cds_lfht *ht, unsigned long hash,
+               cds_lfht_match_fct match, const void *key,
+               struct cds_lfht_iter *iter);
+
+/*
+ * cds_lfht_next_duplicate - get the next item with same key (after a lookup).
+ * @ht: the hash table.
+ * @match: the key match function.
+ * @key: the current node key.
+ * @iter: Node, if found (output). *iter->node set to NULL if not found.
+ *
+ * Uses an iterator initialized by a lookup.
+ * Sets *iter-node to the following node with same key.
+ * Sets *iter->node to NULL if no following node exists with same key.
+ * RCU read-side lock must be held across cds_lfht_lookup and
+ * cds_lfht_next calls, and also between cds_lfht_next calls using the
+ * node returned by a previous cds_lfht_next.
+ * Call with rcu_read_lock held.
+ * Threads calling this API need to be registered RCU read-side threads.
+ */
+void cds_lfht_next_duplicate(struct cds_lfht *ht,
+               cds_lfht_match_fct match, const void *key,
+               struct cds_lfht_iter *iter);
+
+/*
+ * cds_lfht_first - get the first node in the table.
+ * @ht: the hash table.
+ * @iter: First node, if exists (output). *iter->node set to NULL if not found.
+ *
+ * Output in "*iter". *iter->node set to NULL if table is empty.
+ * Call with rcu_read_lock held.
+ * Threads calling this API need to be registered RCU read-side threads.
+ */
+void cds_lfht_first(struct cds_lfht *ht, struct cds_lfht_iter *iter);
+
+/*
+ * cds_lfht_next - get the next node in the table.
+ * @ht: the hash table.
+ * @iter: Next node, if exists (output). *iter->node set to NULL if not found.
+ *
+ * Input/Output in "*iter". *iter->node set to NULL if *iter was
+ * pointing to the last table node.
+ * Call with rcu_read_lock held.
+ * Threads calling this API need to be registered RCU read-side threads.
+ */
+void cds_lfht_next(struct cds_lfht *ht, struct cds_lfht_iter *iter);
+
+/*
+ * cds_lfht_add - add a node to the hash table.
+ * @ht: the hash table.
+ * @hash: the key hash.
+ * @node: the node to add.
+ *
+ * This function supports adding redundant keys into the table.
+ * Call with rcu_read_lock held.
+ * Threads calling this API need to be registered RCU read-side threads.
+ */
+void cds_lfht_add(struct cds_lfht *ht, unsigned long hash,
+               struct cds_lfht_node *node);
+
+/*
+ * cds_lfht_add_unique - add a node to hash table, if key is not present.
+ * @ht: the hash table.
+ * @hash: the node's hash.
+ * @match: the key match function.
+ * @key: the node's key.
+ * @node: the node to try adding.
+ *
+ * Return the node added upon success.
+ * Return the unique node already present upon failure. If
+ * cds_lfht_add_unique fails, the node passed as parameter should be
+ * freed by the caller.
+ * Call with rcu_read_lock held.
+ * Threads calling this API need to be registered RCU read-side threads.
+ *
+ * The semantic of this function is that if only this function is used
+ * to add keys into the table, no duplicated keys should ever be
+ * observable in the table. The same guarantee apply for combination of
+ * add_unique and add_replace (see below).
+ */
+struct cds_lfht_node *cds_lfht_add_unique(struct cds_lfht *ht,
+               unsigned long hash,
+               cds_lfht_match_fct match,
+               const void *key,
+               struct cds_lfht_node *node);
+
+/*
+ * cds_lfht_add_replace - replace or add a node within hash table.
+ * @ht: the hash table.
+ * @hash: the node's hash.
+ * @match: the key match function.
+ * @key: the node's key.
+ * @node: the node to add.
+ *
+ * Return the node replaced upon success. If no node matching the key
+ * was present, return NULL, which also means the operation succeeded.
+ * This replacement operation should never fail.
+ * Call with rcu_read_lock held.
+ * Threads calling this API need to be registered RCU read-side threads.
+ * After successful replacement, a grace period must be waited for before
+ * freeing the memory reserved for the returned node.
+ *
+ * The semantic of replacement vs lookups is the following: if lookups
+ * are performed between a key unique insertion and its removal, we
+ * guarantee that the lookups and get next will always find exactly one
+ * instance of the key if it is replaced concurrently with the lookups.
+ *
+ * Providing this semantic allows us to ensure that replacement-only
+ * schemes will never generate duplicated keys. It also allows us to
+ * guarantee that a combination of add_replace and add_unique updates
+ * will never generate duplicated keys.
+ */
+struct cds_lfht_node *cds_lfht_add_replace(struct cds_lfht *ht,
+               unsigned long hash,
+               cds_lfht_match_fct match,
+               const void *key,
+               struct cds_lfht_node *node);
+
+/*
+ * cds_lfht_replace - replace a node pointer to by iter within hash table.
+ * @ht: the hash table.
+ * @old_iter: the iterator position of the node to replace.
+ * @hash: the node's hash.
+ * @match: the key match function.
+ * @key: the node's key.
+ * @new_node: the new node to use as replacement.
+ *
+ * Return 0 if replacement is successful, negative value otherwise.
+ * Replacing a NULL old node or an already removed node will fail with
+ * -ENOENT.
+ * If the hash or value of the node to replace and the new node differ,
+ * this function returns -EINVAL without proceeding to the replacement.
+ * Old node can be looked up with cds_lfht_lookup and cds_lfht_next.
+ * RCU read-side lock must be held between lookup and replacement.
+ * Call with rcu_read_lock held.
+ * Threads calling this API need to be registered RCU read-side threads.
+ * After successful replacement, a grace period must be waited for before
+ * freeing the memory reserved for the old node (which can be accessed
+ * with cds_lfht_iter_get_node).
+ *
+ * The semantic of replacement vs lookups is the following: if lookups
+ * are performed between a key unique insertion and its removal, we
+ * guarantee that the lookups and get next will always find exactly one
+ * instance of the key if it is replaced concurrently with the lookups.
+ *
+ * Providing this semantic allows us to ensure that replacement-only
+ * schemes will never generate duplicated keys. It also allows us to
+ * guarantee that a combination of add_replace and add_unique updates
+ * will never generate duplicated keys.
+ */
+int cds_lfht_replace(struct cds_lfht *ht,
+               struct cds_lfht_iter *old_iter,
+               unsigned long hash,
+               cds_lfht_match_fct match,
+               const void *key,
+               struct cds_lfht_node *new_node);
+
+/*
+ * cds_lfht_del - remove node pointed to by iterator from hash table.
+ * @ht: the hash table.
+ * @node: the node to delete.
+ *
+ * Return 0 if the node is successfully removed, negative value
+ * otherwise.
+ * Deleting a NULL node or an already removed node will fail with a
+ * negative value.
+ * Node can be looked up with cds_lfht_lookup and cds_lfht_next,
+ * followed by use of cds_lfht_iter_get_node.
+ * RCU read-side lock must be held between lookup and removal.
+ * Call with rcu_read_lock held.
+ * Threads calling this API need to be registered RCU read-side threads.
+ * After successful removal, a grace period must be waited for before
+ * freeing the memory reserved for old node (which can be accessed with
+ * cds_lfht_iter_get_node).
+ */
+int cds_lfht_del(struct cds_lfht *ht, struct cds_lfht_node *node);
+
+/*
+ * cds_lfht_resize - Force a hash table resize
+ * @ht: the hash table.
+ * @new_size: update to this hash table size.
+ *
+ * Threads calling this API need to be registered RCU read-side threads.
+ */
+void cds_lfht_resize(struct cds_lfht *ht, unsigned long new_size);
+
+/*
+ * Note: cds_lfht_for_each are safe for element removal during
+ * iteration.
+ */
+#define cds_lfht_for_each(ht, iter, node)                              \
+       for (cds_lfht_first(ht, iter),                                  \
+                       node = cds_lfht_iter_get_node(iter);            \
+               node != NULL;                                           \
+               cds_lfht_next(ht, iter),                                \
+                       node = cds_lfht_iter_get_node(iter))
+
+#define cds_lfht_for_each_duplicate(ht, hash, match, key, iter, node)  \
+       for (cds_lfht_lookup(ht, hash, match, key, iter),               \
+                       node = cds_lfht_iter_get_node(iter);            \
+               node != NULL;                                           \
+               cds_lfht_next_duplicate(ht, match, key, iter),          \
+                       node = cds_lfht_iter_get_node(iter))
+
+#define cds_lfht_for_each_entry(ht, iter, pos, member)                 \
+       for (cds_lfht_first(ht, iter),                                  \
+                       pos = caa_container_of(cds_lfht_iter_get_node(iter), \
+                                       typeof(*(pos)), member);        \
+               &(pos)->member != NULL;                                 \
+               cds_lfht_next(ht, iter),                                \
+                       pos = caa_container_of(cds_lfht_iter_get_node(iter), \
+                                       typeof(*(pos)), member))
+
+#define cds_lfht_for_each_entry_duplicate(ht, hash, match, key,                \
+                               iter, pos, member)                      \
+       for (cds_lfht_lookup(ht, hash, match, key, iter),               \
+                       pos = caa_container_of(cds_lfht_iter_get_node(iter), \
+                                       typeof(*(pos)), member);        \
+               &(pos)->member != NULL;                                 \
+               cds_lfht_next_duplicate(ht, match, key, iter),          \
+                       pos = caa_container_of(cds_lfht_iter_get_node(iter), \
+                                       typeof(*(pos)), member))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _URCU_RCULFHASH_H */
diff --git a/liblttng-ht/urcu-flavor.h b/liblttng-ht/urcu-flavor.h
new file mode 100644 (file)
index 0000000..9af4d0e
--- /dev/null
@@ -0,0 +1,65 @@
+#ifndef _URCU_FLAVOR_H
+#define _URCU_FLAVOR_H
+
+/*
+ * urcu-flavor.h
+ *
+ * Userspace RCU header - rcu flavor declarations
+ *
+ * Copyright (c) 2011 Lai Jiangshan <laijs@cn.fujitsu.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct rcu_flavor_struct {
+       void (*read_lock)(void);
+       void (*read_unlock)(void);
+       void (*read_quiescent_state)(void);
+       void (*update_call_rcu)(struct rcu_head *head,
+                               void (*func)(struct rcu_head *head));
+       void (*update_synchronize_rcu)(void);
+       void (*update_defer_rcu)(void (*fct)(void *p), void *p);
+
+       void (*thread_offline)(void);
+       void (*thread_online)(void);
+       void (*register_thread)(void);
+       void (*unregister_thread)(void);
+};
+
+#define DEFINE_RCU_FLAVOR(x)                           \
+const struct rcu_flavor_struct x = {                   \
+       .read_lock              = rcu_read_lock,        \
+       .read_unlock            = rcu_read_unlock,      \
+       .read_quiescent_state   = rcu_quiescent_state,  \
+       .update_call_rcu        = call_rcu,             \
+       .update_synchronize_rcu = synchronize_rcu,      \
+       .update_defer_rcu       = defer_rcu,            \
+       .thread_offline         = rcu_thread_offline,   \
+       .thread_online          = rcu_thread_online,    \
+       .register_thread        = rcu_register_thread,  \
+       .unregister_thread      = rcu_unregister_thread,\
+}
+
+extern const struct rcu_flavor_struct rcu_flavor;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _URCU_FLAVOR_H */
diff --git a/liblttng-ht/utils.c b/liblttng-ht/utils.c
new file mode 100644 (file)
index 0000000..0b3d531
--- /dev/null
@@ -0,0 +1,495 @@
+/*
+ * Copyright (C) - Bob Jenkins, May 2006, Public Domain.
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ * Copyright (C) 2011 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * These are functions for producing 32-bit hashes for hash table lookup.
+ * hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() are
+ * externally useful functions.  Routines to test the hash are included if
+ * SELF_TEST is defined.  You can use this free for any purpose.  It's in the
+ * public domain.  It has no warranty.
+ *
+ * You probably want to use hashlittle().  hashlittle() and hashbig() hash byte
+ * arrays.  hashlittle() is is faster than hashbig() on little-endian machines.
+ * Intel and AMD are little-endian machines.  On second thought, you probably
+ * want hashlittle2(), which is identical to hashlittle() except it returns two
+ * 32-bit hashes for the price of one.  You could implement hashbig2() if you
+ * wanted but I haven't bothered here.
+ *
+ * If you want to find a hash of, say, exactly 7 integers, do
+ *   a = i1;  b = i2;  c = i3;
+ *   mix(a,b,c);
+ *   a += i4; b += i5; c += i6;
+ *   mix(a,b,c);
+ *   a += i7;
+ *   final(a,b,c);
+ * then use c as the hash value.  If you have a variable length array of
+ * 4-byte integers to hash, use hashword().  If you have a byte array (like
+ * a character string), use hashlittle().  If you have several byte arrays, or
+ * a mix of things, see the comments above hashlittle().
+ *
+ * Why is this so big?  I read 12 bytes at a time into 3 4-byte integers, then
+ * mix those integers.  This is fast (you can do a lot more thorough mixing
+ * with 12*3 instructions on 3 integers than you can with 3 instructions on 1
+ * byte), but shoehorning those bytes into integers efficiently is messy.
+ */
+
+#include <assert.h>
+#include <endian.h>    /* attempt to define endianness */
+#include <stdint.h>     /* defines uint32_t etc */
+#include <stdio.h>      /* defines printf for tests */
+#include <string.h>
+#include <sys/param.h>  /* attempt to define endianness */
+#include <time.h>       /* defines time_t for timings in the test */
+#include <urcu/compiler.h>
+
+#include "utils.h"
+
+/*
+ * My best guess at if you are big-endian or little-endian.  This may
+ * need adjustment.
+ */
+#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \
+     __BYTE_ORDER == __LITTLE_ENDIAN) || \
+    (defined(i386) || defined(__i386__) || defined(__i486__) || \
+     defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL))
+# define HASH_LITTLE_ENDIAN 1
+# define HASH_BIG_ENDIAN 0
+#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \
+       __BYTE_ORDER == __BIG_ENDIAN) || \
+      (defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel))
+# define HASH_LITTLE_ENDIAN 0
+# define HASH_BIG_ENDIAN 1
+#else
+# define HASH_LITTLE_ENDIAN 0
+# define HASH_BIG_ENDIAN 0
+#endif
+
+#define hashsize(n) ((uint32_t)1<<(n))
+#define hashmask(n) (hashsize(n)-1)
+#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
+
+/*
+ * mix -- mix 3 32-bit values reversibly.
+ *
+ * This is reversible, so any information in (a,b,c) before mix() is
+ * still in (a,b,c) after mix().
+ *
+ * If four pairs of (a,b,c) inputs are run through mix(), or through
+ * mix() in reverse, there are at least 32 bits of the output that
+ * are sometimes the same for one pair and different for another pair.
+ * This was tested for:
+ * * pairs that differed by one bit, by two bits, in any combination
+ *   of top bits of (a,b,c), or in any combination of bottom bits of
+ *   (a,b,c).
+ * * "differ" is defined as +, -, ^, or ~^.  For + and -, I transformed
+ *   the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
+ *   is commonly produced by subtraction) look like a single 1-bit
+ *   difference.
+ * * the base values were pseudorandom, all zero but one bit set, or
+ *   all zero plus a counter that starts at zero.
+ *
+ * Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that
+ * satisfy this are
+ *     4  6  8 16 19  4
+ *     9 15  3 18 27 15
+ *    14  9  3  7 17  3
+ * Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing
+ * for "differ" defined as + with a one-bit base and a two-bit delta.  I
+ * used http://burtleburtle.net/bob/hash/avalanche.html to choose
+ * the operations, constants, and arrangements of the variables.
+ *
+ * This does not achieve avalanche.  There are input bits of (a,b,c)
+ * that fail to affect some output bits of (a,b,c), especially of a.  The
+ * most thoroughly mixed value is c, but it doesn't really even achieve
+ * avalanche in c.
+ *
+ * This allows some parallelism.  Read-after-writes are good at doubling
+ * the number of bits affected, so the goal of mixing pulls in the opposite
+ * direction as the goal of parallelism.  I did what I could.  Rotates
+ * seem to cost as much as shifts on every machine I could lay my hands
+ * on, and rotates are much kinder to the top and bottom bits, so I used
+ * rotates.
+ */
+#define mix(a,b,c) \
+{ \
+  a -= c;  a ^= rot(c, 4);  c += b; \
+  b -= a;  b ^= rot(a, 6);  a += c; \
+  c -= b;  c ^= rot(b, 8);  b += a; \
+  a -= c;  a ^= rot(c,16);  c += b; \
+  b -= a;  b ^= rot(a,19);  a += c; \
+  c -= b;  c ^= rot(b, 4);  b += a; \
+}
+
+/*
+ * final -- final mixing of 3 32-bit values (a,b,c) into c
+ *
+ * Pairs of (a,b,c) values differing in only a few bits will usually
+ * produce values of c that look totally different.  This was tested for
+ * * pairs that differed by one bit, by two bits, in any combination
+ *   of top bits of (a,b,c), or in any combination of bottom bits of
+ *   (a,b,c).
+ * * "differ" is defined as +, -, ^, or ~^.  For + and -, I transformed
+ *   the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
+ *   is commonly produced by subtraction) look like a single 1-bit
+ *   difference.
+ * * the base values were pseudorandom, all zero but one bit set, or
+ *   all zero plus a counter that starts at zero.
+ *
+ * These constants passed:
+ *  14 11 25 16 4 14 24
+ *  12 14 25 16 4 14 24
+ * and these came close:
+ *   4  8 15 26 3 22 24
+ *  10  8 15 26 3 22 24
+ *  11  8 15 26 3 22 24
+ */
+#define final(a,b,c) \
+{ \
+  c ^= b; c -= rot(b,14); \
+  a ^= c; a -= rot(c,11); \
+  b ^= a; b -= rot(a,25); \
+  c ^= b; c -= rot(b,16); \
+  a ^= c; a -= rot(c,4);  \
+  b ^= a; b -= rot(a,14); \
+  c ^= b; c -= rot(b,24); \
+}
+
+/*
+ * k - the key, an array of uint32_t values
+ * length - the length of the key, in uint32_ts
+ * initval - the previous hash, or an arbitrary value
+ */
+static uint32_t __attribute__((unused)) hashword(const uint32_t *k,
+               size_t length, uint32_t initval)
+{
+       uint32_t a, b, c;
+
+       /* Set up the internal state */
+       a = b = c = 0xdeadbeef + (((uint32_t) length) << 2) + initval;
+
+       /*----------------------------------------- handle most of the key */
+       while (length > 3) {
+               a += k[0];
+               b += k[1];
+               c += k[2];
+               mix(a, b, c);
+               length -= 3;
+               k += 3;
+       }
+
+       /*----------------------------------- handle the last 3 uint32_t's */
+       switch (length) {       /* all the case statements fall through */
+       case 3: c += k[2];
+       case 2: b += k[1];
+       case 1: a += k[0];
+               final(a, b, c);
+       case 0:                 /* case 0: nothing left to add */
+               break;
+       }
+       /*---------------------------------------------- report the result */
+       return c;
+}
+
+
+/*
+ * hashword2() -- same as hashword(), but take two seeds and return two 32-bit
+ * values.  pc and pb must both be nonnull, and *pc and *pb must both be
+ * initialized with seeds.  If you pass in (*pb)==0, the output (*pc) will be
+ * the same as the return value from hashword().
+ */
+static void __attribute__((unused)) hashword2(const uint32_t *k, size_t length,
+               uint32_t *pc, uint32_t *pb)
+{
+       uint32_t a, b, c;
+
+       /* Set up the internal state */
+       a = b = c = 0xdeadbeef + ((uint32_t) (length << 2)) + *pc;
+       c += *pb;
+
+       while (length > 3) {
+               a += k[0];
+               b += k[1];
+               c += k[2];
+               mix(a, b, c);
+               length -= 3;
+               k += 3;
+       }
+
+       switch (length) {
+       case 3 :
+               c += k[2];
+       case 2 :
+               b += k[1];
+       case 1 :
+               a += k[0];
+               final(a, b, c);
+       case 0:     /* case 0: nothing left to add */
+               break;
+       }
+
+       *pc = c;
+       *pb = b;
+}
+
+/*
+ * hashlittle() -- hash a variable-length key into a 32-bit value
+ *   k       : the key (the unaligned variable-length array of bytes)
+ *   length  : the length of the key, counting by bytes
+ *   initval : can be any 4-byte value
+ * Returns a 32-bit value.  Every bit of the key affects every bit of
+ * the return value.  Two keys differing by one or two bits will have
+ * totally different hash values.
+ *
+ * The best hash table sizes are powers of 2.  There is no need to do
+ * mod a prime (mod is sooo slow!).  If you need less than 32 bits,
+ * use a bitmask.  For example, if you need only 10 bits, do
+ *   h = (h & hashmask(10));
+ * In which case, the hash table should have hashsize(10) elements.
+ *
+ * If you are hashing n strings (uint8_t **)k, do it like this:
+ *   for (i=0, h=0; i<n; ++i) h = hashlittle( k[i], len[i], h);
+ *
+ * By Bob Jenkins, 2006.  bob_jenkins@burtleburtle.net.  You may use this
+ * code any way you wish, private, educational, or commercial.  It's free.
+ *
+ * Use for hash table lookup, or anything where one collision in 2^^32 is
+ * acceptable.  Do NOT use for cryptographic purposes.
+ */
+static uint32_t __attribute__((unused)) hashlittle(const void *key,
+               size_t length, uint32_t initval)
+{
+       uint32_t a,b,c;
+       union {
+               const void *ptr;
+               size_t i;
+       } u;     /* needed for Mac Powerbook G4 */
+
+       /* Set up the internal state */
+       a = b = c = 0xdeadbeef + ((uint32_t)length) + initval;
+
+       u.ptr = key;
+       if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
+               const uint32_t *k = (const uint32_t *)key;         /* read 32-bit chunks */
+
+               /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
+               while (length > 12) {
+                       a += k[0];
+                       b += k[1];
+                       c += k[2];
+                       mix(a,b,c);
+                       length -= 12;
+                       k += 3;
+               }
+
+               /*
+                * "k[2]&0xffffff" actually reads beyond the end of the string, but
+                * then masks off the part it's not allowed to read.  Because the
+                * string is aligned, the masked-off tail is in the same word as the
+                * rest of the string.  Every machine with memory protection I've seen
+                * does it on word boundaries, so is OK with this.  But VALGRIND will
+                * still catch it and complain.  The masking trick does make the hash
+                * noticably faster for short strings (like English words).
+                */
+#ifndef VALGRIND
+
+               switch (length) {
+               case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+               case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break;
+               case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break;
+               case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break;
+               case 8 : b+=k[1]; a+=k[0]; break;
+               case 7 : b+=k[1]&0xffffff; a+=k[0]; break;
+               case 6 : b+=k[1]&0xffff; a+=k[0]; break;
+               case 5 : b+=k[1]&0xff; a+=k[0]; break;
+               case 4 : a+=k[0]; break;
+               case 3 : a+=k[0]&0xffffff; break;
+               case 2 : a+=k[0]&0xffff; break;
+               case 1 : a+=k[0]&0xff; break;
+               case 0 : return c;              /* zero length strings require no mixing */
+               }
+#else /* make valgrind happy */
+               const uint8_t *k8;
+
+               k8 = (const uint8_t *)k;
+               switch (length) {
+               case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+               case 11: c+=((uint32_t)k8[10])<<16;  /* fall through */
+               case 10: c+=((uint32_t)k8[9])<<8;    /* fall through */
+               case 9 : c+=k8[8];                   /* fall through */
+               case 8 : b+=k[1]; a+=k[0]; break;
+               case 7 : b+=((uint32_t)k8[6])<<16;   /* fall through */
+               case 6 : b+=((uint32_t)k8[5])<<8;    /* fall through */
+               case 5 : b+=k8[4];                   /* fall through */
+               case 4 : a+=k[0]; break;
+               case 3 : a+=((uint32_t)k8[2])<<16;   /* fall through */
+               case 2 : a+=((uint32_t)k8[1])<<8;    /* fall through */
+               case 1 : a+=k8[0]; break;
+               case 0 : return c;
+               }
+#endif /* !valgrind */
+       } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
+               const uint16_t *k = (const uint16_t *)key;         /* read 16-bit chunks */
+               const uint8_t *k8;
+
+               /*--------------- all but last block: aligned reads and different mixing */
+               while (length > 12) {
+                       a += k[0] + (((uint32_t)k[1])<<16);
+                       b += k[2] + (((uint32_t)k[3])<<16);
+                       c += k[4] + (((uint32_t)k[5])<<16);
+                       mix(a,b,c);
+                       length -= 12;
+                       k += 6;
+               }
+
+               k8 = (const uint8_t *)k;
+               switch (length) {
+               case 12:
+                       c+=k[4]+(((uint32_t)k[5])<<16);
+                       b+=k[2]+(((uint32_t)k[3])<<16);
+                       a+=k[0]+(((uint32_t)k[1])<<16);
+                       break;
+               case 11:
+                       c+=((uint32_t)k8[10])<<16;     /* fall through */
+               case 10:
+                       c+=k[4];
+                       b+=k[2]+(((uint32_t)k[3])<<16);
+                       a+=k[0]+(((uint32_t)k[1])<<16);
+                       break;
+               case 9:
+                       c+=k8[8];                      /* fall through */
+               case 8:
+                       b+=k[2]+(((uint32_t)k[3])<<16);
+                       a+=k[0]+(((uint32_t)k[1])<<16);
+                       break;
+               case 7:
+                       b+=((uint32_t)k8[6])<<16;      /* fall through */
+               case 6:
+                       b+=k[2];
+                       a+=k[0]+(((uint32_t)k[1])<<16);
+                       break;
+               case 5:
+                       b+=k8[4];                      /* fall through */
+               case 4:
+                       a+=k[0]+(((uint32_t)k[1])<<16);
+                       break;
+               case 3:
+                       a+=((uint32_t)k8[2])<<16;      /* fall through */
+               case 2:
+                       a+=k[0];
+                       break;
+               case 1:
+                       a+=k8[0];
+                       break;
+               case 0:
+                       return c;   /* zero length requires no mixing */
+               }
+
+       } else {    /* need to read the key one byte at a time */
+               const uint8_t *k = (const uint8_t *)key;
+
+               while (length > 12) {
+                       a += k[0];
+                       a += ((uint32_t)k[1])<<8;
+                       a += ((uint32_t)k[2])<<16;
+                       a += ((uint32_t)k[3])<<24;
+                       b += k[4];
+                       b += ((uint32_t)k[5])<<8;
+                       b += ((uint32_t)k[6])<<16;
+                       b += ((uint32_t)k[7])<<24;
+                       c += k[8];
+                       c += ((uint32_t)k[9])<<8;
+                       c += ((uint32_t)k[10])<<16;
+                       c += ((uint32_t)k[11])<<24;
+                       mix(a,b,c);
+                       length -= 12;
+                       k += 12;
+               }
+
+               switch(length) {                  /* all the case statements fall through */
+               case 12: c+=((uint32_t)k[11])<<24;
+               case 11: c+=((uint32_t)k[10])<<16;
+               case 10: c+=((uint32_t)k[9])<<8;
+               case 9: c+=k[8];
+               case 8: b+=((uint32_t)k[7])<<24;
+               case 7: b+=((uint32_t)k[6])<<16;
+               case 6: b+=((uint32_t)k[5])<<8;
+               case 5: b+=k[4];
+               case 4: a+=((uint32_t)k[3])<<24;
+               case 3: a+=((uint32_t)k[2])<<16;
+               case 2: a+=((uint32_t)k[1])<<8;
+               case 1:
+                       a+=k[0];
+                       break;
+               case 0:
+                       return c;
+               }
+       }
+
+       final(a,b,c);
+       return c;
+}
+
+#if (CAA_BITS_PER_LONG == 64)
+/*
+ * Hash function for number value.
+ */
+unsigned long hash_key_ulong(void *_key, unsigned long seed)
+{
+       union {
+               uint64_t v64;
+               uint32_t v32[2];
+       } v;
+       union {
+               uint64_t v64;
+               uint32_t v32[2];
+       } key;
+
+       v.v64 = (uint64_t) seed;
+       key.v64 = (uint64_t) _key;
+       hashword2(key.v32, 2, &v.v32[0], &v.v32[1]);
+       return v.v64;
+}
+#else
+/*
+ * Hash function for number value.
+ */
+unsigned long hash_key_ulong(void *_key, unsigned long seed)
+{
+       uint32_t key = (uint32_t) _key;
+
+       return hashword(&key, 1, seed);
+}
+#endif /* CAA_BITS_PER_LONG */
+
+/*
+ * Hash function for string.
+ */
+unsigned long hash_key_str(void *key, unsigned long seed)
+{
+       return hashlittle(key, strlen((char *) key), seed);
+}
+
+/*
+ * Hash function compare for number value.
+ */
+int hash_match_key_ulong(void *key1, void *key2)
+{
+       if (key1 == key2) {
+               return 1;
+       }
+
+       return 0;
+}
+
+/*
+ * Hash compare function for string.
+ */
+int hash_match_key_str(void *key1, void *key2)
+{
+       if (strcmp(key1, key2) == 0) {
+               return 1;
+       }
+
+       return 0;
+}
diff --git a/liblttng-ht/utils.h b/liblttng-ht/utils.h
new file mode 100644 (file)
index 0000000..86b340f
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; only version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifndef _LTT_HT_UTILS_H
+#define _LTT_HT_UTILS_H
+
+#include <stdint.h>
+
+unsigned long hash_key_ulong(void *_key, unsigned long seed);
+unsigned long hash_key_str(void *key, unsigned long seed);
+int hash_match_key_ulong(void *key1, void *key2);
+int hash_match_key_str(void *key1, void *key2);
+
+#endif /* _LTT_HT_UTILS_H */
index e9861f20fb6b2e11103dacfc8bf36c7e0b77c527..388cc5b20f8f4e67853a01b4ee7038f8a398cf1f 100644 (file)
@@ -33,6 +33,7 @@
 #include <lttng-sessiond-comm.h>
 #include <lttng/lttng-kconsumer.h>
 #include <lttngerr.h>
+#include <runas.h>
 
 extern struct lttng_consumer_global_data consumer_data;
 extern int consumer_poll_timeout;
@@ -256,7 +257,9 @@ int lttng_kconsumer_recv_cmd(struct lttng_consumer_local_data *ctx,
                                msg.u.stream.state,
                                msg.u.stream.mmap_len,
                                msg.u.stream.output,
-                               msg.u.stream.path_name);
+                               msg.u.stream.path_name,
+                               msg.u.stream.uid,
+                               msg.u.stream.gid);
                if (new_stream == NULL) {
                        lttng_consumer_send_error(ctx, CONSUMERD_OUTFD_ERROR);
                        goto end;
@@ -393,8 +396,10 @@ int lttng_kconsumer_on_recv_stream(struct lttng_consumer_stream *stream)
 
        /* Opening the tracefile in write mode */
        if (stream->path_name != NULL) {
-               ret = open(stream->path_name,
-                               O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU|S_IRWXG|S_IRWXO);
+               ret = open_run_as(stream->path_name,
+                               O_WRONLY|O_CREAT|O_TRUNC,
+                               S_IRWXU|S_IRWXG|S_IRWXO,
+                               stream->uid, stream->gid);
                if (ret < 0) {
                        ERR("Opening %s", stream->path_name);
                        perror("open");
index 3672a83ab98f4e732d8ddcaccbb091f610e49fb1..483b346d6692072513f3d59a0f79332b00d1a587 100644 (file)
@@ -22,7 +22,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/un.h>
@@ -54,6 +53,7 @@ static const char *lttcomm_readable_code[] = {
        [ LTTCOMM_ERR_INDEX(LTTCOMM_EXIST_SESS) ] = "Session name already exist",
        [ LTTCOMM_ERR_INDEX(LTTCOMM_CONNECT_FAIL) ] = "Unable to connect to Unix socket",
        [ LTTCOMM_ERR_INDEX(LTTCOMM_APP_NOT_FOUND) ] = "Application not found",
+       [ LTTCOMM_ERR_INDEX(LTTCOMM_EPERM) ] = "Permission denied",
        [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_NA) ] = "Kernel tracer not available",
        [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_EVENT_EXIST) ] = "Kernel event already exists",
        [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_SESS_FAIL) ] = "Kernel create session failed",
@@ -181,13 +181,9 @@ int lttcomm_accept_unix_sock(int sock)
        new_fd = accept(sock, (struct sockaddr *) &sun, &len);
        if (new_fd < 0) {
                perror("accept");
-               goto error;
        }
 
        return new_fd;
-
-error:
-       return -1;
 }
 
 /*
@@ -409,3 +405,130 @@ ssize_t lttcomm_recv_fds_unix_sock(int sock, int *fds, size_t nb_fd)
 end:
        return ret;
 }
+
+/*
+ * Send a message with credentials over a unix socket.
+ *
+ * Returns the size of data sent, or negative error value.
+ */
+ssize_t lttcomm_send_creds_unix_sock(int sock, void *buf, size_t len)
+{
+       struct msghdr msg = { 0 };
+       struct cmsghdr *cmptr;
+       struct iovec iov[1];
+       ssize_t ret = -1;
+       struct ucred *creds;
+       size_t sizeof_cred = sizeof(struct ucred);
+       char anc_buf[CMSG_SPACE(sizeof_cred)];
+
+       iov[0].iov_base = buf;
+       iov[0].iov_len = len;
+       msg.msg_iov = iov;
+       msg.msg_iovlen = 1;
+
+       msg.msg_control = (caddr_t) anc_buf;
+       msg.msg_controllen = CMSG_LEN(sizeof_cred);
+
+       cmptr = CMSG_FIRSTHDR(&msg);
+       cmptr->cmsg_level = SOL_SOCKET;
+       cmptr->cmsg_type = SCM_CREDENTIALS;
+       cmptr->cmsg_len = CMSG_LEN(sizeof_cred);
+
+       creds = (struct ucred *) CMSG_DATA(cmptr);
+
+       creds->uid = geteuid();
+       creds->gid = getegid();
+       creds->pid = getpid();
+
+       ret = sendmsg(sock, &msg, 0);
+       if (ret < 0) {
+               perror("sendmsg");
+       }
+
+       return ret;
+}
+
+/*
+ * Recv a message accompanied with credentials from a unix socket.
+ *
+ * Returns the size of received data, or negative error value.
+ */
+ssize_t lttcomm_recv_creds_unix_sock(int sock, void *buf, size_t len,
+               struct ucred *creds)
+{
+       struct msghdr msg = { 0 };
+       struct cmsghdr *cmptr;
+       struct iovec iov[1];
+       ssize_t ret;
+       size_t sizeof_cred = sizeof(struct ucred);
+       char anc_buf[CMSG_SPACE(sizeof_cred)];
+
+       /* Not allowed */
+       if (creds == NULL) {
+               ret = -1;
+               goto end;
+       }
+
+       /* Prepare to receive the structures */
+       iov[0].iov_base = buf;
+       iov[0].iov_len = len;
+       msg.msg_iov = iov;
+       msg.msg_iovlen = 1;
+
+       msg.msg_control = anc_buf;
+       msg.msg_controllen = sizeof(anc_buf);
+
+       ret = recvmsg(sock, &msg, 0);
+       if (ret < 0) {
+               perror("recvmsg fds");
+               goto end;
+       }
+
+       if (msg.msg_flags & MSG_CTRUNC) {
+               fprintf(stderr, "Error: Control message truncated.\n");
+               ret = -1;
+               goto end;
+       }
+
+       cmptr = CMSG_FIRSTHDR(&msg);
+       if (cmptr == NULL) {
+               fprintf(stderr, "Error: Invalid control message header\n");
+               ret = -1;
+               goto end;
+       }
+
+       if (cmptr->cmsg_level != SOL_SOCKET ||
+                       cmptr->cmsg_type != SCM_CREDENTIALS) {
+               fprintf(stderr, "Didn't received any credentials\n");
+               ret = -1;
+               goto end;
+       }
+
+       if (cmptr->cmsg_len != CMSG_LEN(sizeof_cred)) {
+               fprintf(stderr, "Error: Received %zu bytes of ancillary data, expected %zu\n",
+                               cmptr->cmsg_len, CMSG_LEN(sizeof_cred));
+               ret = -1;
+               goto end;
+       }
+
+       memcpy(creds, CMSG_DATA(cmptr), sizeof_cred);
+
+end:
+       return ret;
+}
+
+/*
+ * Set socket option to use credentials passing.
+ */
+int lttcomm_setsockopt_creds_unix_sock(int sock)
+{
+       int ret, on = 1;
+
+       /* Set socket for credentials retrieval */
+       ret = setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
+       if (ret < 0) {
+               perror("setsockopt creds unix sock");
+       }
+
+       return ret;
+}
index 284eccf97c4b4626df5488fe70044ade92898642..e01064fd74f7e7c7ecaa682a3c9764e2eb2fc0f7 100644 (file)
@@ -6,4 +6,5 @@ noinst_LTLIBRARIES = liblttng-ustconsumer.la
 liblttng_ustconsumer_la_SOURCES = lttng-ustconsumer.c
 
 liblttng_ustconsumer_la_LIBADD = -llttng-ust-ctl
+
 endif
index 89dbefa361380ad2c317bf161a6d65cc7baae0e5..26e680a9cd757d6bc5974dda20d6a30df3ccb9b2 100644 (file)
@@ -33,6 +33,7 @@
 #include <lttng/lttng-ustconsumer.h>
 #include <lttng/ust-ctl.h>
 #include <lttngerr.h>
+#include <runas.h>
 
 extern struct lttng_consumer_global_data consumer_data;
 extern int consumer_poll_timeout;
@@ -215,7 +216,9 @@ int lttng_ustconsumer_recv_cmd(struct lttng_consumer_local_data *ctx,
                                msg.u.stream.state,
                                msg.u.stream.mmap_len,
                                msg.u.stream.output,
-                               msg.u.stream.path_name);
+                               msg.u.stream.path_name,
+                               msg.u.stream.uid,
+                               msg.u.stream.gid);
                if (new_stream == NULL) {
                        lttng_consumer_send_error(ctx, CONSUMERD_OUTFD_ERROR);
                        goto end;
@@ -395,8 +398,10 @@ int lttng_ustconsumer_on_recv_stream(struct lttng_consumer_stream *stream)
 
        /* Opening the tracefile in write mode */
        if (stream->path_name != NULL) {
-               ret = open(stream->path_name,
-                               O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU|S_IRWXG|S_IRWXO);
+               ret = open_run_as(stream->path_name,
+                               O_WRONLY|O_CREAT|O_TRUNC,
+                               S_IRWXU|S_IRWXG|S_IRWXO,
+                               stream->uid, stream->gid);
                if (ret < 0) {
                        ERR("Opening %s", stream->path_name);
                        perror("open");
index c5e8cf8f8cffae86ecce900354f13a0752b4e382..c65760155347d4ca7e576ea7b24132b580d47e7f 100644 (file)
@@ -93,7 +93,7 @@ static int send_session_msg(struct lttcomm_session_msg *lsm)
                goto end;
        }
 
-       ret = lttcomm_send_unix_sock(sessiond_socket, lsm,
+       ret = lttcomm_send_creds_unix_sock(sessiond_socket, lsm,
                        sizeof(struct lttcomm_session_msg));
 
 end:
index 70ebc50356e67a072bbeafcdc0e2dfd73989f273..9e8d036da6c9d74191f1c400ea3ad741c19716f9 100644 (file)
@@ -7,7 +7,8 @@ lttng_consumerd_SOURCES = lttng-consumerd.c
 lttng_consumerd_LDADD = \
           $(top_builddir)/libkernelctl/libkernelctl.la \
           $(top_builddir)/liblttng-consumer/liblttng-consumer.la \
-          $(top_builddir)/liblttng-sessiond-comm/liblttng-sessiond-comm.la
+          $(top_builddir)/liblttng-sessiond-comm/liblttng-sessiond-comm.la \
+          $(top_builddir)/common/libcommon.la
 
 if HAVE_LIBLTTNG_UST_CTL
 lttng_consumerd_LDADD += -llttng-ust-ctl
index db621d8e38999fcee97c5e48ae5a9ad85a6506ce..829f9abb03bf58012ec4b5bf87933effdc8b8c48 100644 (file)
@@ -13,7 +13,6 @@ COMPAT=compat/compat-poll.c
 endif
 
 lttng_sessiond_SOURCES = utils.c utils.h \
-                       hashtable.c hashtable.h \
                        compat/poll.h $(COMPAT) \
                        trace-kernel.c trace-kernel.h \
                        kernel.c kernel.h \
@@ -24,9 +23,7 @@ lttng_sessiond_SOURCES = utils.c utils.h \
                        futex.c futex.h \
                        shm.c shm.h \
                        session.c session.h \
-                       ../hashtable/rculfhash.c \
-                       ../hashtable/rculfhash.h \
-                       ../hashtable/hash.c ../hashtable/hash.h
+                       lttng-ust-ctl.h lttng-ust-abi.h
 
 if HAVE_LIBLTTNG_UST_CTL
 lttng_sessiond_SOURCES += trace-ust.c ust-app.c ust-consumer.c ust-consumer.h
@@ -40,7 +37,9 @@ lttng_sessiond_LDADD = -lrt -lurcu-common -lurcu \
                 $(top_builddir)/liblttng-sessiond-comm/liblttng-sessiond-comm.la \
                 $(top_builddir)/libkernelctl/libkernelctl.la \
                 $(top_builddir)/liblttngctl/liblttngctl.la \
-                $(top_builddir)/benchmark/liblttng-benchmark.la
+                $(top_builddir)/benchmark/liblttng-benchmark.la \
+                $(top_builddir)/common/libcommon.la \
+                $(top_builddir)/liblttng-ht/liblttng-ht.la
 
 if HAVE_LIBLTTNG_UST_CTL
 lttng_sessiond_LDADD += -llttng-ust-ctl
index 06b799db1d2132aab97e10ca2ad7e8970be1c10c..7ff974da05930c772b5734e1e7de3d4098766df8 100644 (file)
 #include <unistd.h>
 
 #include <lttng/lttng.h>
+#include <lttng-ht.h>
 #include <lttng-sessiond-comm.h>
 #include <lttngerr.h>
 
 #include "channel.h"
-#include "hashtable.h"
 #include "kernel.h"
 #include "ust-ctl.h"
 #include "utils.h"
@@ -216,7 +216,7 @@ int channel_ust_create(struct ltt_ust_session *usess, int domain,
                struct lttng_channel *attr)
 {
        int ret = LTTCOMM_OK;
-       struct cds_lfht *chan_ht;
+       struct lttng_ht *chan_ht;
        struct ltt_ust_channel *uchan = NULL;
        struct lttng_channel *defattr = NULL;
 
@@ -259,7 +259,7 @@ int channel_ust_create(struct ltt_ust_session *usess, int domain,
        }
 
        uchan->enabled = 1;
-       hashtable_add_unique(chan_ht, &uchan->node);
+       lttng_ht_add_unique_str(chan_ht, &uchan->node);
        DBG2("Channel %s created successfully", uchan->name);
 
        free(defattr);
index 081e05430ba1e9b44c95b536fc9dd7152db991d0..e29cc09a5379bd0ea59dacc783790483b5984d12 100644 (file)
 #include <unistd.h>
 #include <urcu/list.h>
 
+#include <lttng-ht.h>
 #include <lttng-sessiond-comm.h>
 #include <lttngerr.h>
 
 #include "context.h"
-#include "hashtable.h"
 #include "kernel.h"
 #include "ust-app.h"
 #include "trace-ust.h"
@@ -180,7 +180,7 @@ static int add_uctx_to_channel(struct ltt_ust_session *usess, int domain,
        }
 
        /* Add ltt UST context node to ltt UST channel */
-       hashtable_add_unique(uchan->ctx, &uctx->node);
+       lttng_ht_add_unique_ulong(uchan->ctx, &uctx->node);
 
        return LTTCOMM_OK;
 
@@ -219,7 +219,7 @@ static int add_uctx_to_event(struct ltt_ust_session *usess, int domain,
        }
 
        /* Add ltt UST context node to ltt UST event */
-       hashtable_add_unique(uevent->ctx, &uctx->node);
+       lttng_ht_add_unique_ulong(uevent->ctx, &uctx->node);
 
        return LTTCOMM_OK;
 
@@ -280,8 +280,8 @@ int context_ust_add(struct ltt_ust_session *usess, int domain,
                char *channel_name)
 {
        int ret = LTTCOMM_OK, have_event = 0;
-       struct cds_lfht_iter iter, uiter;
-       struct cds_lfht *chan_ht;
+       struct lttng_ht_iter iter, uiter;
+       struct lttng_ht *chan_ht;
        struct ltt_ust_channel *uchan = NULL;
        struct ltt_ust_event *uevent = NULL;
 
@@ -332,7 +332,7 @@ int context_ust_add(struct ltt_ust_session *usess, int domain,
                ret = add_uctx_to_channel(usess, domain, uchan, ctx);
        } else if (!uchan && have_event) {      /* Add ctx to event */
                /* Add context to event without having the channel name */
-               cds_lfht_for_each_entry(chan_ht, &iter, uchan, node) {
+               cds_lfht_for_each_entry(chan_ht->ht, &iter.iter, uchan, node.node) {
                        uevent = trace_ust_find_event_by_name(uchan->events, event_name);
                        if (uevent != NULL) {
                                ret = add_uctx_to_event(usess, domain, uchan, uevent, ctx);
@@ -348,7 +348,7 @@ int context_ust_add(struct ltt_ust_session *usess, int domain,
                goto error;
        } else if (!uchan && !have_event) {     /* Add ctx all events, all channels */
                /* For all channels */
-               cds_lfht_for_each_entry(chan_ht, &iter, uchan, node) {
+               cds_lfht_for_each_entry(chan_ht->ht, &iter.iter, uchan, node.node) {
                        ret = add_uctx_to_channel(usess, domain, uchan, ctx);
                        if (ret < 0) {
                                ERR("Context added to channel %s failed", uchan->name);
@@ -356,7 +356,8 @@ int context_ust_add(struct ltt_ust_session *usess, int domain,
                        }
 
                        /* For all events in channel */
-                       cds_lfht_for_each_entry(uchan->events, &uiter, uevent, node) {
+                       cds_lfht_for_each_entry(uchan->events->ht, &uiter.iter, uevent,
+                                       node.node) {
                                ret = add_uctx_to_event(usess, domain, uchan, uevent, ctx);
                                if (ret < 0) {
                                        ERR("Context add to event %s in channel %s failed",
index 6e247aa9139db8cf5fcabff7b55810886c47f719..8582d2f8fc4907e59745feb07c93473ee08c7733 100644 (file)
  * Place - Suite 330, Boston, MA  02111-1307, USA.
  */
 
+#define _GNU_SOURCE
 #include <errno.h>
 #include <urcu/list.h>
 #include <string.h>
 
 #include <lttng/lttng.h>
+#include <lttng-ht.h>
 #include <lttng-sessiond-comm.h>
 #include <lttngerr.h>
 
 #include "channel.h"
 #include "event.h"
-#include "hashtable.h"
 #include "kernel.h"
 #include "ust-ctl.h"
 #include "ust-app.h"
@@ -259,7 +260,7 @@ int event_ust_enable_all_tracepoints(struct ltt_ust_session *usess, int domain,
 {
        int ret, i;
        size_t size;
-       struct cds_lfht_iter iter;
+       struct lttng_ht_iter iter;
        struct ltt_ust_event *uevent = NULL;
        struct lttng_event *events = NULL;
 
@@ -267,7 +268,8 @@ int event_ust_enable_all_tracepoints(struct ltt_ust_session *usess, int domain,
        case LTTNG_DOMAIN_UST:
        {
                /* Enable existing events */
-               cds_lfht_for_each_entry(uchan->events, &iter, uevent, node) {
+               cds_lfht_for_each_entry(uchan->events->ht, &iter.iter, uevent,
+                               node.node) {
                        if (uevent->enabled == 0) {
                                ret = ust_app_enable_event_glb(usess, uchan, uevent);
                                if (ret < 0) {
@@ -326,7 +328,7 @@ int event_ust_enable_all_tracepoints(struct ltt_ust_session *usess, int domain,
                        uevent->enabled = 1;
                        /* Add ltt ust event to channel */
                        rcu_read_lock();
-                       hashtable_add_unique(uchan->events, &uevent->node);
+                       lttng_ht_add_unique_str(uchan->events, &uevent->node);
                        rcu_read_unlock();
                }
 
@@ -409,7 +411,7 @@ int event_ust_enable_tracepoint(struct ltt_ust_session *usess, int domain,
        /* Add ltt ust event to channel */
        if (to_create) {
                rcu_read_lock();
-               hashtable_add_unique(uchan->events, &uevent->node);
+               lttng_ht_add_unique_str(uchan->events, &uevent->node);
                rcu_read_unlock();
        }
 
@@ -480,7 +482,7 @@ int event_ust_disable_all_tracepoints(struct ltt_ust_session *usess, int domain,
 {
        int ret, i;
        size_t size;
-       struct cds_lfht_iter iter;
+       struct lttng_ht_iter iter;
        struct ltt_ust_event *uevent = NULL;
        struct lttng_event *events = NULL;
 
@@ -488,7 +490,8 @@ int event_ust_disable_all_tracepoints(struct ltt_ust_session *usess, int domain,
        case LTTNG_DOMAIN_UST:
        {
                /* Disabling existing events */
-               cds_lfht_for_each_entry(uchan->events, &iter, uevent, node) {
+               cds_lfht_for_each_entry(uchan->events->ht, &iter.iter, uevent,
+                               node.node) {
                        if (uevent->enabled == 1) {
                                ret = ust_app_disable_event_glb(usess, uchan, uevent);
                                if (ret < 0) {
diff --git a/lttng-sessiond/hashtable.c b/lttng-sessiond/hashtable.c
deleted file mode 100644 (file)
index 956cfd4..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; only version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
- * Place - Suite 330, Boston, MA  02111-1307, USA.
- */
-
-#include <urcu.h>
-
-#include <lttng-share.h>
-
-#include "hashtable.h"
-#include "../hashtable/rculfhash.h"
-#include "../hashtable/hash.h"
-
-struct cds_lfht *hashtable_new(unsigned long size)
-{
-       if (size == 0) {
-               size = DEFAULT_HT_SIZE;
-       }
-
-       return cds_lfht_new(hash_key, hash_compare_key, 0x42UL,
-                       size, size, CDS_LFHT_AUTO_RESIZE, NULL);
-}
-
-struct cds_lfht *hashtable_new_str(unsigned long size)
-{
-       if (size == 0) {
-               size = DEFAULT_HT_SIZE;
-       }
-
-       return cds_lfht_new(hash_key_str, hash_compare_key_str, 0x42UL,
-                       size, size, CDS_LFHT_AUTO_RESIZE, NULL);
-}
-
-struct cds_lfht_node *hashtable_iter_get_node(struct cds_lfht_iter *iter)
-{
-       /* Safety net */
-       if (iter == NULL) {
-               return NULL;
-       }
-
-       return cds_lfht_iter_get_node(iter);
-}
-
-struct cds_lfht_node *hashtable_lookup(struct cds_lfht *ht, void *key,
-               size_t key_len, struct cds_lfht_iter *iter)
-{
-       /* Safety net */
-       if (ht == NULL || iter == NULL || key == NULL) {
-               return NULL;
-       }
-
-       cds_lfht_lookup(ht, key, key_len, iter);
-
-       return hashtable_iter_get_node(iter);
-}
-
-void hashtable_get_first(struct cds_lfht *ht, struct cds_lfht_iter *iter)
-{
-       cds_lfht_first(ht, iter);
-}
-
-void hashtable_get_next(struct cds_lfht *ht, struct cds_lfht_iter *iter)
-{
-       cds_lfht_next(ht, iter);
-}
-
-void hashtable_add_unique(struct cds_lfht *ht, struct cds_lfht_node *node)
-{
-       cds_lfht_add_unique(ht, node);
-}
-
-void hashtable_node_init(struct cds_lfht_node *node, void *key,
-               size_t key_len)
-{
-       cds_lfht_node_init(node, key, key_len);
-}
-
-int hashtable_del(struct cds_lfht *ht, struct cds_lfht_iter *iter)
-{
-       /* Safety net */
-       if (ht == NULL || iter == NULL) {
-               return -1;
-       }
-
-       return cds_lfht_del(ht, iter);
-}
-
-unsigned long hashtable_get_count(struct cds_lfht *ht)
-{
-       long ab, aa;
-       unsigned long count, removed;
-
-       cds_lfht_count_nodes(ht, &ab, &count, &removed, &aa);
-
-       return count;
-}
-
-int hashtable_destroy(struct cds_lfht *ht)
-{
-       return cds_lfht_destroy(ht, NULL);
-}
diff --git a/lttng-sessiond/hashtable.h b/lttng-sessiond/hashtable.h
deleted file mode 100644 (file)
index 7212fb0..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; only version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
- * Place - Suite 330, Boston, MA  02111-1307, USA.
- */
-
-#ifndef _LTT_HASHTABLE_H 
-#define _LTT_HASHTABLE_H
-
-#include <urcu.h>
-#include "../hashtable/rculfhash.h"
-
-struct cds_lfht *hashtable_new(unsigned long size);
-struct cds_lfht *hashtable_new_str(unsigned long size);
-
-struct cds_lfht_node *hashtable_iter_get_node(struct cds_lfht_iter *iter);
-struct cds_lfht_node *hashtable_lookup(struct cds_lfht *ht, void *key,
-               size_t key_len, struct cds_lfht_iter *iter);
-
-void hashtable_get_first(struct cds_lfht *ht, struct cds_lfht_iter *iter);
-void hashtable_get_next(struct cds_lfht *ht, struct cds_lfht_iter *iter);
-void hashtable_add_unique(struct cds_lfht *ht, struct cds_lfht_node *node);
-void hashtable_node_init(struct cds_lfht_node *node,
-               void *key, size_t key_len);
-
-int hashtable_del(struct cds_lfht *ht, struct cds_lfht_iter *iter);
-unsigned long hashtable_get_count(struct cds_lfht *ht);
-int hashtable_destroy(struct cds_lfht *ht);
-
-#endif /* _LTT_HASHTABLE_H */
index 63de2e12076ac3509f049062b2d7bfd60786d9b1..7de024f805136061e8f9c503fa699e7fec1c68aa 100644 (file)
@@ -71,6 +71,7 @@ extern const char default_home_dir[],
 struct command_ctx {
        int ust_sock;
        unsigned int lttng_msg_size;
+       struct ucred creds;
        struct ltt_session *session;
        struct lttcomm_lttng_msg *llm;
        struct lttcomm_session_msg *lsm;
index 66c84c9ddb195e1426070973659df236b33c4896..b8cb965b028f9fabbbcf594d8676863722fb0492 100644 (file)
@@ -133,5 +133,7 @@ void ustctl_flush_buffer(struct lttng_ust_shm_handle *handle,
 
 /* Release object created by members of this API */
 int ustctl_release_object(int sock, struct lttng_ust_object_data *data);
+/* Release handle returned by create session. */
+int ustctl_release_handle(int sock, int handle);
 
 #endif /* _LTTNG_UST_CTL_H */
index d8f2125a4834066761d41cd6a0133ecfaddeb87c..6d6af18e23c35acd599226efb279ab9a71895d5d 100644 (file)
 #include <config.h>
 
 #include <lttng-consumerd.h>
+#include <lttng-ht.h>
 #include <lttng-sessiond-comm.h>
 #include <lttng/lttng-consumer.h>
 
 #include <lttngerr.h>
+#include <runas.h>
 
 #include "channel.h"
 #include "compat/poll.h"
 #include "context.h"
 #include "event.h"
 #include "futex.h"
-#include "hashtable.h"
 #include "kernel.h"
 #include "lttng-sessiond.h"
 #include "shm.h"
@@ -94,6 +95,7 @@ const char *progname;
 const char *opt_tracing_group;
 static int opt_sig_parent;
 static int opt_daemon;
+static int opt_no_kernel;
 static int is_root;                    /* Set to 1 if the daemon is running as root */
 static pid_t ppid;          /* Parent PID for --sig-parent option */
 static char *rundir;
@@ -459,10 +461,9 @@ static void cleanup(void)
 
        pthread_mutex_destroy(&kconsumer_data.pid_mutex);
 
-       DBG("Closing kernel fd");
-       close(kernel_tracer_fd);
-
-       if (is_root) {
+       if (is_root && !opt_no_kernel) {
+               DBG2("Closing kernel fd");
+               close(kernel_tracer_fd);
                DBG("Unloading kernel modules");
                modprobe_remove_kernel_modules();
        }
@@ -490,7 +491,7 @@ static void cleanup(void)
        /* END BENCHMARK */
 
        /* <fun> */
-       MSG("%c[%d;%dm*** assert failed :-) *** ==> %c[%dm%c[%d;%dm"
+       DBG("%c[%d;%dm*** assert failed :-) *** ==> %c[%dm%c[%d;%dm"
                        "Matthew, BEET driven development works!%c[%dm",
                        27, 1, 31, 27, 0, 27, 1, 33, 27, 0);
        /* </fun> */
@@ -533,7 +534,8 @@ static void clean_command_ctx(struct command_ctx **cmd_ctx)
  * Send all stream fds of kernel channel to the consumer.
  */
 static int send_kconsumer_channel_streams(struct consumer_data *consumer_data,
-               int sock, struct ltt_kernel_channel *channel)
+               int sock, struct ltt_kernel_channel *channel,
+               uid_t uid, gid_t gid)
 {
        int ret;
        struct ltt_kernel_stream *stream;
@@ -565,6 +567,8 @@ static int send_kconsumer_channel_streams(struct consumer_data *consumer_data,
                lkm.u.stream.state = stream->state;
                lkm.u.stream.output = channel->channel->attr.output;
                lkm.u.stream.mmap_len = 0;      /* for kernel */
+               lkm.u.stream.uid = uid;
+               lkm.u.stream.gid = gid;
                strncpy(lkm.u.stream.path_name, stream->pathname, PATH_MAX - 1);
                lkm.u.stream.path_name[PATH_MAX - 1] = '\0';
                DBG("Sending stream %d to consumer", lkm.u.stream.stream_key);
@@ -626,6 +630,8 @@ static int send_kconsumer_session_streams(struct consumer_data *consumer_data,
                lkm.u.stream.state = LTTNG_CONSUMER_ACTIVE_STREAM;
                lkm.u.stream.output = DEFAULT_KERNEL_CHANNEL_OUTPUT;
                lkm.u.stream.mmap_len = 0;      /* for kernel */
+               lkm.u.stream.uid = session->uid;
+               lkm.u.stream.gid = session->gid;
                strncpy(lkm.u.stream.path_name, session->metadata->pathname, PATH_MAX - 1);
                lkm.u.stream.path_name[PATH_MAX - 1] = '\0';
                DBG("Sending metadata stream %d to consumer", lkm.u.stream.stream_key);
@@ -642,7 +648,8 @@ static int send_kconsumer_session_streams(struct consumer_data *consumer_data,
        }
 
        cds_list_for_each_entry(chan, &session->channel_list.head, list) {
-               ret = send_kconsumer_channel_streams(consumer_data, sock, chan);
+               ret = send_kconsumer_channel_streams(consumer_data, sock, chan,
+                               session->uid, session->gid);
                if (ret < 0) {
                        goto error;
                }
@@ -802,7 +809,8 @@ static int update_kernel_stream(struct consumer_data *consumer_data, int fd)
                                 */
                                if (session->kernel_session->consumer_fds_sent == 1) {
                                        ret = send_kconsumer_channel_streams(consumer_data,
-                                                       session->kernel_session->consumer_fd, channel);
+                                                       session->kernel_session->consumer_fd, channel,
+                                                       session->uid, session->gid);
                                        if (ret < 0) {
                                                goto error;
                                        }
@@ -1191,7 +1199,7 @@ static void *thread_manage_apps(void *data)
                                         */
                                        update_ust_app(ust_cmd.sock);
 
-                                       ret = ustctl_register_done(ust_cmd.sock);
+                                       ret = ust_app_register_done(ust_cmd.sock);
                                        if (ret < 0) {
                                                /*
                                                 * If the registration is not possible, we simply
@@ -1643,7 +1651,7 @@ static pid_t spawn_consumerd(struct consumer_data *consumer_data)
                                }
                        }
                        DBG("Using 64-bit UST consumer at: %s",  consumerd64_bin);
-                       ret = execl(consumerd64_bin, verbosity, "-u",
+                       ret = execl(consumerd64_bin, "lttng-consumerd", verbosity, "-u",
                                        "--consumerd-cmd-sock", consumer_data->cmd_unix_sock_path,
                                        "--consumerd-err-sock", consumer_data->err_unix_sock_path,
                                        NULL);
@@ -1687,7 +1695,7 @@ static pid_t spawn_consumerd(struct consumer_data *consumer_data)
                                }
                        }
                        DBG("Using 32-bit UST consumer at: %s",  consumerd32_bin);
-                       ret = execl(consumerd32_bin, verbosity, "-u",
+                       ret = execl(consumerd32_bin, "lttng-consumerd", verbosity, "-u",
                                        "--consumerd-cmd-sock", consumer_data->cmd_unix_sock_path,
                                        "--consumerd-err-sock", consumer_data->err_unix_sock_path,
                                        NULL);
@@ -1800,7 +1808,7 @@ static int mount_debugfs(char *path)
        int ret;
        char *type = "debugfs";
 
-       ret = mkdir_recursive(path, S_IRWXU | S_IRWXG, geteuid(), getegid());
+       ret = mkdir_recursive_run_as(path, S_IRWXU | S_IRWXG, geteuid(), getegid());
        if (ret < 0) {
                PERROR("Cannot create debugfs path");
                goto error;
@@ -1936,9 +1944,8 @@ error:
 static int create_ust_session(struct ltt_session *session,
                struct lttng_domain *domain)
 {
-       int ret;
-       unsigned int uid;
        struct ltt_ust_session *lus = NULL;
+       int ret;
 
        switch (domain->type) {
        case LTTNG_DOMAIN_UST:
@@ -1950,15 +1957,14 @@ static int create_ust_session(struct ltt_session *session,
 
        DBG("Creating UST session");
 
-       uid = session->uid;
-       lus = trace_ust_create_session(session->path, uid, domain);
+       lus = trace_ust_create_session(session->path, session->id, domain);
        if (lus == NULL) {
                ret = LTTCOMM_UST_SESS_FAIL;
                goto error;
        }
 
-       ret = mkdir_recursive(lus->pathname, S_IRWXU | S_IRWXG,
-                       geteuid(), allowed_group());
+       ret = mkdir_recursive_run_as(lus->pathname, S_IRWXU | S_IRWXG,
+                       session->uid, session->gid);
        if (ret < 0) {
                if (ret != -EEXIST) {
                        ERR("Trace directory creation error");
@@ -1976,6 +1982,8 @@ static int create_ust_session(struct ltt_session *session,
                ERR("Unknown UST domain on create session %d", domain->type);
                goto error;
        }
+       lus->uid = session->uid;
+       lus->gid = session->gid;
        session->ust_session = lus;
 
        return LTTCOMM_OK;
@@ -2005,19 +2013,55 @@ static int create_kernel_session(struct ltt_session *session)
                session->kernel_session->consumer_fd = kconsumer_data.cmd_sock;
        }
 
-       ret = mkdir_recursive(session->kernel_session->trace_path,
-                       S_IRWXU | S_IRWXG, geteuid(), allowed_group());
+       ret = mkdir_recursive_run_as(session->kernel_session->trace_path,
+                       S_IRWXU | S_IRWXG, session->uid, session->gid);
        if (ret < 0) {
                if (ret != -EEXIST) {
                        ERR("Trace directory creation error");
                        goto error;
                }
        }
+       session->kernel_session->uid = session->uid;
+       session->kernel_session->gid = session->gid;
 
 error:
        return ret;
 }
 
+/*
+ * Check if the UID or GID match the session. Root user has access to
+ * all sessions.
+ */
+static int session_access_ok(struct ltt_session *session,
+        uid_t uid, gid_t gid)
+{
+       if (uid != session->uid && gid != session->gid
+                       && uid != 0) {
+               return 0;
+       } else {
+               return 1;
+       }
+}
+
+static unsigned int lttng_sessions_count(uid_t uid, gid_t gid)
+{
+       unsigned int i = 0;
+       struct ltt_session *session;
+
+       DBG("Counting number of available session for UID %d GID %d",
+               uid, gid);
+       cds_list_for_each_entry(session, &session_list_ptr->head, list) {
+               /*
+                * Only list the sessions the user can control.
+                */
+               if (!session_access_ok(session, uid, gid)) {
+                       continue;
+               }
+               i++;
+       }
+       return i;
+}
+
 /*
  * Using the session list, filled a lttng_session array to send back to the
  * client for session listing.
@@ -2025,17 +2069,25 @@ error:
  * The session list lock MUST be acquired before calling this function. Use
  * session_lock_list() and session_unlock_list().
  */
-static void list_lttng_sessions(struct lttng_session *sessions)
+static void list_lttng_sessions(struct lttng_session *sessions,
+               uid_t uid, gid_t gid)
 {
-       int i = 0;
+       unsigned int i = 0;
        struct ltt_session *session;
 
-       DBG("Getting all available session");
+       DBG("Getting all available session for UID %d GID %d",
+               uid, gid);
        /*
         * Iterate over session list and append data after the control struct in
         * the buffer.
         */
        cds_list_for_each_entry(session, &session_list_ptr->head, list) {
+               /*
+                * Only list the sessions the user can control.
+                */
+               if (!session_access_ok(session, uid, gid)) {
+                       continue;
+               }
                strncpy(sessions[i].path, session->path, PATH_MAX);
                sessions[i].path[PATH_MAX - 1] = '\0';
                strncpy(sessions[i].name, session->name, NAME_MAX);
@@ -2071,11 +2123,11 @@ static void list_lttng_channels(int domain, struct ltt_session *session,
                break;
        case LTTNG_DOMAIN_UST:
        {
-               struct cds_lfht_iter iter;
+               struct lttng_ht_iter iter;
                struct ltt_ust_channel *uchan;
 
-               cds_lfht_for_each_entry(session->ust_session->domain_global.channels,
-                               &iter, uchan, node) {
+               cds_lfht_for_each_entry(session->ust_session->domain_global.channels->ht,
+                               &iter.iter, uchan, node.node) {
                        strncpy(channels[i].name, uchan->name, LTTNG_SYMBOL_NAME_LEN);
                        channels[i].attr.overwrite = uchan->attr.overwrite;
                        channels[i].attr.subbuf_size = uchan->attr.subbuf_size;
@@ -2108,8 +2160,8 @@ static int list_lttng_ust_global_events(char *channel_name,
 {
        int i = 0, ret = 0;
        unsigned int nb_event = 0;
-       struct cds_lfht_iter iter;
-       struct cds_lfht_node *node;
+       struct lttng_ht_iter iter;
+       struct lttng_ht_node_str *node;
        struct ltt_ust_channel *uchan;
        struct ltt_ust_event *uevent;
        struct lttng_event *tmp;
@@ -2118,16 +2170,16 @@ static int list_lttng_ust_global_events(char *channel_name,
 
        rcu_read_lock();
 
-       node = hashtable_lookup(ust_global->channels, (void *) channel_name,
-                       strlen(channel_name), &iter);
+       lttng_ht_lookup(ust_global->channels, (void *)channel_name, &iter);
+       node = lttng_ht_iter_get_node_str(&iter);
        if (node == NULL) {
                ret = -LTTCOMM_UST_CHAN_NOT_FOUND;
                goto error;
        }
 
-       uchan = caa_container_of(node, struct ltt_ust_channel, node);
+       uchan = caa_container_of(&node->node, struct ltt_ust_channel, node.node);
 
-       nb_event += hashtable_get_count(uchan->events);
+       nb_event += lttng_ht_get_count(uchan->events);
 
        if (nb_event == 0) {
                ret = nb_event;
@@ -2142,7 +2194,7 @@ static int list_lttng_ust_global_events(char *channel_name,
                goto error;
        }
 
-       cds_lfht_for_each_entry(uchan->events, &iter, uevent, node) {
+       cds_lfht_for_each_entry(uchan->events->ht, &iter.iter, uevent, node.node) {
                strncpy(tmp[i].name, uevent->attr.name, LTTNG_SYMBOL_NAME_LEN);
                tmp[i].name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0';
                tmp[i].enabled = uevent->enabled;
@@ -2268,7 +2320,7 @@ static int cmd_disable_channel(struct ltt_session *session,
        case LTTNG_DOMAIN_UST:
        {
                struct ltt_ust_channel *uchan;
-               struct cds_lfht *chan_ht;
+               struct lttng_ht *chan_ht;
 
                chan_ht = usess->domain_global.channels;
 
@@ -2308,7 +2360,7 @@ static int cmd_enable_channel(struct ltt_session *session,
 {
        int ret;
        struct ltt_ust_session *usess = session->ust_session;
-       struct cds_lfht *chan_ht;
+       struct lttng_ht *chan_ht;
 
        DBG("Enabling channel %s for session %s", attr->name, session->name);
 
@@ -2631,7 +2683,6 @@ static int cmd_enable_event(struct ltt_session *session, int domain,
                }
 
                /* At this point, the session and channel exist on the tracer */
-
                ret = event_ust_enable_tracepoint(usess, domain, uchan, event);
                if (ret != LTTCOMM_OK) {
                        goto error;
@@ -2835,8 +2886,11 @@ static int cmd_start_trace(struct ltt_session *session)
        ksession = session->kernel_session;
        usess = session->ust_session;
 
-       if (session->enabled)
-               return LTTCOMM_UST_START_FAIL;
+       if (session->enabled) {
+               ret = LTTCOMM_UST_START_FAIL;
+               goto error;
+       }
+
        session->enabled = 1;
 
        /* Kernel tracing */
@@ -2924,8 +2978,11 @@ static int cmd_stop_trace(struct ltt_session *session)
        ksession = session->kernel_session;
        usess = session->ust_session;
 
-       if (!session->enabled)
-               return LTTCOMM_UST_START_FAIL;
+       if (!session->enabled) {
+               ret = LTTCOMM_UST_START_FAIL;
+               goto error;
+       }
+
        session->enabled = 0;
 
        /* Kernel tracer */
@@ -2973,11 +3030,11 @@ error:
 /*
  * Command LTTNG_CREATE_SESSION processed by the client thread.
  */
-static int cmd_create_session(char *name, char *path)
+static int cmd_create_session(char *name, char *path, struct ucred *creds)
 {
        int ret;
 
-       ret = session_create(name, path);
+       ret = session_create(name, path, creds->uid, creds->gid);
        if (ret != LTTCOMM_OK) {
                goto error;
        }
@@ -3057,7 +3114,7 @@ static int cmd_register_consumer(struct ltt_session *session, int domain,
        switch (domain) {
        case LTTNG_DOMAIN_KERNEL:
                /* Can't register a consumer if there is already one */
-               if (session->kernel_session->consumer_fd != 0) {
+               if (session->kernel_session->consumer_fds_sent != 0) {
                        ret = LTTCOMM_KERN_CONSUMER_FAIL;
                        goto error;
                }
@@ -3141,7 +3198,7 @@ static ssize_t cmd_list_channels(int domain, struct ltt_session *session,
                break;
        case LTTNG_DOMAIN_UST:
                if (session->ust_session != NULL) {
-                       nb_chan = hashtable_get_count(
+                       nb_chan = lttng_ht_get_count(
                                        session->ust_session->domain_global.channels);
                }
                DBG3("Number of UST global channels %zd", nb_chan);
@@ -3219,6 +3276,11 @@ static int process_client_msg(struct command_ctx *cmd_ctx)
 
        DBG("Processing client command %d", cmd_ctx->lsm->cmd_type);
 
+       if (opt_no_kernel && cmd_ctx->lsm->domain.type == LTTNG_DOMAIN_KERNEL) {
+               ret = LTTCOMM_KERN_NA;
+               goto error;
+       }
+
        /*
         * Check for command that don't needs to allocate a returned payload. We do
         * this here so we don't have to make the call for no payload at each
@@ -3358,6 +3420,18 @@ static int process_client_msg(struct command_ctx *cmd_ctx)
                break;
        }
 
+       /*
+        * Check that the UID or GID match that of the tracing session.
+        * The root user can interact with all sessions.
+        */
+       if (need_tracing_session) {
+               if (!session_access_ok(cmd_ctx->session,
+                               cmd_ctx->creds.uid, cmd_ctx->creds.gid)) {
+                       ret = LTTCOMM_EPERM;
+                       goto error;
+               }
+       }
+
        /* Process by command type */
        switch (cmd_ctx->lsm->cmd_type) {
        case LTTNG_ADD_CONTEXT:
@@ -3456,7 +3530,7 @@ static int process_client_msg(struct command_ctx *cmd_ctx)
        {
                tracepoint(create_session_start);
                ret = cmd_create_session(cmd_ctx->lsm->session.name,
-                               cmd_ctx->lsm->session.path);
+                               cmd_ctx->lsm->session.path, &cmd_ctx->creds);
                tracepoint(create_session_end);
                break;
        }
@@ -3547,23 +3621,24 @@ static int process_client_msg(struct command_ctx *cmd_ctx)
        }
        case LTTNG_LIST_SESSIONS:
        {
-               session_lock_list();
+               unsigned int nr_sessions;
 
-               if (session_list_ptr->count == 0) {
+               session_lock_list();
+               nr_sessions = lttng_sessions_count(cmd_ctx->creds.uid, cmd_ctx->creds.gid);
+               if (nr_sessions == 0) {
                        ret = LTTCOMM_NO_SESSION;
                        session_unlock_list();
                        goto error;
                }
-
-               ret = setup_lttng_msg(cmd_ctx, sizeof(struct lttng_session) *
-                               session_list_ptr->count);
+               ret = setup_lttng_msg(cmd_ctx, sizeof(struct lttng_session) * nr_sessions);
                if (ret < 0) {
                        session_unlock_list();
                        goto setup_error;
                }
 
                /* Filled the session array */
-               list_lttng_sessions((struct lttng_session *)(cmd_ctx->llm->payload));
+               list_lttng_sessions((struct lttng_session *)(cmd_ctx->llm->payload),
+                       cmd_ctx->creds.uid, cmd_ctx->creds.gid);
 
                session_unlock_list();
 
@@ -3688,6 +3763,12 @@ static void *thread_manage_clients(void *data)
                        goto error;
                }
 
+               /* Set socket option for credentials retrieval */
+               ret = lttcomm_setsockopt_creds_unix_sock(sock);
+               if (ret < 0) {
+                       goto error;
+               }
+
                /* Allocate context command to process the client request */
                cmd_ctx = zmalloc(sizeof(struct command_ctx));
                if (cmd_ctx == NULL) {
@@ -3711,8 +3792,8 @@ static void *thread_manage_clients(void *data)
                 * the client.
                 */
                DBG("Receiving data from client ...");
-               ret = lttcomm_recv_unix_sock(sock, cmd_ctx->lsm,
-                               sizeof(struct lttcomm_session_msg));
+               ret = lttcomm_recv_creds_unix_sock(sock, cmd_ctx->lsm,
+                               sizeof(struct lttcomm_session_msg), &cmd_ctx->creds);
                if (ret <= 0) {
                        DBG("Nothing recv() from client... continuing");
                        close(sock);
@@ -3750,10 +3831,10 @@ static void *thread_manage_clients(void *data)
                        ERR("Failed to send data back to client");
                }
 
-               clean_command_ctx(&cmd_ctx);
-
                /* End of transmission */
                close(sock);
+
+               clean_command_ctx(&cmd_ctx);
        }
 
 error:
@@ -3796,6 +3877,7 @@ static void usage(void)
        fprintf(stderr, "  -q, --quiet                        No output at all.\n");
        fprintf(stderr, "  -v, --verbose                      Verbose mode. Activate DBG() macro.\n");
        fprintf(stderr, "      --verbose-consumer             Verbose mode for consumer. Activate DBG() macro.\n");
+       fprintf(stderr, "      --no-kernel                    Disable kernel tracer\n");
 }
 
 /*
@@ -3826,12 +3908,13 @@ static int parse_args(int argc, char **argv)
                { "quiet", 0, 0, 'q' },
                { "verbose", 0, 0, 'v' },
                { "verbose-consumer", 0, 0, 'Z' },
+               { "no-kernel", 0, 0, 'N' },
                { NULL, 0, 0, 0 }
        };
 
        while (1) {
                int option_index = 0;
-               c = getopt_long(argc, argv, "dhqvVS" "a:c:g:s:C:E:D:F:Z:u:t",
+               c = getopt_long(argc, argv, "dhqvVSN" "a:c:g:s:C:E:D:F:Z:u:t",
                                long_options, &option_index);
                if (c == -1) {
                        break;
@@ -3883,6 +3966,9 @@ static int parse_args(int argc, char **argv)
                case 'G':
                        snprintf(ustconsumer32_data.cmd_unix_sock_path, PATH_MAX, "%s", optarg);
                        break;
+               case 'N':
+                       opt_no_kernel = 1;
+                       break;
                case 'q':
                        opt_quiet = 1;
                        break;
@@ -3990,27 +4076,22 @@ static int check_existing_daemon(void)
  * Race window between mkdir and chown is OK because we are going from more
  * permissive (root.root) to les permissive (root.tracing).
  */
-static int set_permissions(void)
+static int set_permissions(char *rundir)
 {
        int ret;
        gid_t gid;
 
        gid = allowed_group();
        if (gid < 0) {
-               if (is_root) {
-                       WARN("No tracing group detected");
-                       ret = 0;
-               } else {
-                       ERR("Missing tracing group. Aborting execution.");
-                       ret = -1;
-               }
+               WARN("No tracing group detected");
+               ret = 0;
                goto end;
        }
 
        /* Set lttng run dir */
-       ret = chown(LTTNG_RUNDIR, 0, gid);
+       ret = chown(rundir, 0, gid);
        if (ret < 0) {
-               ERR("Unable to set group on " LTTNG_RUNDIR);
+               ERR("Unable to set group on %s", rundir);
                perror("chown");
        }
 
@@ -4395,7 +4476,9 @@ int main(int argc, char **argv)
                }
 
                /* Setup kernel tracer */
-               init_kernel_tracer();
+               if (!opt_no_kernel) {
+                       init_kernel_tracer();
+               }
 
                /* Set ulimit for open files */
                set_ulimit();
@@ -4421,7 +4504,7 @@ int main(int argc, char **argv)
        }
 
        /* Set credentials to socket */
-       if (is_root && ((ret = set_permissions()) < 0)) {
+       if (is_root && ((ret = set_permissions(rundir)) < 0)) {
                goto exit;
        }
 
index bf16f76cba40a9cfeb7a71ef7e6c8cbaa30ec439..cccb43b74b7dcb23d47275d5d672a03a2ead10e4 100644 (file)
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
 #include <urcu.h>
 
 #include <lttng-sessiond-comm.h>
 #include <lttngerr.h>
+#include <runas.h>
 
-#include "hashtable.h"
 #include "session.h"
 
-#include "../hashtable/hash.h"
-
 /*
  * NOTES:
  *
@@ -164,7 +164,7 @@ int session_destroy(struct ltt_session *session)
 /*
  * Create a brand new session and add it to the session list.
  */
-int session_create(char *name, char *path)
+int session_create(char *name, char *path, uid_t uid, gid_t gid)
 {
        int ret;
        struct ltt_session *new_session;
@@ -214,12 +214,27 @@ int session_create(char *name, char *path)
        /* Init lock */
        pthread_mutex_init(&new_session->lock, NULL);
 
+       new_session->uid = uid;
+       new_session->gid = gid;
+
+       ret = mkdir_recursive_run_as(new_session->path, S_IRWXU | S_IRWXG,
+                       new_session->uid, new_session->gid);
+       if (ret < 0) {
+               if (ret != -EEXIST) {
+                       ERR("Trace directory creation error");
+                       ret = LTTCOMM_CREATE_FAIL;
+                       goto error;
+               }
+       }
+
        /* Add new session to the session list */
        session_lock_list();
-       new_session->uid = add_session_list(new_session);
+       new_session->id = add_session_list(new_session);
        session_unlock_list();
 
-       DBG("Tracing session %s created in %s with UID %d", name, path, new_session->uid);
+       DBG("Tracing session %s created in %s with ID %d by UID %d GID %d",
+               name, path, new_session->id,
+               new_session->uid, new_session->gid);
 
        return LTTCOMM_OK;
 
index 9d8bd32aa97f5e5877a91b2b6e5229447391ff2f..6264e14ac4f0b6430fd06f9960e0c9915e539fa2 100644 (file)
@@ -20,6 +20,7 @@
 #define _LTT_SESSION_H
 
 #include <pthread.h>
+#include <unistd.h>
 #include <urcu/list.h>
 
 #include "trace-kernel.h"
@@ -68,11 +69,14 @@ struct ltt_session {
        pthread_mutex_t lock;
        struct cds_list_head list;
        int enabled;    /* enabled/started flag */
-       int uid;
+       int id;         /* session unique identifier */
+       /* UID/GID of the user owning the session */
+       uid_t uid;
+       gid_t gid;
 };
 
 /* Prototypes */
-int session_create(char *name, char *path);
+int session_create(char *name, char *path, uid_t uid, gid_t gid);
 int session_destroy(struct ltt_session *session);
 
 void session_lock(struct ltt_session *session);
index bcea65148be4d5994f0d5eec805e40449555cddb..1057c1e07a04eb2255b7f1624b3570ed11d61357 100644 (file)
@@ -97,6 +97,9 @@ struct ltt_kernel_session {
        char *trace_path;
        struct ltt_kernel_metadata *metadata;
        struct ltt_kernel_channel_list channel_list;
+       /* UID/GID of the user owning the session */
+       uid_t uid;
+       gid_t gid;
 };
 
 /*
index 8fd144b6ba7fc77aa5052a18cdb7aa1e727f6397..0bfcc3bd811ddb3cc4babf55ada1d8fe2686db30 100644 (file)
 #include <unistd.h>
 
 #include <lttngerr.h>
+#include <lttng-ht.h>
 #include <lttng-share.h>
 
-#include "hashtable.h"
 #include "trace-ust.h"
 
 /*
  * Find the channel in the hashtable.
  */
-struct ltt_ust_channel *trace_ust_find_channel_by_name(struct cds_lfht *ht,
+struct ltt_ust_channel *trace_ust_find_channel_by_name(struct lttng_ht *ht,
                char *name)
 {
-       struct cds_lfht_node *node;
-       struct cds_lfht_iter iter;
+       struct lttng_ht_node_str *node;
+       struct lttng_ht_iter iter;
 
        rcu_read_lock();
-       node = hashtable_lookup(ht, (void *) name, strlen(name), &iter);
+       lttng_ht_lookup(ht, (void *)name, &iter);
+       node = lttng_ht_iter_get_node_str(&iter);
        if (node == NULL) {
                rcu_read_unlock();
                goto error;
@@ -56,14 +57,15 @@ error:
 /*
  * Find the event in the hashtable.
  */
-struct ltt_ust_event *trace_ust_find_event_by_name(struct cds_lfht *ht,
+struct ltt_ust_event *trace_ust_find_event_by_name(struct lttng_ht *ht,
                char *name)
 {
-       struct cds_lfht_node *node;
-       struct cds_lfht_iter iter;
+       struct lttng_ht_node_str *node;
+       struct lttng_ht_iter iter;
 
        rcu_read_lock();
-       node = hashtable_lookup(ht, (void *)name, strlen(name), &iter);
+       lttng_ht_lookup(ht, (void *) name, &iter);
+       node = lttng_ht_iter_get_node_str(&iter);
        if (node == NULL) {
                rcu_read_unlock();
                goto error;
@@ -84,7 +86,7 @@ error:
  *
  * Return pointer to structure or NULL.
  */
-struct ltt_ust_session *trace_ust_create_session(char *path, unsigned int uid,
+struct ltt_ust_session *trace_ust_create_session(char *path, int session_id,
                struct lttng_domain *domain)
 {
        int ret;
@@ -98,15 +100,15 @@ struct ltt_ust_session *trace_ust_create_session(char *path, unsigned int uid,
        }
 
        /* Init data structure */
-       lus->uid = uid;
+       lus->id = session_id;
        lus->start_trace = 0;
 
        /* Alloc UST domain hash tables */
-       lus->domain_pid = hashtable_new(0);
-       lus->domain_exec = hashtable_new_str(0);
+       lus->domain_pid = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
+       lus->domain_exec = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
 
        /* Alloc UST global domain channels' HT */
-       lus->domain_global.channels = hashtable_new_str(0);
+       lus->domain_global.channels = lttng_ht_new(0, LTTNG_HT_TYPE_STRING);
 
        /* Set session path */
        ret = snprintf(lus->pathname, PATH_MAX, "%s/ust", path);
@@ -162,10 +164,10 @@ struct ltt_ust_channel *trace_ust_create_channel(struct lttng_channel *chan,
        luc->name[LTTNG_UST_SYM_NAME_LEN - 1] = '\0';
 
        /* Init node */
-       hashtable_node_init(&luc->node, (void *) luc->name, strlen(luc->name));
+       lttng_ht_node_init_str(&luc->node, luc->name);
        /* Alloc hash tables */
-       luc->events = hashtable_new_str(0);
-       luc->ctx = hashtable_new(0);
+       luc->events = lttng_ht_new(0, LTTNG_HT_TYPE_STRING);
+       luc->ctx = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
 
        /* Set trace output path */
        ret = snprintf(luc->pathname, PATH_MAX, "%s", path);
@@ -225,10 +227,9 @@ struct ltt_ust_event *trace_ust_create_event(struct lttng_event *ev)
        lue->attr.name[LTTNG_UST_SYM_NAME_LEN - 1] = '\0';
 
        /* Init node */
-       hashtable_node_init(&lue->node, (void *) lue->attr.name,
-                       strlen(lue->attr.name));
+       lttng_ht_node_init_str(&lue->node, lue->attr.name);
        /* Alloc context hash tables */
-       lue->ctx = hashtable_new(0);
+       lue->ctx = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
 
        DBG2("Trace UST event %s created", lue->attr.name);
 
@@ -297,8 +298,7 @@ struct ltt_ust_context *trace_ust_create_context(
        }
 
        uctx->ctx.ctx = ctx->ctx;
-       hashtable_node_init(&uctx->node, (void *)((unsigned long) uctx->ctx.ctx),
-                               sizeof(void *));
+       lttng_ht_node_init_ulong(&uctx->node, (unsigned long) uctx->ctx.ctx);
 
        return uctx;
 
@@ -311,8 +311,8 @@ error:
  */
 static void destroy_context_rcu(struct rcu_head *head)
 {
-       struct cds_lfht_node *node =
-               caa_container_of(head, struct cds_lfht_node, head);
+       struct lttng_ht_node_ulong *node =
+               caa_container_of(head, struct lttng_ht_node_ulong, head);
        struct ltt_ust_context *ctx =
                caa_container_of(node, struct ltt_ust_context, node);
 
@@ -322,20 +322,20 @@ static void destroy_context_rcu(struct rcu_head *head)
 /*
  * Cleanup UST context hash table.
  */
-static void destroy_context(struct cds_lfht *ht)
+static void destroy_context(struct lttng_ht *ht)
 {
        int ret;
-       struct cds_lfht_node *node;
-       struct cds_lfht_iter iter;
+       struct lttng_ht_node_ulong *node;
+       struct lttng_ht_iter iter;
 
-       cds_lfht_for_each(ht, &iter, node) {
-               ret = hashtable_del(ht, &iter);
+       cds_lfht_for_each_entry(ht->ht, &iter.iter, node, node) {
+               ret = lttng_ht_del(ht, &iter);
                if (!ret) {
                        call_rcu(&node->head, destroy_context_rcu);
                }
        }
 
-       hashtable_destroy(ht);
+       lttng_ht_destroy(ht);
 }
 
 /*
@@ -354,8 +354,8 @@ void trace_ust_destroy_event(struct ltt_ust_event *event)
  */
 static void destroy_event_rcu(struct rcu_head *head)
 {
-       struct cds_lfht_node *node =
-               caa_container_of(head, struct cds_lfht_node, head);
+       struct lttng_ht_node_str *node =
+               caa_container_of(head, struct lttng_ht_node_str, head);
        struct ltt_ust_event *event =
                caa_container_of(node, struct ltt_ust_event, node);
 
@@ -365,20 +365,20 @@ static void destroy_event_rcu(struct rcu_head *head)
 /*
  * Cleanup UST events hashtable.
  */
-static void destroy_event(struct cds_lfht *events)
+static void destroy_event(struct lttng_ht *events)
 {
        int ret;
-       struct cds_lfht_node *node;
-       struct cds_lfht_iter iter;
+       struct lttng_ht_node_str *node;
+       struct lttng_ht_iter iter;
 
-       cds_lfht_for_each(events, &iter, node) {
-               ret = hashtable_del(events, &iter);
+       cds_lfht_for_each_entry(events->ht, &iter.iter, node, node) {
+               ret = lttng_ht_del(events, &iter);
                if (!ret) {
                        call_rcu(&node->head, destroy_event_rcu);
                }
        }
 
-       hashtable_destroy(events);
+       lttng_ht_destroy(events);
 }
 
 /*
@@ -387,15 +387,15 @@ static void destroy_event(struct cds_lfht *events)
 void trace_ust_destroy_channel(struct ltt_ust_channel *channel)
 {
        int ret;
-       struct cds_lfht_node *node;
-       struct cds_lfht_iter iter;
+       struct lttng_ht_node_str *node;
+       struct lttng_ht_iter iter;
 
        DBG2("Trace destroy UST channel %s", channel->name);
 
        rcu_read_lock();
 
-       cds_lfht_for_each(channel->events, &iter, node) {
-               ret = hashtable_del(channel->events, &iter);
+       cds_lfht_for_each_entry(channel->events->ht, &iter.iter, node, node) {
+               ret = lttng_ht_del(channel->events, &iter);
                if (!ret) {
                        destroy_event(channel->events);
                }
@@ -412,8 +412,8 @@ void trace_ust_destroy_channel(struct ltt_ust_channel *channel)
  */
 static void destroy_channel_rcu(struct rcu_head *head)
 {
-       struct cds_lfht_node *node =
-               caa_container_of(head, struct cds_lfht_node, head);
+       struct lttng_ht_node_str *node =
+               caa_container_of(head, struct lttng_ht_node_str, head);
        struct ltt_ust_channel *channel =
                caa_container_of(node, struct ltt_ust_channel, node);
 
@@ -433,58 +433,58 @@ void trace_ust_destroy_metadata(struct ltt_ust_metadata *metadata)
 /*
  * Iterate over a hash table containing channels and cleanup safely.
  */
-static void destroy_channels(struct cds_lfht *channels)
+static void destroy_channels(struct lttng_ht *channels)
 {
        int ret;
-       struct cds_lfht_node *node;
-       struct cds_lfht_iter iter;
+       struct lttng_ht_node_str *node;
+       struct lttng_ht_iter iter;
 
-       cds_lfht_for_each(channels, &iter, node) {
-               ret = hashtable_del(channels, &iter);
+       cds_lfht_for_each_entry(channels->ht, &iter.iter, node, node) {
+               ret = lttng_ht_del(channels, &iter);
                if (!ret) {
                        call_rcu(&node->head, destroy_channel_rcu);
                }
        }
 
-       hashtable_destroy(channels);
+       lttng_ht_destroy(channels);
 }
 
 /*
  * Cleanup UST pid domain.
  */
-static void destroy_domain_pid(struct cds_lfht *ht)
+static void destroy_domain_pid(struct lttng_ht *ht)
 {
        int ret;
-       struct cds_lfht_iter iter;
+       struct lttng_ht_iter iter;
        struct ltt_ust_domain_pid *dpid;
 
-       cds_lfht_for_each_entry(ht, &iter, dpid, node) {
-               ret = hashtable_del(ht , &iter);
+       cds_lfht_for_each_entry(ht->ht, &iter.iter, dpid, node.node) {
+               ret = lttng_ht_del(ht , &iter);
                if (!ret) {
                        destroy_channels(dpid->channels);
                }
        }
 
-       hashtable_destroy(ht);
+       lttng_ht_destroy(ht);
 }
 
 /*
  * Cleanup UST exec name domain.
  */
-static void destroy_domain_exec(struct cds_lfht *ht)
+static void destroy_domain_exec(struct lttng_ht *ht)
 {
        int ret;
-       struct cds_lfht_iter iter;
+       struct lttng_ht_iter iter;
        struct ltt_ust_domain_exec *dexec;
 
-       cds_lfht_for_each_entry(ht, &iter, dexec, node) {
-               ret = hashtable_del(ht , &iter);
+       cds_lfht_for_each_entry(ht->ht, &iter.iter, dexec, node.node) {
+               ret = lttng_ht_del(ht , &iter);
                if (!ret) {
                        destroy_channels(dexec->channels);
                }
        }
 
-       hashtable_destroy(ht);
+       lttng_ht_destroy(ht);
 }
 
 /*
@@ -507,7 +507,7 @@ void trace_ust_destroy_session(struct ltt_ust_session *session)
 
        rcu_read_lock();
 
-       DBG2("Trace UST destroy session %d", session->uid);
+       DBG2("Trace UST destroy session %d", session->id);
 
        /* Cleaning up UST domain */
        destroy_domain_global(&session->domain_global);
index e834d2f6e1d0c1391922b7a2c6552953cab552f8..c033ed6c57c7959f3f2836a8d1e039032f6fff2f 100644 (file)
 #include <limits.h>
 #include <urcu.h>
 #include <urcu/list.h>
+
 #include <lttng/lttng.h>
+#include <lttng-ht.h>
 
 #include "ust-ctl.h"
 
-#include "../hashtable/rculfhash.h"
-
 /* UST Stream list */
 struct ltt_ust_stream_list {
        unsigned int count;
@@ -38,15 +38,15 @@ struct ltt_ust_stream_list {
 /* Context hash table nodes */
 struct ltt_ust_context {
        struct lttng_ust_context ctx;
-       struct cds_lfht_node node;
+       struct lttng_ht_node_ulong node;
 };
 
 /* UST event */
 struct ltt_ust_event {
        unsigned int enabled;
        struct lttng_ust_event attr;
-       struct cds_lfht *ctx;
-       struct cds_lfht_node node;
+       struct lttng_ht *ctx;
+       struct lttng_ht_node_str node;
 };
 
 /* UST stream */
@@ -64,9 +64,9 @@ struct ltt_ust_channel {
        char name[LTTNG_UST_SYM_NAME_LEN];
        char pathname[PATH_MAX];
        struct lttng_ust_channel attr;
-       struct cds_lfht *ctx;
-       struct cds_lfht *events;
-       struct cds_lfht_node node;
+       struct lttng_ht *ctx;
+       struct lttng_ht *events;
+       struct lttng_ht_node_str node;
 };
 
 /* UST Metadata */
@@ -80,26 +80,26 @@ struct ltt_ust_metadata {
 
 /* UST domain global (LTTNG_DOMAIN_UST) */
 struct ltt_ust_domain_global {
-       struct cds_lfht *channels;
+       struct lttng_ht *channels;
 };
 
 /* UST domain pid (LTTNG_DOMAIN_UST_PID) */
 struct ltt_ust_domain_pid {
        pid_t pid;
-       struct cds_lfht *channels;
-       struct cds_lfht_node node;
+       struct lttng_ht *channels;
+       struct lttng_ht_node_ulong node;
 };
 
 /* UST domain exec name (LTTNG_DOMAIN_UST_EXEC_NAME) */
 struct ltt_ust_domain_exec {
        char exec_name[LTTNG_UST_SYM_NAME_LEN];
-       struct cds_lfht *channels;
-       struct cds_lfht_node node;
+       struct lttng_ht *channels;
+       struct lttng_ht_node_str node;
 };
 
 /* UST session */
 struct ltt_ust_session {
-       int uid;   /* Unique identifier of session */
+       int id;    /* Unique identifier of session */
        int start_trace;
        char pathname[PATH_MAX];
        struct ltt_ust_domain_global domain_global;
@@ -108,8 +108,11 @@ struct ltt_ust_session {
         * contains a HT of channels. See ltt_ust_domain_exec and
         * ltt_ust_domain_pid data structures.
         */
-       struct cds_lfht *domain_pid;
-       struct cds_lfht *domain_exec;
+       struct lttng_ht *domain_pid;
+       struct lttng_ht *domain_exec;
+       /* UID/GID of the user owning the session */
+       uid_t uid;
+       gid_t gid;
 };
 
 #ifdef HAVE_LIBLTTNG_UST_CTL
@@ -117,15 +120,15 @@ struct ltt_ust_session {
 /*
  * Lookup functions. NULL is returned if not found.
  */
-struct ltt_ust_event *trace_ust_find_event_by_name(struct cds_lfht *ht,
+struct ltt_ust_event *trace_ust_find_event_by_name(struct lttng_ht *ht,
                char *name);
-struct ltt_ust_channel *trace_ust_find_channel_by_name(struct cds_lfht *ht,
+struct ltt_ust_channel *trace_ust_find_channel_by_name(struct lttng_ht *ht,
                char *name);
 
 /*
  * Create functions malloc() the data structure.
  */
-struct ltt_ust_session *trace_ust_create_session(char *path, unsigned int uid,
+struct ltt_ust_session *trace_ust_create_session(char *path, int session_id,
                struct lttng_domain *domain);
 struct ltt_ust_channel *trace_ust_create_channel(struct lttng_channel *attr,
                char *path);
@@ -146,14 +149,14 @@ void trace_ust_destroy_event(struct ltt_ust_event *event);
 #else /* HAVE_LIBLTTNG_UST_CTL */
 
 static inline
-struct ltt_ust_event *trace_ust_find_event_by_name(struct cds_lfht *ht,
+struct ltt_ust_event *trace_ust_find_event_by_name(struct lttng_ht *ht,
                char *name)
 {
        return NULL;
 }
 
 static inline
-struct ltt_ust_channel *trace_ust_find_channel_by_name(struct cds_lfht *ht,
+struct ltt_ust_channel *trace_ust_find_channel_by_name(struct lttng_ht *ht,
                char *name)
 {
        return NULL;
index 616a7ae2f50c04ebf1365bc20d218d3ec28b256e..d34134e94dbce9ab089c065b570cb01689e772e3 100644 (file)
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
+#include <runas.h>
 
 #include <urcu/compiler.h>
+
 #include <lttngerr.h>
+#include <lttng-ht.h>
 #include <lttng-share.h>
+#include <runas.h>
 
-#include "hashtable.h"
 #include "ust-app.h"
 #include "ust-consumer.h"
 #include "ust-ctl.h"
@@ -57,16 +60,16 @@ static
 void delete_ust_app_event(int sock, struct ust_app_event *ua_event)
 {
        int ret;
-       struct cds_lfht_iter iter;
+       struct lttng_ht_iter iter;
        struct ust_app_ctx *ua_ctx;
 
-       cds_lfht_for_each_entry(ua_event->ctx, &iter, ua_ctx, node) {
-               ret = hashtable_del(ua_event->ctx, &iter);
+       cds_lfht_for_each_entry(ua_event->ctx->ht, &iter.iter, ua_ctx,
+                       node.node) {
+               ret = lttng_ht_del(ua_event->ctx, &iter);
                assert(!ret);
                delete_ust_app_ctx(sock, ua_ctx);
        }
-       ret = hashtable_destroy(ua_event->ctx);
-       assert(!ret);
+       lttng_ht_destroy(ua_event->ctx);
 
        if (ua_event->obj != NULL) {
                ustctl_release_object(sock, ua_event->obj);
@@ -97,7 +100,7 @@ static
 void delete_ust_app_channel(int sock, struct ust_app_channel *ua_chan)
 {
        int ret;
-       struct cds_lfht_iter iter;
+       struct lttng_ht_iter iter;
        struct ust_app_event *ua_event;
        struct ust_app_ctx *ua_ctx;
        struct ltt_ust_stream *stream, *stmp;
@@ -109,22 +112,21 @@ void delete_ust_app_channel(int sock, struct ust_app_channel *ua_chan)
        }
 
        /* Wipe context */
-       cds_lfht_for_each_entry(ua_chan->ctx, &iter, ua_ctx, node) {
-               ret = hashtable_del(ua_chan->ctx, &iter);
+       cds_lfht_for_each_entry(ua_chan->ctx->ht, &iter.iter, ua_ctx, node.node) {
+               ret = lttng_ht_del(ua_chan->ctx, &iter);
                assert(!ret);
                delete_ust_app_ctx(sock, ua_ctx);
        }
-       ret = hashtable_destroy(ua_chan->ctx);
-       assert(!ret);
+       lttng_ht_destroy(ua_chan->ctx);
 
        /* Wipe events */
-       cds_lfht_for_each_entry(ua_chan->events, &iter, ua_event, node) {
-               ret = hashtable_del(ua_chan->events, &iter);
+       cds_lfht_for_each_entry(ua_chan->events->ht, &iter.iter, ua_event,
+                       node.node) {
+               ret = lttng_ht_del(ua_chan->events, &iter);
                assert(!ret);
                delete_ust_app_event(sock, ua_event);
        }
-       ret = hashtable_destroy(ua_chan->events);
-       assert(!ret);
+       lttng_ht_destroy(ua_chan->events);
 
        if (ua_chan->obj != NULL) {
                ustctl_release_object(sock, ua_chan->obj);
@@ -141,7 +143,7 @@ static
 void delete_ust_app_session(int sock, struct ust_app_session *ua_sess)
 {
        int ret;
-       struct cds_lfht_iter iter;
+       struct lttng_ht_iter iter;
        struct ust_app_channel *ua_chan;
 
        if (ua_sess->metadata) {
@@ -155,14 +157,17 @@ void delete_ust_app_session(int sock, struct ust_app_session *ua_sess)
                }
        }
 
-       cds_lfht_for_each_entry(ua_sess->channels, &iter, ua_chan, node) {
-               ret = hashtable_del(ua_sess->channels, &iter);
+       cds_lfht_for_each_entry(ua_sess->channels->ht, &iter.iter, ua_chan,
+                       node.node) {
+               ret = lttng_ht_del(ua_sess->channels, &iter);
                assert(!ret);
                delete_ust_app_channel(sock, ua_chan);
        }
-       ret = hashtable_destroy(ua_sess->channels);
-       assert(!ret);
+       lttng_ht_destroy(ua_sess->channels);
 
+       if (ua_sess->handle != -1) {
+               ustctl_release_handle(sock, ua_sess->handle);
+       }
        free(ua_sess);
 }
 
@@ -174,56 +179,34 @@ static
 void delete_ust_app(struct ust_app *app)
 {
        int ret, sock;
-       struct cds_lfht_node *node;
-       struct cds_lfht_iter iter;
+       struct lttng_ht_iter iter;
        struct ust_app_session *ua_sess;
 
        rcu_read_lock();
 
-       /* Remove from key hash table */
-       node = hashtable_lookup(ust_app_sock_key_map,
-                       (void *) ((unsigned long) app->key.sock), sizeof(void *), &iter);
-       if (node == NULL) {
-               /* Not suppose to happen */
-               ERR("UST app key %d not found in key hash table", app->key.sock);
-               goto end;
-       }
-
-       ret = hashtable_del(ust_app_sock_key_map, &iter);
-       if (ret) {
-               ERR("UST app unable to delete app sock %d from key hash table",
-                               app->key.sock);
-       } else {
-               DBG2("UST app pair sock %d key %d deleted",
-                               app->key.sock, app->key.pid);
-       }
-
-       /* Socket is already closed at this point */
-
        /* Delete ust app sessions info */
        sock = app->key.sock;
        app->key.sock = -1;
 
        /* Wipe sessions */
-       cds_lfht_for_each_entry(app->sessions, &iter, ua_sess, node) {
-               ret = hashtable_del(app->sessions, &iter);
+       cds_lfht_for_each_entry(app->sessions->ht, &iter.iter, ua_sess,
+                       node.node) {
+               ret = lttng_ht_del(app->sessions, &iter);
                assert(!ret);
                delete_ust_app_session(app->key.sock, ua_sess);
        }
-       ret = hashtable_destroy(app->sessions);
-       assert(!ret);
+       lttng_ht_destroy(app->sessions);
 
        /*
-        * Wait until we have removed the key from the sock hash table
-        * before closing this socket, otherwise an application could
-        * re-use the socket ID and race with the teardown, using the
-        * same hash table entry.
+        * Wait until we have removed the key from the sock hash table before
+        * closing this socket, otherwise an application could re-use the socket ID
+        * and race with the teardown, using the same hash table entry.
         */
        close(sock);
 
        DBG2("UST app pid %d deleted", app->key.pid);
        free(app);
-end:
+
        rcu_read_unlock();
 }
 
@@ -233,11 +216,12 @@ end:
 static
 void delete_ust_app_rcu(struct rcu_head *head)
 {
-       struct cds_lfht_node *node =
-               caa_container_of(head, struct cds_lfht_node, head);
+       struct lttng_ht_node_ulong *node =
+               caa_container_of(head, struct lttng_ht_node_ulong, head);
        struct ust_app *app =
                caa_container_of(node, struct ust_app, node);
 
+       DBG3("Call RCU deleting app PID %d", app->key.pid);
        delete_ust_app(app);
 }
 
@@ -257,7 +241,7 @@ struct ust_app_session *alloc_ust_app_session(void)
        }
 
        ua_sess->handle = -1;
-       ua_sess->channels = hashtable_new_str(0);
+       ua_sess->channels = lttng_ht_new(0, LTTNG_HT_TYPE_STRING);
 
        return ua_sess;
 
@@ -287,10 +271,9 @@ struct ust_app_channel *alloc_ust_app_channel(char *name,
 
        ua_chan->enabled = 1;
        ua_chan->handle = -1;
-       ua_chan->ctx = hashtable_new(0);
-       ua_chan->events = hashtable_new_str(0);
-       hashtable_node_init(&ua_chan->node, (void *) ua_chan->name,
-                       strlen(ua_chan->name));
+       ua_chan->ctx = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
+       ua_chan->events = lttng_ht_new(0, LTTNG_HT_TYPE_STRING);
+       lttng_ht_node_init_str(&ua_chan->node, ua_chan->name);
 
        CDS_INIT_LIST_HEAD(&ua_chan->streams.head);
 
@@ -326,9 +309,8 @@ struct ust_app_event *alloc_ust_app_event(char *name,
        ua_event->enabled = 1;
        strncpy(ua_event->name, name, sizeof(ua_event->name));
        ua_event->name[sizeof(ua_event->name) - 1] = '\0';
-       ua_event->ctx = hashtable_new(0);
-       hashtable_node_init(&ua_event->node, (void *) ua_event->name,
-                       strlen(ua_event->name));
+       ua_event->ctx = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
+       lttng_ht_node_init_str(&ua_event->node, ua_event->name);
 
        /* Copy attributes */
        if (attr) {
@@ -373,21 +355,21 @@ error:
 static
 struct ust_app *find_app_by_sock(int sock)
 {
-       struct cds_lfht_node *node;
+       struct lttng_ht_node_ulong *node;
        struct ust_app_key *key;
-       struct cds_lfht_iter iter;
+       struct lttng_ht_iter iter;
 
-       node = hashtable_lookup(ust_app_sock_key_map,
-                       (void *)((unsigned long) sock), sizeof(void *), &iter);
+       lttng_ht_lookup(ust_app_sock_key_map, (void *)((unsigned long) sock),
+                       &iter);
+       node = lttng_ht_iter_get_node_ulong(&iter);
        if (node == NULL) {
                DBG2("UST app find by sock %d key not found", sock);
                goto error;
        }
-
        key = caa_container_of(node, struct ust_app_key, node);
 
-       node = hashtable_lookup(ust_app_ht,
-                       (void *)((unsigned long) key->pid), sizeof(void *), &iter);
+       lttng_ht_lookup(ust_app_ht, (void *)((unsigned long) key->pid), &iter);
+       node = lttng_ht_iter_get_node_ulong(&iter);
        if (node == NULL) {
                DBG2("UST app find by sock %d not found", sock);
                goto error;
@@ -670,7 +652,7 @@ error:
 static void shadow_copy_event(struct ust_app_event *ua_event,
                struct ltt_ust_event *uevent)
 {
-       struct cds_lfht_iter iter;
+       struct lttng_ht_iter iter;
        struct ltt_ust_context *uctx;
        struct ust_app_ctx *ua_ctx;
 
@@ -680,14 +662,14 @@ static void shadow_copy_event(struct ust_app_event *ua_event,
        /* Copy event attributes */
        memcpy(&ua_event->attr, &uevent->attr, sizeof(ua_event->attr));
 
-       cds_lfht_for_each_entry(uevent->ctx, &iter, uctx, node) {
+       cds_lfht_for_each_entry(uevent->ctx->ht, &iter.iter, uctx, node.node) {
                ua_ctx = alloc_ust_app_ctx(&uctx->ctx);
                if (ua_ctx == NULL) {
                        continue;
                }
-               hashtable_node_init(&ua_ctx->node,
-                               (void *)((unsigned long) ua_ctx->ctx.ctx), sizeof(void *));
-               hashtable_add_unique(ua_event->ctx, &ua_ctx->node);
+               lttng_ht_node_init_ulong(&ua_ctx->node,
+                               (unsigned long) ua_ctx->ctx.ctx);
+               lttng_ht_add_unique_ulong(ua_event->ctx, &ua_ctx->node);
        }
 }
 
@@ -697,8 +679,8 @@ static void shadow_copy_event(struct ust_app_event *ua_event,
 static void shadow_copy_channel(struct ust_app_channel *ua_chan,
                struct ltt_ust_channel *uchan)
 {
-       struct cds_lfht_iter iter;
-       struct cds_lfht_node *ua_event_node;
+       struct lttng_ht_iter iter;
+       struct lttng_ht_node_str *ua_event_node;
        struct ltt_ust_event *uevent;
        struct ltt_ust_context *uctx;
        struct ust_app_event *ua_event;
@@ -711,23 +693,22 @@ static void shadow_copy_channel(struct ust_app_channel *ua_chan,
        /* Copy event attributes */
        memcpy(&ua_chan->attr, &uchan->attr, sizeof(ua_chan->attr));
 
-       cds_lfht_for_each_entry(uchan->ctx, &iter, uctx, node) {
+       cds_lfht_for_each_entry(uchan->ctx->ht, &iter.iter, uctx, node.node) {
                ua_ctx = alloc_ust_app_ctx(&uctx->ctx);
                if (ua_ctx == NULL) {
                        continue;
                }
-               hashtable_node_init(&ua_ctx->node,
-                               (void *)((unsigned long) ua_ctx->ctx.ctx), sizeof(void *));
-               hashtable_add_unique(ua_chan->ctx, &ua_ctx->node);
+               lttng_ht_node_init_ulong(&ua_ctx->node,
+                               (unsigned long) ua_ctx->ctx.ctx);
+               lttng_ht_add_unique_ulong(ua_chan->ctx, &ua_ctx->node);
        }
 
        /* Copy all events from ltt ust channel to ust app channel */
-       cds_lfht_for_each_entry(uchan->events, &iter, uevent, node) {
-               struct cds_lfht_iter uiter;
+       cds_lfht_for_each_entry(uchan->events->ht, &iter.iter, uevent, node.node) {
+               struct lttng_ht_iter uiter;
 
-               ua_event_node = hashtable_lookup(ua_chan->events,
-                               (void *) uevent->attr.name, strlen(uevent->attr.name),
-                               &uiter);
+               lttng_ht_lookup(ua_chan->events, (void *) uevent->attr.name, &uiter);
+               ua_event_node = lttng_ht_iter_get_node_str(&uiter);
                if (ua_event_node == NULL) {
                        DBG2("UST event %s not found on shadow copy channel",
                                        uevent->attr.name);
@@ -736,7 +717,7 @@ static void shadow_copy_channel(struct ust_app_channel *ua_chan,
                                continue;
                        }
                        shadow_copy_event(ua_event, uevent);
-                       hashtable_add_unique(ua_chan->events, &ua_event->node);
+                       lttng_ht_add_unique_str(ua_chan->events, &ua_event->node);
                }
        }
 
@@ -747,11 +728,10 @@ static void shadow_copy_channel(struct ust_app_channel *ua_chan,
  * Copy data between a UST app session and a regular LTT session.
  */
 static void shadow_copy_session(struct ust_app_session *ua_sess,
-               struct ltt_ust_session *usess,
-               struct ust_app *app)
+               struct ltt_ust_session *usess, struct ust_app *app)
 {
-       struct cds_lfht_node *ua_chan_node;
-       struct cds_lfht_iter iter;
+       struct lttng_ht_node_str *ua_chan_node;
+       struct lttng_ht_iter iter;
        struct ltt_ust_channel *uchan;
        struct ust_app_channel *ua_chan;
        time_t rawtime;
@@ -766,7 +746,9 @@ static void shadow_copy_session(struct ust_app_session *ua_sess,
 
        DBG2("Shadow copy of session handle %d", ua_sess->handle);
 
+       ua_sess->id = usess->id;
        ua_sess->uid = usess->uid;
+       ua_sess->gid = usess->gid;
 
        ret = snprintf(ua_sess->path, PATH_MAX,
                        "%s/%s-%d-%s",
@@ -781,13 +763,12 @@ static void shadow_copy_session(struct ust_app_session *ua_sess,
        /* TODO: support all UST domain */
 
        /* Iterate over all channels in global domain. */
-       cds_lfht_for_each_entry(usess->domain_global.channels, &iter,
-                       uchan, node) {
-               struct cds_lfht_iter uiter;
+       cds_lfht_for_each_entry(usess->domain_global.channels->ht, &iter.iter,
+                       uchan, node.node) {
+               struct lttng_ht_iter uiter;
 
-               ua_chan_node = hashtable_lookup(ua_sess->channels,
-                               (void *)uchan->name, strlen(uchan->name),
-                               &uiter);
+               lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter);
+               ua_chan_node = lttng_ht_iter_get_node_str(&uiter);
                if (ua_chan_node != NULL) {
                        continue;
                }
@@ -801,7 +782,7 @@ static void shadow_copy_session(struct ust_app_session *ua_sess,
                }
 
                shadow_copy_channel(ua_chan, uchan);
-               hashtable_add_unique(ua_sess->channels, &ua_chan->node);
+               lttng_ht_add_unique_str(ua_sess->channels, &ua_chan->node);
        }
 }
 
@@ -810,26 +791,24 @@ static void shadow_copy_session(struct ust_app_session *ua_sess,
  */
 static
 void __lookup_session_by_app(struct ltt_ust_session *usess,
-                       struct ust_app *app, struct cds_lfht_iter *iter)
+                       struct ust_app *app, struct lttng_ht_iter *iter)
 {
        /* Get right UST app session from app */
-       (void) hashtable_lookup(app->sessions,
-                       (void *) ((unsigned long) usess->uid), sizeof(void *),
-                       iter);
+       lttng_ht_lookup(app->sessions, (void *)((unsigned long) usess->uid), iter);
 }
 
 /*
  * Return ust app session from the app session hashtable using the UST session
- * uid.
+ * id.
  */
 static struct ust_app_session *lookup_session_by_app(
                struct ltt_ust_session *usess, struct ust_app *app)
 {
-       struct cds_lfht_iter iter;
-       struct cds_lfht_node *node;
+       struct lttng_ht_iter iter;
+       struct lttng_ht_node_ulong *node;
 
        __lookup_session_by_app(usess, app, &iter);
-       node = hashtable_iter_get_node(&iter);
+       node = lttng_ht_iter_get_node_ulong(&iter);
        if (node == NULL) {
                goto error;
        }
@@ -854,8 +833,8 @@ static struct ust_app_session *create_ust_app_session(
 
        ua_sess = lookup_session_by_app(usess, app);
        if (ua_sess == NULL) {
-               DBG2("UST app pid: %d session uid %d not found, creating it",
-                               app->key.pid, usess->uid);
+               DBG2("UST app pid: %d session id %d not found, creating it",
+                               app->key.pid, usess->id);
                ua_sess = alloc_ust_app_session();
                if (ua_sess == NULL) {
                        /* Only malloc can failed so something is really wrong */
@@ -877,9 +856,8 @@ static struct ust_app_session *create_ust_app_session(
                ua_sess->handle = ret;
 
                /* Add ust app session to app's HT */
-               hashtable_node_init(&ua_sess->node,
-                               (void *)((unsigned long) ua_sess->uid), sizeof(void *));
-               hashtable_add_unique(app->sessions, &ua_sess->node);
+               lttng_ht_node_init_ulong(&ua_sess->node, (unsigned long) ua_sess->uid);
+               lttng_ht_add_unique_ulong(app->sessions, &ua_sess->node);
 
                DBG2("UST app session created successfully with handle %d", ret);
        }
@@ -899,14 +877,14 @@ int create_ust_app_channel_context(struct ust_app_session *ua_sess,
                struct ust_app *app)
 {
        int ret = 0;
-       struct cds_lfht_iter iter;
-       struct cds_lfht_node *node;
+       struct lttng_ht_iter iter;
+       struct lttng_ht_node_ulong *node;
        struct ust_app_ctx *ua_ctx;
 
        DBG2("UST app adding context to channel %s", ua_chan->name);
 
-       node = hashtable_lookup(ua_chan->ctx, (void *)((unsigned long)uctx->ctx),
-                       sizeof(void *), &iter);
+       lttng_ht_lookup(ua_chan->ctx, (void *)((unsigned long)uctx->ctx), &iter);
+       node = lttng_ht_iter_get_node_ulong(&iter);
        if (node != NULL) {
                ret = -EEXIST;
                goto error;
@@ -919,9 +897,8 @@ int create_ust_app_channel_context(struct ust_app_session *ua_sess,
                goto error;
        }
 
-       hashtable_node_init(&ua_ctx->node,
-                       (void *)((unsigned long) ua_ctx->ctx.ctx), sizeof(void *));
-       hashtable_add_unique(ua_chan->ctx, &ua_ctx->node);
+       lttng_ht_node_init_ulong(&ua_ctx->node, (unsigned long) ua_ctx->ctx.ctx);
+       lttng_ht_add_unique_ulong(ua_chan->ctx, &ua_ctx->node);
 
        ret = create_ust_channel_context(ua_chan, ua_ctx, app);
        if (ret < 0) {
@@ -941,14 +918,14 @@ int create_ust_app_event_context(struct ust_app_session *ua_sess,
                struct ust_app *app)
 {
        int ret = 0;
-       struct cds_lfht_iter iter;
-       struct cds_lfht_node *node;
+       struct lttng_ht_iter iter;
+       struct lttng_ht_node_ulong *node;
        struct ust_app_ctx *ua_ctx;
 
        DBG2("UST app adding context to event %s", ua_event->name);
 
-       node = hashtable_lookup(ua_event->ctx, (void *)((unsigned long)uctx->ctx),
-                       sizeof(void *), &iter);
+       lttng_ht_lookup(ua_event->ctx, (void *)((unsigned long)uctx->ctx), &iter);
+       node = lttng_ht_iter_get_node_ulong(&iter);
        if (node != NULL) {
                ret = -EEXIST;
                goto error;
@@ -961,9 +938,8 @@ int create_ust_app_event_context(struct ust_app_session *ua_sess,
                goto error;
        }
 
-       hashtable_node_init(&ua_ctx->node,
-                       (void *)((unsigned long) ua_ctx->ctx.ctx), sizeof(void *));
-       hashtable_add_unique(ua_event->ctx, &ua_ctx->node);
+       lttng_ht_node_init_ulong(&ua_ctx->node, (unsigned long) ua_ctx->ctx.ctx);
+       lttng_ht_add_unique_ulong(ua_event->ctx, &ua_ctx->node);
 
        ret = create_ust_event_context(ua_event, ua_ctx, app);
        if (ret < 0) {
@@ -1040,15 +1016,15 @@ static int enable_ust_app_channel(struct ust_app_session *ua_sess,
                struct ltt_ust_channel *uchan, struct ust_app *app)
 {
        int ret = 0;
-       struct cds_lfht_iter iter;
-       struct cds_lfht_node *ua_chan_node;
+       struct lttng_ht_iter iter;
+       struct lttng_ht_node_str *ua_chan_node;
        struct ust_app_channel *ua_chan;
 
-       ua_chan_node = hashtable_lookup(ua_sess->channels,
-                       (void *)uchan->name, strlen(uchan->name), &iter);
+       lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &iter);
+       ua_chan_node = lttng_ht_iter_get_node_str(&iter);
        if (ua_chan_node == NULL) {
-               DBG2("Unable to find channel %s in ust session uid %u",
-                               uchan->name, ua_sess->uid);
+               DBG2("Unable to find channel %s in ust session id %u",
+                               uchan->name, ua_sess->id);
                goto error;
        }
 
@@ -1071,23 +1047,23 @@ static struct ust_app_channel *create_ust_app_channel(
                struct ust_app *app)
 {
        int ret = 0;
-       struct cds_lfht_iter iter;
-       struct cds_lfht_node *ua_chan_node;
+       struct lttng_ht_iter iter;
+       struct lttng_ht_node_str *ua_chan_node;
        struct ust_app_channel *ua_chan;
 
        /* Lookup channel in the ust app session */
-       ua_chan_node = hashtable_lookup(ua_sess->channels,
-                       (void *)uchan->name, strlen(uchan->name), &iter);
+       lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &iter);
+       ua_chan_node = lttng_ht_iter_get_node_str(&iter);
        if (ua_chan_node == NULL) {
-               DBG2("Unable to find channel %s in ust session uid %u",
-                               uchan->name, ua_sess->uid);
+               DBG2("Unable to find channel %s in ust session id %u",
+                               uchan->name, ua_sess->id);
                ua_chan = alloc_ust_app_channel(uchan->name, &uchan->attr);
                if (ua_chan == NULL) {
                        goto error;
                }
                shadow_copy_channel(ua_chan, uchan);
 
-               hashtable_add_unique(ua_sess->channels, &ua_chan->node);
+               lttng_ht_add_unique_str(ua_sess->channels, &ua_chan->node);
        } else {
                ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node);
        }
@@ -1112,13 +1088,13 @@ int create_ust_app_event(struct ust_app_session *ua_sess,
                struct ust_app *app)
 {
        int ret = 0;
-       struct cds_lfht_iter iter;
-       struct cds_lfht_node *ua_event_node;
+       struct lttng_ht_iter iter;
+       struct lttng_ht_node_str *ua_event_node;
        struct ust_app_event *ua_event;
 
        /* Get event node */
-       ua_event_node = hashtable_lookup(ua_chan->events,
-                       (void *)uevent->attr.name, strlen(uevent->attr.name), &iter);
+       lttng_ht_lookup(ua_chan->events, (void *)uevent->attr.name, &iter);
+       ua_event_node = lttng_ht_iter_get_node_str(&iter);
        if (ua_event_node != NULL) {
                ERR("UST app event %s already exist. Stopping creation.",
                                uevent->attr.name);
@@ -1145,7 +1121,7 @@ int create_ust_app_event(struct ust_app_session *ua_sess,
 
        ua_event->enabled = 1;
 
-       hashtable_add_unique(ua_chan->events, &ua_event->node);
+       lttng_ht_add_unique_str(ua_chan->events, &ua_event->node);
 
        DBG2("UST app create event %s for PID %d completed",
                        ua_event->name, app->key.pid);
@@ -1162,7 +1138,6 @@ static int create_ust_app_metadata(struct ust_app_session *ua_sess,
                char *pathname, struct ust_app *app)
 {
        int ret = 0;
-       mode_t old_umask;
 
        if (ua_sess->metadata == NULL) {
                /* Allocate UST metadata */
@@ -1188,13 +1163,12 @@ static int create_ust_app_metadata(struct ust_app_session *ua_sess,
                        goto error;
                }
 
-               old_umask = umask(0);
-               ret = mkdir(ua_sess->path, S_IRWXU | S_IRWXG);
+               ret = mkdir_run_as(ua_sess->path, S_IRWXU | S_IRWXG,
+                               ua_sess->uid, ua_sess->gid);
                if (ret < 0) {
                        PERROR("mkdir UST metadata");
                        goto error;
                }
-               umask(old_umask);
 
                ret = snprintf(ua_sess->metadata->pathname, PATH_MAX,
                                "%s/metadata", ua_sess->path);
@@ -1219,7 +1193,7 @@ error:
 /*
  * Return pointer to traceable apps list.
  */
-struct cds_lfht *ust_app_get_ht(void)
+struct lttng_ht *ust_app_get_ht(void)
 {
        return ust_app_ht;
 }
@@ -1229,12 +1203,12 @@ struct cds_lfht *ust_app_get_ht(void)
  */
 struct ust_app *ust_app_find_by_pid(pid_t pid)
 {
-       struct cds_lfht_node *node;
-       struct cds_lfht_iter iter;
+       struct lttng_ht_node_ulong *node;
+       struct lttng_ht_iter iter;
 
        rcu_read_lock();
-       node = hashtable_lookup(ust_app_ht,
-                       (void *)((unsigned long) pid), sizeof(void *), &iter);
+       lttng_ht_lookup(ust_app_ht, (void *)((unsigned long) pid), &iter);
+       node = lttng_ht_iter_get_node_ulong(&iter);
        if (node == NULL) {
                DBG2("UST app no found with pid %d", pid);
                goto error;
@@ -1283,19 +1257,17 @@ int ust_app_register(struct ust_register_msg *msg, int sock)
        lta->v_minor = msg->minor;
        strncpy(lta->name, msg->name, sizeof(lta->name));
        lta->name[16] = '\0';
-       lta->sessions = hashtable_new(0);
+       lta->sessions = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
 
        /* Set key map */
        lta->key.pid = msg->pid;
-       hashtable_node_init(&lta->node, (void *)((unsigned long)lta->key.pid),
-                       sizeof(void *));
+       lttng_ht_node_init_ulong(&lta->node, (unsigned long)lta->key.pid);
        lta->key.sock = sock;
-       hashtable_node_init(&lta->key.node, (void *)((unsigned long)lta->key.sock),
-                       sizeof(void *));
+       lttng_ht_node_init_ulong(&lta->key.node, (unsigned long)lta->key.sock);
 
        rcu_read_lock();
-       hashtable_add_unique(ust_app_sock_key_map, &lta->key.node);
-       hashtable_add_unique(ust_app_ht, &lta->node);
+       lttng_ht_add_unique_ulong(ust_app_sock_key_map, &lta->key.node);
+       lttng_ht_add_unique_ulong(ust_app_ht, &lta->node);
        rcu_read_unlock();
 
        DBG("App registered with pid:%d ppid:%d uid:%d gid:%d sock:%d name:%s"
@@ -1314,8 +1286,8 @@ int ust_app_register(struct ust_register_msg *msg, int sock)
 void ust_app_unregister(int sock)
 {
        struct ust_app *lta;
-       struct cds_lfht_node *node;
-       struct cds_lfht_iter iter;
+       struct lttng_ht_node_ulong *node;
+       struct lttng_ht_iter iter;
        int ret;
 
        rcu_read_lock();
@@ -1328,14 +1300,14 @@ void ust_app_unregister(int sock)
        DBG("PID %d unregistering with sock %d", lta->key.pid, sock);
 
        /* Get the node reference for a call_rcu */
-       node = hashtable_lookup(ust_app_ht,
-                       (void *)((unsigned long) lta->key.pid), sizeof(void *), &iter);
+       lttng_ht_lookup(ust_app_ht, (void *)((unsigned long) lta->key.pid), &iter);
+       node = lttng_ht_iter_get_node_ulong(&iter);
        if (node == NULL) {
                ERR("Unable to find app sock %d by pid %d", sock, lta->key.pid);
                goto error;
        }
 
-       ret = hashtable_del(ust_app_ht, &iter);
+       ret = lttng_ht_del(ust_app_ht, &iter);
        assert(!ret);
        call_rcu(&node->head, delete_ust_app_rcu);
 error:
@@ -1351,7 +1323,7 @@ unsigned long ust_app_list_count(void)
        unsigned long count;
 
        rcu_read_lock();
-       count = hashtable_get_count(ust_app_ht);
+       count = lttng_ht_get_count(ust_app_ht);
        rcu_read_unlock();
 
        return count;
@@ -1364,7 +1336,7 @@ int ust_app_list_events(struct lttng_event **events)
 {
        int ret, handle;
        size_t nbmem, count = 0;
-       struct cds_lfht_iter iter;
+       struct lttng_ht_iter iter;
        struct ust_app *app;
        struct lttng_event *tmp;
 
@@ -1378,8 +1350,8 @@ int ust_app_list_events(struct lttng_event **events)
 
        rcu_read_lock();
 
-       cds_lfht_for_each_entry(ust_app_ht, &iter, app, node) {
-               struct lttng_ust_tracepoint_iter iter;
+       cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, node.node) {
+               struct lttng_ust_tracepoint_iter uiter;
 
                handle = ustctl_tracepoint_list(app->key.sock);
                if (handle < 0) {
@@ -1389,7 +1361,7 @@ int ust_app_list_events(struct lttng_event **events)
                }
 
                while ((ret = ustctl_tracepoint_list_get(app->key.sock, handle,
-                                               &iter)) != -ENOENT) {
+                                               &uiter)) != -ENOENT) {
                        if (count >= nbmem) {
                                DBG2("Reallocating event list from %zu to %zu entries", nbmem,
                                                2 * nbmem);
@@ -1401,9 +1373,9 @@ int ust_app_list_events(struct lttng_event **events)
                                        goto rcu_error;
                                }
                        }
-                       memcpy(tmp[count].name, iter.name, LTTNG_UST_SYM_NAME_LEN);
-                       memcpy(tmp[count].loglevel, iter.loglevel, LTTNG_UST_SYM_NAME_LEN);
-                       tmp[count].loglevel_value = iter.loglevel_value;
+                       memcpy(tmp[count].name, uiter.name, LTTNG_UST_SYM_NAME_LEN);
+                       memcpy(tmp[count].loglevel, uiter.loglevel, LTTNG_UST_SYM_NAME_LEN);
+                       tmp[count].loglevel_value = uiter.loglevel_value;
                        tmp[count].type = LTTNG_UST_TRACEPOINT;
                        tmp[count].pid = app->key.pid;
                        tmp[count].enabled = -1;
@@ -1428,21 +1400,27 @@ error:
 void ust_app_clean_list(void)
 {
        int ret;
-       struct cds_lfht_iter iter;
-       struct ust_app *app;
+       struct lttng_ht_iter iter;
+       struct lttng_ht_node_ulong *node;
 
        DBG2("UST app cleaning registered apps hash table");
 
        rcu_read_lock();
 
-       cds_lfht_for_each_entry(ust_app_ht, &iter, app, node) {
-               ret = hashtable_del(ust_app_ht, &iter);
+       cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, node, node) {
+               ret = lttng_ht_del(ust_app_ht, &iter);
                assert(!ret);
-               call_rcu(&iter.node->head, delete_ust_app_rcu);
+               call_rcu(&node->head, delete_ust_app_rcu);
        }
+       /* Destroy is done only when the ht is empty */
+       lttng_ht_destroy(ust_app_ht);
 
-       hashtable_destroy(ust_app_ht);
-       hashtable_destroy(ust_app_sock_key_map);
+       cds_lfht_for_each_entry(ust_app_sock_key_map->ht, &iter.iter, node, node) {
+               ret = lttng_ht_del(ust_app_sock_key_map, &iter);
+               assert(!ret);
+       }
+       /* Destroy is done only when the ht is empty */
+       lttng_ht_destroy(ust_app_sock_key_map);
 
        rcu_read_unlock();
 }
@@ -1452,8 +1430,8 @@ void ust_app_clean_list(void)
  */
 void ust_app_ht_alloc(void)
 {
-       ust_app_ht = hashtable_new(0);
-       ust_app_sock_key_map = hashtable_new(0);
+       ust_app_ht = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
+       ust_app_sock_key_map = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
 }
 
 /*
@@ -1463,8 +1441,8 @@ int ust_app_disable_channel_glb(struct ltt_ust_session *usess,
                struct ltt_ust_channel *uchan)
 {
        int ret = 0;
-       struct cds_lfht_iter iter;
-       struct cds_lfht_node *ua_chan_node;
+       struct lttng_ht_iter iter;
+       struct lttng_ht_node_str *ua_chan_node;
        struct ust_app *app;
        struct ust_app_session *ua_sess;
        struct ust_app_channel *ua_chan;
@@ -1475,21 +1453,22 @@ int ust_app_disable_channel_glb(struct ltt_ust_session *usess,
                goto error;
        }
 
-       DBG2("UST app disabling channel %s from global domain for session uid %d",
-                       uchan->name, usess->uid);
+       DBG2("UST app disabling channel %s from global domain for session id %d",
+                       uchan->name, usess->id);
 
        rcu_read_lock();
 
        /* For every registered applications */
-       cds_lfht_for_each_entry(ust_app_ht, &iter, app, node) {
+       cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, node.node) {
+               struct lttng_ht_iter uiter;
                ua_sess = lookup_session_by_app(usess, app);
                if (ua_sess == NULL) {
                        continue;
                }
 
                /* Get channel */
-               ua_chan_node = hashtable_lookup(ua_sess->channels,
-                               (void *)uchan->name, strlen(uchan->name), &iter);
+               lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter);
+               ua_chan_node = lttng_ht_iter_get_node_str(&uiter);
                /* If the session if found for the app, the channel must be there */
                assert(ua_chan_node);
 
@@ -1518,7 +1497,7 @@ int ust_app_enable_channel_glb(struct ltt_ust_session *usess,
                struct ltt_ust_channel *uchan)
 {
        int ret = 0;
-       struct cds_lfht_iter iter;
+       struct lttng_ht_iter iter;
        struct ust_app *app;
        struct ust_app_session *ua_sess;
 
@@ -1528,13 +1507,13 @@ int ust_app_enable_channel_glb(struct ltt_ust_session *usess,
                goto error;
        }
 
-       DBG2("UST app enabling channel %s to global domain for session uid %d",
-                       uchan->name, usess->uid);
+       DBG2("UST app enabling channel %s to global domain for session id %d",
+                       uchan->name, usess->id);
 
        rcu_read_lock();
 
        /* For every registered applications */
-       cds_lfht_for_each_entry(ust_app_ht, &iter, app, node) {
+       cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, node.node) {
                ua_sess = lookup_session_by_app(usess, app);
                if (ua_sess == NULL) {
                        continue;
@@ -1561,20 +1540,20 @@ int ust_app_disable_event_glb(struct ltt_ust_session *usess,
                struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent)
 {
        int ret = 0;
-       struct cds_lfht_iter iter, uiter;
-       struct cds_lfht_node *ua_chan_node, *ua_event_node;
+       struct lttng_ht_iter iter, uiter;
+       struct lttng_ht_node_str *ua_chan_node, *ua_event_node;
        struct ust_app *app;
        struct ust_app_session *ua_sess;
        struct ust_app_channel *ua_chan;
        struct ust_app_event *ua_event;
 
        DBG("UST app disabling event %s for all apps in channel "
-                       "%s for session uid %d", uevent->attr.name, uchan->name, usess->uid);
+                       "%s for session id %d", uevent->attr.name, uchan->name, usess->id);
 
        rcu_read_lock();
 
        /* For all registered applications */
-       cds_lfht_for_each_entry(ust_app_ht, &iter, app, node) {
+       cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, node.node) {
                ua_sess = lookup_session_by_app(usess, app);
                if (ua_sess == NULL) {
                        /* Next app */
@@ -1582,17 +1561,17 @@ int ust_app_disable_event_glb(struct ltt_ust_session *usess,
                }
 
                /* Lookup channel in the ust app session */
-               ua_chan_node = hashtable_lookup(ua_sess->channels,
-                               (void *)uchan->name, strlen(uchan->name), &uiter);
+               lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter);
+               ua_chan_node = lttng_ht_iter_get_node_str(&uiter);
                if (ua_chan_node == NULL) {
-                       DBG2("Channel %s not found in session uid %d for app pid %d."
-                                       "Skipping", uchan->name, usess->uid, app->key.pid);
+                       DBG2("Channel %s not found in session id %d for app pid %d."
+                                       "Skipping", uchan->name, usess->id, app->key.pid);
                        continue;
                }
                ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node);
 
-               ua_event_node = hashtable_lookup(ua_chan->events,
-                               (void *)uevent->attr.name, strlen(uevent->attr.name), &uiter);
+               lttng_ht_lookup(ua_chan->events, (void *)uevent->attr.name, &uiter);
+               ua_event_node = lttng_ht_iter_get_node_str(&uiter);
                if (ua_event_node == NULL) {
                        DBG2("Event %s not found in channel %s for app pid %d."
                                        "Skipping", uevent->attr.name, uchan->name, app->key.pid);
@@ -1620,34 +1599,35 @@ int ust_app_disable_all_event_glb(struct ltt_ust_session *usess,
                struct ltt_ust_channel *uchan)
 {
        int ret = 0;
-       struct cds_lfht_iter iter, uiter;
-       struct cds_lfht_node *ua_chan_node;
+       struct lttng_ht_iter iter, uiter;
+       struct lttng_ht_node_str *ua_chan_node;
        struct ust_app *app;
        struct ust_app_session *ua_sess;
        struct ust_app_channel *ua_chan;
        struct ust_app_event *ua_event;
 
        DBG("UST app disabling all event for all apps in channel "
-                       "%s for session uid %d", uchan->name, usess->uid);
+                       "%s for session id %d", uchan->name, usess->id);
 
        rcu_read_lock();
 
        /* For all registered applications */
-       cds_lfht_for_each_entry(ust_app_ht, &iter, app, node) {
+       cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, node.node) {
                ua_sess = lookup_session_by_app(usess, app);
                /* If ua_sess is NULL, there is a code flow error */
                assert(ua_sess);
 
                /* Lookup channel in the ust app session */
-               ua_chan_node = hashtable_lookup(ua_sess->channels, (void *)uchan->name,
-                               strlen(uchan->name), &uiter);
+               lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter);
+               ua_chan_node = lttng_ht_iter_get_node_str(&uiter);
                /* If the channel is not found, there is a code flow error */
                assert(ua_chan_node);
 
                ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node);
 
                /* Disable each events of channel */
-               cds_lfht_for_each_entry(ua_chan->events, &uiter, ua_event, node) {
+               cds_lfht_for_each_entry(ua_chan->events->ht, &uiter.iter, ua_event,
+                               node.node) {
                        ret = disable_ust_app_event(ua_sess, ua_event, app);
                        if (ret < 0) {
                                /* XXX: Report error someday... */
@@ -1668,7 +1648,7 @@ int ust_app_create_channel_glb(struct ltt_ust_session *usess,
                struct ltt_ust_channel *uchan)
 {
        int ret = 0;
-       struct cds_lfht_iter iter;
+       struct lttng_ht_iter iter;
        struct ust_app *app;
        struct ust_app_session *ua_sess;
        struct ust_app_channel *ua_chan;
@@ -1679,13 +1659,13 @@ int ust_app_create_channel_glb(struct ltt_ust_session *usess,
                goto error;
        }
 
-       DBG2("UST app adding channel %s to global domain for session uid %d",
-                       uchan->name, usess->uid);
+       DBG2("UST app adding channel %s to global domain for session id %d",
+                       uchan->name, usess->id);
 
        rcu_read_lock();
 
        /* For every registered applications */
-       cds_lfht_for_each_entry(ust_app_ht, &iter, app, node) {
+       cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, node.node) {
                /*
                 * Create session on the tracer side and add it to app session HT. Note
                 * that if session exist, it will simply return a pointer to the ust
@@ -1716,15 +1696,15 @@ int ust_app_enable_event_glb(struct ltt_ust_session *usess,
                struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent)
 {
        int ret = 0;
-       struct cds_lfht_iter iter, uiter;
-       struct cds_lfht_node *ua_chan_node, *ua_event_node;
+       struct lttng_ht_iter iter, uiter;
+       struct lttng_ht_node_str *ua_chan_node, *ua_event_node;
        struct ust_app *app;
        struct ust_app_session *ua_sess;
        struct ust_app_channel *ua_chan;
        struct ust_app_event *ua_event;
 
-       DBG("UST app enabling event %s for all apps for session uid %d",
-                       uevent->attr.name, usess->uid);
+       DBG("UST app enabling event %s for all apps for session id %d",
+                       uevent->attr.name, usess->id);
 
        /*
         * NOTE: At this point, this function is called only if the session and
@@ -1735,21 +1715,21 @@ int ust_app_enable_event_glb(struct ltt_ust_session *usess,
        rcu_read_lock();
 
        /* For all registered applications */
-       cds_lfht_for_each_entry(ust_app_ht, &iter, app, node) {
+       cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, node.node) {
                ua_sess = lookup_session_by_app(usess, app);
                /* If ua_sess is NULL, there is a code flow error */
                assert(ua_sess);
 
                /* Lookup channel in the ust app session */
-               ua_chan_node = hashtable_lookup(ua_sess->channels, (void *)uchan->name,
-                               strlen(uchan->name), &uiter);
+               lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter);
+               ua_chan_node = lttng_ht_iter_get_node_str(&uiter);
                /* If the channel is not found, there is a code flow error */
                assert(ua_chan_node);
 
                ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node);
 
-               ua_event_node = hashtable_lookup(ua_chan->events,
-                               (void*)uevent->attr.name, strlen(uevent->attr.name), &uiter);
+               lttng_ht_lookup(ua_chan->events, (void*)uevent->attr.name, &uiter);
+               ua_event_node = lttng_ht_iter_get_node_str(&uiter);
                if (ua_event_node == NULL) {
                        DBG3("UST app enable event %s not found for app PID %d."
                                        "Skipping app", uevent->attr.name, app->key.pid);
@@ -1776,14 +1756,14 @@ int ust_app_create_event_glb(struct ltt_ust_session *usess,
                struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent)
 {
        int ret = 0;
-       struct cds_lfht_iter iter, uiter;
-       struct cds_lfht_node *ua_chan_node;
+       struct lttng_ht_iter iter, uiter;
+       struct lttng_ht_node_str *ua_chan_node;
        struct ust_app *app;
        struct ust_app_session *ua_sess;
        struct ust_app_channel *ua_chan;
 
-       DBG("UST app creating event %s for all apps for session uid %d",
-                       uevent->attr.name, usess->uid);
+       DBG("UST app creating event %s for all apps for session id %d",
+                       uevent->attr.name, usess->id);
 
        /*
         * NOTE: At this point, this function is called only if the session and
@@ -1794,14 +1774,14 @@ int ust_app_create_event_glb(struct ltt_ust_session *usess,
        rcu_read_lock();
 
        /* For all registered applications */
-       cds_lfht_for_each_entry(ust_app_ht, &iter, app, node) {
+       cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, node.node) {
                ua_sess = lookup_session_by_app(usess, app);
                /* If ua_sess is NULL, there is a code flow error */
                assert(ua_sess);
 
                /* Lookup channel in the ust app session */
-               ua_chan_node = hashtable_lookup(ua_sess->channels, (void *)uchan->name,
-                               strlen(uchan->name), &uiter);
+               lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter);
+               ua_chan_node = lttng_ht_iter_get_node_str(&uiter);
                /* If the channel is not found, there is a code flow error */
                assert(ua_chan_node);
 
@@ -1824,7 +1804,7 @@ int ust_app_create_event_glb(struct ltt_ust_session *usess,
 int ust_app_start_trace(struct ltt_ust_session *usess, struct ust_app *app)
 {
        int ret = 0;
-       struct cds_lfht_iter iter;
+       struct lttng_ht_iter iter;
        struct ust_app_session *ua_sess;
        struct ust_app_channel *ua_chan;
        struct ltt_ust_stream *ustream;
@@ -1850,7 +1830,8 @@ int ust_app_start_trace(struct ltt_ust_session *usess, struct ust_app *app)
        }
 
        /* For each channel */
-       cds_lfht_for_each_entry(ua_sess->channels, &iter, ua_chan, node) {
+       cds_lfht_for_each_entry(ua_sess->channels->ht, &iter.iter, ua_chan,
+                       node.node) {
                /* Create all streams */
                while (1) {
                        /* Create UST stream */
@@ -1927,7 +1908,7 @@ error_rcu_unlock:
 int ust_app_stop_trace(struct ltt_ust_session *usess, struct ust_app *app)
 {
        int ret = 0;
-       struct cds_lfht_iter iter;
+       struct lttng_ht_iter iter;
        struct ust_app_session *ua_sess;
        struct ust_app_channel *ua_chan;
 
@@ -1941,14 +1922,19 @@ int ust_app_stop_trace(struct ltt_ust_session *usess, struct ust_app *app)
                goto error_rcu_unlock;
        }
 
-       /* Flush all buffers before stopping */
-       ret = ustctl_sock_flush_buffer(app->key.sock, ua_sess->metadata->obj);
+       /* This inhibits UST tracing */
+       ret = ustctl_stop_session(app->key.sock, ua_sess->handle);
        if (ret < 0) {
-               ERR("UST app PID %d metadata flush failed", app->key.pid);
-               ERR("Ended with ret %d", ret);
+               ERR("Error stopping tracing for app pid: %d", app->key.pid);
+               goto error_rcu_unlock;
        }
 
-       cds_lfht_for_each_entry(ua_sess->channels, &iter, ua_chan, node) {
+       /* Quiescent wait after stopping trace */
+       ustctl_wait_quiescent(app->key.sock);
+
+       /* Flushing buffers */
+       cds_lfht_for_each_entry(ua_sess->channels->ht, &iter.iter, ua_chan,
+                       node.node) {
                ret = ustctl_sock_flush_buffer(app->key.sock, ua_chan->obj);
                if (ret < 0) {
                        ERR("UST app PID %d channel %s flush failed",
@@ -1959,18 +1945,15 @@ int ust_app_stop_trace(struct ltt_ust_session *usess, struct ust_app *app)
                }
        }
 
-       /* This inhibits UST tracing */
-       ret = ustctl_stop_session(app->key.sock, ua_sess->handle);
+       /* Flush all buffers before stopping */
+       ret = ustctl_sock_flush_buffer(app->key.sock, ua_sess->metadata->obj);
        if (ret < 0) {
-               ERR("Error stopping tracing for app pid: %d", app->key.pid);
-               goto error_rcu_unlock;
+               ERR("UST app PID %d metadata flush failed", app->key.pid);
+               ERR("Ended with ret %d", ret);
        }
 
        rcu_read_unlock();
 
-       /* Quiescent wait after stopping trace */
-       ustctl_wait_quiescent(app->key.sock);
-
        return 0;
 
 error_rcu_unlock:
@@ -1985,8 +1968,8 @@ int ust_app_destroy_trace(struct ltt_ust_session *usess, struct ust_app *app)
 {
        struct ust_app_session *ua_sess;
        struct lttng_ust_object_data obj;
-       struct cds_lfht_iter iter;
-       struct cds_lfht_node *node;
+       struct lttng_ht_iter iter;
+       struct lttng_ht_node_ulong *node;
        int ret;
 
        DBG("Destroy tracing for ust app pid %d", app->key.pid);
@@ -1994,13 +1977,13 @@ int ust_app_destroy_trace(struct ltt_ust_session *usess, struct ust_app *app)
        rcu_read_lock();
 
        __lookup_session_by_app(usess, app, &iter);
-       node = hashtable_iter_get_node(&iter);
+       node = lttng_ht_iter_get_node_ulong(&iter);
        if (node == NULL) {
                /* Only malloc can failed so something is really wrong */
                goto error_rcu_unlock;
        }
        ua_sess = caa_container_of(node, struct ust_app_session, node);
-       ret = hashtable_del(app->sessions, &iter);
+       ret = lttng_ht_del(app->sessions, &iter);
        assert(!ret);
        delete_ust_app_session(app->key.sock, ua_sess);
        obj.handle = ua_sess->handle;
@@ -2027,14 +2010,14 @@ error_rcu_unlock:
 int ust_app_start_trace_all(struct ltt_ust_session *usess)
 {
        int ret = 0;
-       struct cds_lfht_iter iter;
+       struct lttng_ht_iter iter;
        struct ust_app *app;
 
        DBG("Starting all UST traces");
 
        rcu_read_lock();
 
-       cds_lfht_for_each_entry(ust_app_ht, &iter, app, node) {
+       cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, node.node) {
                ret = ust_app_start_trace(usess, app);
                if (ret < 0) {
                        /* Continue to next apps even on error */
@@ -2053,14 +2036,14 @@ int ust_app_start_trace_all(struct ltt_ust_session *usess)
 int ust_app_stop_trace_all(struct ltt_ust_session *usess)
 {
        int ret = 0;
-       struct cds_lfht_iter iter;
+       struct lttng_ht_iter iter;
        struct ust_app *app;
 
        DBG("Stopping all UST traces");
 
        rcu_read_lock();
 
-       cds_lfht_for_each_entry(ust_app_ht, &iter, app, node) {
+       cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, node.node) {
                ret = ust_app_stop_trace(usess, app);
                if (ret < 0) {
                        /* Continue to next apps even on error */
@@ -2079,14 +2062,14 @@ int ust_app_stop_trace_all(struct ltt_ust_session *usess)
 int ust_app_destroy_trace_all(struct ltt_ust_session *usess)
 {
        int ret = 0;
-       struct cds_lfht_iter iter;
+       struct lttng_ht_iter iter;
        struct ust_app *app;
 
        DBG("Destroy all UST traces");
 
        rcu_read_lock();
 
-       cds_lfht_for_each_entry(ust_app_ht, &iter, app, node) {
+       cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, node.node) {
                ret = ust_app_destroy_trace(usess, app);
                if (ret < 0) {
                        /* Continue to next apps even on error */
@@ -2105,7 +2088,7 @@ int ust_app_destroy_trace_all(struct ltt_ust_session *usess)
 void ust_app_global_update(struct ltt_ust_session *usess, int sock)
 {
        int ret = 0;
-       struct cds_lfht_iter iter;
+       struct lttng_ht_iter iter, uiter;
        struct ust_app *app;
        struct ust_app_session *ua_sess;
        struct ust_app_channel *ua_chan;
@@ -2116,8 +2099,8 @@ void ust_app_global_update(struct ltt_ust_session *usess, int sock)
                goto error;
        }
 
-       DBG2("UST app global update for app sock %d for session uid %d", sock,
-                       usess->uid);
+       DBG2("UST app global update for app sock %d for session id %d", sock,
+                       usess->id);
 
        rcu_read_lock();
 
@@ -2137,7 +2120,8 @@ void ust_app_global_update(struct ltt_ust_session *usess, int sock)
         * app session above made a shadow copy of the UST global domain from the
         * ltt ust session.
         */
-       cds_lfht_for_each_entry(ua_sess->channels, &iter, ua_chan, node) {
+       cds_lfht_for_each_entry(ua_sess->channels->ht, &iter.iter, ua_chan,
+                       node.node) {
                ret = create_ust_channel(app, ua_sess, ua_chan);
                if (ret < 0) {
                        /* FIXME: Should we quit here or continue... */
@@ -2145,7 +2129,8 @@ void ust_app_global_update(struct ltt_ust_session *usess, int sock)
                }
 
                /* For each events */
-               cds_lfht_for_each_entry(ua_chan->events, &iter, ua_event, node) {
+               cds_lfht_for_each_entry(ua_chan->events->ht, &uiter.iter, ua_event,
+                               node.node) {
                        ret = create_ust_event(app, ua_sess, ua_chan, ua_event);
                        if (ret < 0) {
                                /* FIXME: Should we quit here or continue... */
@@ -2175,23 +2160,23 @@ int ust_app_add_ctx_channel_glb(struct ltt_ust_session *usess,
                struct ltt_ust_channel *uchan, struct ltt_ust_context *uctx)
 {
        int ret = 0;
-       struct cds_lfht_node *ua_chan_node;
-       struct cds_lfht_iter iter, uiter;
+       struct lttng_ht_node_str *ua_chan_node;
+       struct lttng_ht_iter iter, uiter;
        struct ust_app_channel *ua_chan = NULL;
        struct ust_app_session *ua_sess;
        struct ust_app *app;
 
        rcu_read_lock();
 
-       cds_lfht_for_each_entry(ust_app_ht, &iter, app, node) {
+       cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, node.node) {
                ua_sess = lookup_session_by_app(usess, app);
                if (ua_sess == NULL) {
                        continue;
                }
 
                /* Lookup channel in the ust app session */
-               ua_chan_node = hashtable_lookup(ua_sess->channels,
-                               (void *)uchan->name, strlen(uchan->name), &uiter);
+               lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter);
+               ua_chan_node = lttng_ht_iter_get_node_str(&uiter);
                if (ua_chan_node == NULL) {
                        continue;
                }
@@ -2216,8 +2201,8 @@ int ust_app_add_ctx_event_glb(struct ltt_ust_session *usess,
                struct ltt_ust_context *uctx)
 {
        int ret = 0;
-       struct cds_lfht_node *ua_chan_node, *ua_event_node;
-       struct cds_lfht_iter iter, uiter;
+       struct lttng_ht_node_str *ua_chan_node, *ua_event_node;
+       struct lttng_ht_iter iter, uiter;
        struct ust_app_session *ua_sess;
        struct ust_app_event *ua_event;
        struct ust_app_channel *ua_chan = NULL;
@@ -2225,23 +2210,23 @@ int ust_app_add_ctx_event_glb(struct ltt_ust_session *usess,
 
        rcu_read_lock();
 
-       cds_lfht_for_each_entry(ust_app_ht, &iter, app, node) {
+       cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, node.node) {
                ua_sess = lookup_session_by_app(usess, app);
                if (ua_sess == NULL) {
                        continue;
                }
 
                /* Lookup channel in the ust app session */
-               ua_chan_node = hashtable_lookup(ua_sess->channels,
-                               (void *)uchan->name, strlen(uchan->name), &uiter);
+               lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter);
+               ua_chan_node = lttng_ht_iter_get_node_str(&uiter);
                if (ua_chan_node == NULL) {
                        continue;
                }
                ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel,
                                node);
 
-               ua_event_node = hashtable_lookup(ua_chan->events,
-                               (void *)uevent->attr.name, strlen(uevent->attr.name), &uiter);
+               lttng_ht_lookup(ua_chan->events, (void *)uevent->attr.name, &uiter);
+               ua_event_node = lttng_ht_iter_get_node_str(&uiter);
                if (ua_event_node == NULL) {
                        continue;
                }
@@ -2265,8 +2250,8 @@ int ust_app_enable_event_pid(struct ltt_ust_session *usess,
                struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent, pid_t pid)
 {
        int ret = 0;
-       struct cds_lfht_iter iter;
-       struct cds_lfht_node *ua_chan_node, *ua_event_node;
+       struct lttng_ht_iter iter;
+       struct lttng_ht_node_str *ua_chan_node, *ua_event_node;
        struct ust_app *app;
        struct ust_app_session *ua_sess;
        struct ust_app_channel *ua_chan;
@@ -2288,15 +2273,15 @@ int ust_app_enable_event_pid(struct ltt_ust_session *usess,
        assert(ua_sess);
 
        /* Lookup channel in the ust app session */
-       ua_chan_node = hashtable_lookup(ua_sess->channels, (void *)uchan->name,
-                       strlen(uchan->name), &iter);
+       lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &iter);
+       ua_chan_node = lttng_ht_iter_get_node_str(&iter);
        /* If the channel is not found, there is a code flow error */
        assert(ua_chan_node);
 
        ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node);
 
-       ua_event_node = hashtable_lookup(ua_chan->events,
-                       (void*)uevent->attr.name, strlen(uevent->attr.name), &iter);
+       lttng_ht_lookup(ua_chan->events, (void *)uevent->attr.name, &iter);
+       ua_event_node = lttng_ht_iter_get_node_str(&iter);
        if (ua_event_node == NULL) {
                ret = create_ust_app_event(ua_sess, ua_chan, uevent, app);
                if (ret < 0) {
@@ -2323,8 +2308,8 @@ int ust_app_disable_event_pid(struct ltt_ust_session *usess,
                struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent, pid_t pid)
 {
        int ret = 0;
-       struct cds_lfht_iter iter;
-       struct cds_lfht_node *ua_chan_node, *ua_event_node;
+       struct lttng_ht_iter iter;
+       struct lttng_ht_node_str *ua_chan_node, *ua_event_node;
        struct ust_app *app;
        struct ust_app_session *ua_sess;
        struct ust_app_channel *ua_chan;
@@ -2346,16 +2331,16 @@ int ust_app_disable_event_pid(struct ltt_ust_session *usess,
        assert(ua_sess);
 
        /* Lookup channel in the ust app session */
-       ua_chan_node = hashtable_lookup(ua_sess->channels, (void *)uchan->name,
-                       strlen(uchan->name), &iter);
+       lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &iter);
+       ua_chan_node = lttng_ht_iter_get_node_str(&iter);
        if (ua_chan_node == NULL) {
                /* Channel does not exist, skip disabling */
                goto error;
        }
        ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node);
 
-       ua_event_node = hashtable_lookup(ua_chan->events,
-                       (void*)uevent->attr.name, strlen(uevent->attr.name), &iter);
+       lttng_ht_lookup(ua_chan->events, (void *)uevent->attr.name, &iter);
+       ua_event_node = lttng_ht_iter_get_node_str(&iter);
        if (ua_event_node == NULL) {
                /* Event does not exist, skip disabling */
                goto error;
index a5ae56a21398d1f7f6c482a3347acf0bc96da930..071ad0d90d0189c93ebfc3427a256cf820cd5e8d 100644 (file)
@@ -45,21 +45,21 @@ struct ust_register_msg {
 /*
  * Global applications HT used by the session daemon.
  */
-struct cds_lfht *ust_app_ht;
+struct lttng_ht *ust_app_ht;
 
-struct cds_lfht *ust_app_sock_key_map;
+struct lttng_ht *ust_app_sock_key_map;
 
 struct ust_app_key {
        pid_t pid;
        int sock;
-       struct cds_lfht_node node;
+       struct lttng_ht_node_ulong node;
 };
 
 struct ust_app_ctx {
        int handle;
        struct lttng_ust_context ctx;
        struct lttng_ust_object_data *obj;
-       struct cds_lfht_node node;
+       struct lttng_ht_node_ulong node;
 };
 
 struct ust_app_event {
@@ -68,8 +68,8 @@ struct ust_app_event {
        struct lttng_ust_object_data *obj;
        struct lttng_ust_event attr;
        char name[LTTNG_UST_SYM_NAME_LEN];
-       struct cds_lfht *ctx;
-       struct cds_lfht_node node;
+       struct lttng_ht *ctx;
+       struct lttng_ht_node_str node;
 };
 
 struct ust_app_channel {
@@ -79,21 +79,24 @@ struct ust_app_channel {
        struct lttng_ust_channel attr;
        struct lttng_ust_object_data *obj;
        struct ltt_ust_stream_list streams;
-       struct cds_lfht *ctx;
-       struct cds_lfht *events;
-       struct cds_lfht_node node;
+       struct lttng_ht *ctx;
+       struct lttng_ht *events;
+       struct lttng_ht_node_str node;
 };
 
 struct ust_app_session {
        int enabled;
        /* started: has the session been in started state at any time ? */
        int started;  /* allows detection of start vs restart. */
-       int handle;   /* Used has unique identifier */
-       unsigned int uid;
+       int handle;   /* used has unique identifier for app session */
+       int id;       /* session unique identifier */
        struct ltt_ust_metadata *metadata;
-       struct cds_lfht *channels; /* Registered channels */
-       struct cds_lfht_node node;
+       struct lttng_ht *channels; /* Registered channels */
+       struct lttng_ht_node_ulong node;
        char path[PATH_MAX];
+       /* UID/GID of the user owning the session */
+       uid_t uid;
+       gid_t gid;
 };
 
 /*
@@ -108,14 +111,19 @@ struct ust_app {
        uint32_t v_major;    /* Verion major number */
        uint32_t v_minor;    /* Verion minor number */
        char name[17];       /* Process name (short) */
-       struct cds_lfht *sessions;
-       struct cds_lfht_node node;
+       struct lttng_ht *sessions;
+       struct lttng_ht_node_ulong node;
        struct ust_app_key key;
 };
 
 #ifdef HAVE_LIBLTTNG_UST_CTL
 
 int ust_app_register(struct ust_register_msg *msg, int sock);
+static inline
+int ust_app_register_done(int sock)
+{
+       return ustctl_register_done(sock);
+}
 void ust_app_unregister(int sock);
 unsigned long ust_app_list_count(void);
 int ust_app_start_trace(struct ltt_ust_session *usess, struct ust_app *app);
@@ -156,7 +164,7 @@ void ust_app_global_update(struct ltt_ust_session *usess, int sock);
 
 void ust_app_clean_list(void);
 void ust_app_ht_alloc(void);
-struct cds_lfht *ust_app_get_ht(void);
+struct lttng_ht *ust_app_get_ht(void);
 struct ust_app *ust_app_find_by_pid(pid_t pid);
 
 #else /* HAVE_LIBLTTNG_UST_CTL */
@@ -192,6 +200,11 @@ int ust_app_register(struct ust_register_msg *msg, int sock)
        return -ENOSYS;
 }
 static inline
+int ust_app_register_done(int sock)
+{
+       return -ENOSYS;
+}
+static inline
 void ust_app_unregister(int sock)
 {
 }
@@ -223,7 +236,7 @@ struct ust_app *ust_app_get_by_pid(pid_t pid)
        return NULL;
 }
 static inline
-struct cds_lfht *ust_app_get_ht(void)
+struct lttng_ht *ust_app_get_ht(void)
 {
        return NULL;
 }
index 8a2ba728d76dc0b8a6e34c66a90819bc6a12e7f4..c02cbf96588c2c4701c7c9e9667c590582c1723e 100644 (file)
 #include <unistd.h>
 
 #include <lttngerr.h>
+#include <lttng-ht.h>
 #include <lttng-share.h>
 #include <lttng-sessiond-comm.h>
 #include <lttng/lttng-consumer.h>
 
-#include "hashtable.h"
 #include "ust-consumer.h"
 
 /*
  * Send all stream fds of UST channel to the consumer.
  */
 static int send_channel_streams(int sock,
-               struct ust_app_channel *uchan)
+               struct ust_app_channel *uchan,
+               uid_t uid, gid_t gid)
 {
        int ret, fd;
        struct lttcomm_consumer_msg lum;
@@ -84,6 +85,8 @@ static int send_channel_streams(int sock,
                 */
                lum.u.stream.output = DEFAULT_UST_CHANNEL_OUTPUT;
                lum.u.stream.mmap_len = stream->obj->memory_map_size;
+               lum.u.stream.uid = uid;
+               lum.u.stream.gid = gid;
                strncpy(lum.u.stream.path_name, stream->pathname, PATH_MAX - 1);
                lum.u.stream.path_name[PATH_MAX - 1] = '\0';
                DBG("Sending stream %d to consumer", lum.u.stream.stream_key);
@@ -117,10 +120,9 @@ int ust_consumer_send_session(int consumer_fd, struct ust_app_session *usess)
 {
        int ret = 0;
        int sock = consumer_fd;
-       struct cds_lfht_iter iter;
-       struct cds_lfht_node *node;
+       struct lttng_ht_iter iter;
        struct lttcomm_consumer_msg lum;
-       struct ust_app_channel *uchan;
+       struct ust_app_channel *ua_chan;
 
        DBG("Sending metadata stream fd");
 
@@ -158,6 +160,8 @@ int ust_consumer_send_session(int consumer_fd, struct ust_app_session *usess)
                lum.u.stream.state = LTTNG_CONSUMER_ACTIVE_STREAM;
                lum.u.stream.output = DEFAULT_UST_CHANNEL_OUTPUT;
                lum.u.stream.mmap_len = usess->metadata->stream_obj->memory_map_size;
+               lum.u.stream.uid = usess->uid;
+               lum.u.stream.gid = usess->gid;
                strncpy(lum.u.stream.path_name, usess->metadata->pathname, PATH_MAX - 1);
                lum.u.stream.path_name[PATH_MAX - 1] = '\0';
                DBG("Sending metadata stream %d to consumer", lum.u.stream.stream_key);
@@ -177,16 +181,13 @@ int ust_consumer_send_session(int consumer_fd, struct ust_app_session *usess)
 
        /* Send each channel fd streams of session */
        rcu_read_lock();
-       hashtable_get_first(usess->channels, &iter);
-       while ((node = hashtable_iter_get_node(&iter)) != NULL) {
-               uchan = caa_container_of(node, struct ust_app_channel, node);
-
-               ret = send_channel_streams(sock, uchan);
+       cds_lfht_for_each_entry(usess->channels->ht, &iter.iter, ua_chan,
+                       node.node) {
+               ret = send_channel_streams(sock, ua_chan, usess->uid, usess->gid);
                if (ret < 0) {
                        rcu_read_unlock();
                        goto error;
                }
-               hashtable_get_next(usess->channels, &iter);
        }
        rcu_read_unlock();
 
index 0da5642109fcc5ddf7eeced026a0d441cb685594..22a07ffaefd4174d7e2bb2cad1878eefd78d66b4 100644 (file)
@@ -22,8 +22,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
 #include <unistd.h>
 
 #include <lttngerr.h>
@@ -54,78 +52,3 @@ const char *get_home_dir(void)
 {
        return ((const char *) getenv("HOME"));
 }
-
-/*
- * Create recursively directory using the FULL path.
- */
-int mkdir_recursive(const char *path, mode_t mode, uid_t uid, gid_t gid)
-{
-       int ret;
-       char *p, tmp[PATH_MAX];
-       size_t len;
-       mode_t old_umask;
-
-       ret = snprintf(tmp, sizeof(tmp), "%s", path);
-       if (ret < 0) {
-               PERROR("snprintf mkdir");
-               goto error;
-       }
-
-       len = ret;
-       if (tmp[len - 1] == '/') {
-               tmp[len - 1] = 0;
-       }
-
-       old_umask = umask(0);
-       for (p = tmp + 1; *p; p++) {
-               if (*p == '/') {
-                       *p = 0;
-                       ret = mkdir(tmp, mode);
-                       if (ret < 0) {
-                               if (!(errno == EEXIST)) {
-                                       PERROR("mkdir recursive");
-                                       ret = -errno;
-                                       goto umask_error;
-                               }
-                       } else if (ret == 0) {
-                               /*
-                                * We created the directory. Set its ownership to the
-                                * user/group specified.
-                                */
-                               ret = chown(tmp, uid, gid);
-                               if (ret < 0) {
-                                       PERROR("chown in mkdir recursive");
-                                       ret = -errno;
-                                       goto umask_error;
-                               }
-                       }
-                       *p = '/';
-               }
-       }
-
-       ret = mkdir(tmp, mode);
-       if (ret < 0) {
-               if (!(errno == EEXIST)) {
-                       PERROR("mkdir recursive last piece");
-                       ret = -errno;
-               } else {
-                       ret = 0;
-               }
-       } else if (ret == 0) {
-               /*
-                * We created the directory. Set its ownership to the user/group
-                * specified.
-                */
-               ret = chown(tmp, uid, gid);
-               if (ret < 0) {
-                       PERROR("chown in mkdir recursive");
-                       ret = -errno;
-                       goto umask_error;
-               }
-       }
-
-umask_error:
-       umask(old_umask);
-error:
-       return ret;
-}
index 415e0135bb94e1bedb27bd202b81a2d9778400dd..7bcd1a87eb224f2eb654766f2f2b09bd6ea57510 100644 (file)
 #ifndef _LTT_UTILS_H
 #define _LTT_UTILS_H
 
-#include <unistd.h>
-
 #ifndef __stringify
 #define __stringify1(x)        #x
 #define __stringify(x) __stringify1(x)
 #endif
 
-int mkdir_recursive(const char *path, mode_t mode, uid_t uid, gid_t gid);
 const char *get_home_dir(void);
 int notify_thread_pipe(int wpipe);
 
index f64ca5291ac0efdd7fe18ed45ccf13c49b0b7957..d775644c56913aadbdb573a043fbe0d3c717c8d9 100644 (file)
@@ -7,13 +7,13 @@ noinst_PROGRAMS = test_sessions test_kernel_data_trace kernel_all_events_basic \
                                  kernel_event_basic
 
 UTILS=utils.h
-SESSIONS=$(top_srcdir)/lttng-sessiond/session.c $(top_srcdir)/lttng-sessiond/hashtable.c \
-                $(top_srcdir)/hashtable/rculfhash.c $(top_srcdir)/hashtable/hash.c
+SESSIONS=$(top_srcdir)/lttng-sessiond/session.c
 KERN_DATA_TRACE=$(top_srcdir)/lttng-sessiond/trace-kernel.c
 LIBLTTNG=$(top_srcdir)/liblttngctl/lttngctl.c \
                 $(top_srcdir)/liblttng-sessiond-comm/lttng-sessiond-comm.c
 
 test_sessions_SOURCES = test_sessions.c $(UTILS) $(SESSIONS)
+test_sessions_LDADD = $(top_builddir)/common/libcommon.la
 
 test_kernel_data_trace_SOURCES = test_kernel_data_trace.c $(UTILS) $(KERN_DATA_TRACE)
 
index 3e45c21b669563846e41dbfd0e624496cf95b9df..5b17e63911eeb8c45fdd05723978d79eae209313 100644 (file)
@@ -24,6 +24,7 @@
 #include <string.h>
 #include <unistd.h>
 #include <time.h>
+#include <sys/types.h>
 
 #include <lttng-sessiond-comm.h>
 
@@ -118,7 +119,7 @@ static int create_one_session(char *name, char *path)
 {
        int ret;
 
-       ret = session_create(name, path);
+       ret = session_create(name, path, geteuid(), getegid());
        if (ret == LTTCOMM_OK) {
                /* Validate */
                ret = find_session_name(name);
@@ -251,6 +252,7 @@ int main(int argc, char **argv)
        }
 
        printf("Create 1 session %s: ", SESSION1);
+       fflush(stdout);
        ret = create_one_session(SESSION1, PATH1);
        if (ret < 0) {
                return -1;
@@ -258,6 +260,7 @@ int main(int argc, char **argv)
        PRINT_OK();
 
        printf("Validating created session %s: ", SESSION1);
+       fflush(stdout);
        tmp = session_find_by_name(SESSION1);
        if (tmp == NULL) {
                return -1;
@@ -272,6 +275,7 @@ int main(int argc, char **argv)
        PRINT_OK();
 
        printf("Destroy 1 session %s: ", SESSION1);
+       fflush(stdout);
        ret = destroy_one_session(tmp);
        if (ret < 0) {
                return -1;
@@ -279,6 +283,7 @@ int main(int argc, char **argv)
        PRINT_OK();
 
        printf("Two session with same name: ");
+       fflush(stdout);
        ret = two_session_same_name();
        if (ret < 0) {
                return -1;
@@ -288,6 +293,7 @@ int main(int argc, char **argv)
        empty_session_list();
 
        printf("Fuzzing create_session arguments: ");
+       fflush(stdout);
        ret = fuzzing_create_args();
        if (ret < 0) {
                return -1;
@@ -295,6 +301,7 @@ int main(int argc, char **argv)
        PRINT_OK();
 
        printf("Fuzzing destroy_session argument: ");
+       fflush(stdout);
        ret = fuzzing_destroy_args();
        if (ret < 0) {
                return -1;
@@ -302,6 +309,7 @@ int main(int argc, char **argv)
        PRINT_OK();
 
        printf("Creating %d sessions: ", MAX_SESSIONS);
+       fflush(stdout);
        for (i = 0; i < MAX_SESSIONS; i++) {
                tmp_name = get_random_string();
                ret = create_one_session(tmp_name, PATH1);
@@ -314,6 +322,7 @@ int main(int argc, char **argv)
        PRINT_OK();
 
        printf("Destroying %d sessions: ", MAX_SESSIONS);
+       fflush(stdout);
        for (i = 0; i < MAX_SESSIONS; i++) {
                cds_list_for_each_entry_safe(iter, tmp, &session_list->head, list) {
                        ret = destroy_one_session(iter);
This page took 0.22195 seconds and 4 git commands to generate.