uuidd: Implement continuous clock handling for time based UUIDs

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



In a uuidd setup, the daemon is a singleton and can maintain it's own
resources for time based UUID generation. This requires a dedicated
'clock sequence range' but does not need any further lock/update of
the LIBUUID_CLOCK_FILE from uuidd. The range of available clock values
is extended by a continuous clock update - instead of updating the
value to the current timestamp, it is incremented by the number of
requested UUIDs.
---

Here is the first implementation of a continuous clock for time based
UUIDs. Of course, there are pros + cons to have the current time in
the timestamp - which needs be discussed, but in some use cases it's
not relevant at all.
The feature has to be enabled with '-C' and can be checked with

# uuidgen -t
d1ff28e1-c21a-11ec-8000-f80f41fc650a
# uuidgen -t
d1ff28eb-c21a-11ec-8000-f80f41fc650a

which shows an increment of 10 UUIDs for each request (due to the
cache in libuuid).


 libuuid/src/gen_uuid.c  | 84 ++++++++++++++++++++++++++++++++++++++---
 libuuid/src/libuuid.sym |  1 +
 libuuid/src/uuidd.h     |  1 +
 misc-utils/uuidd.8.adoc |  3 ++
 misc-utils/uuidd.c      | 32 +++++++++++++---
 5 files changed, 111 insertions(+), 10 deletions(-)

diff --git a/libuuid/src/gen_uuid.c b/libuuid/src/gen_uuid.c
index 805b40d90..cb6ceb50e 100644
--- a/libuuid/src/gen_uuid.c
+++ b/libuuid/src/gen_uuid.c
@@ -209,6 +209,8 @@ static int get_node_id(unsigned char *node_id)
 
 /* Assume that the gettimeofday() has microsecond granularity */
 #define MAX_ADJUSTMENT 10
+/* Reserve a clock_seq value for the 'continuous clock' implementation */
+#define CLOCK_SEQ_CONT 0
 
 /*
  * Get clock from global sequence clock counter.
@@ -275,8 +277,10 @@ static int get_clock(uint32_t *clock_high, uint32_t *clock_low,
 	}
 
 	if ((last.tv_sec == 0) && (last.tv_usec == 0)) {
-		ul_random_get_bytes(&clock_seq, sizeof(clock_seq));
-		clock_seq &= 0x3FFF;
+		do {
+			ul_random_get_bytes(&clock_seq, sizeof(clock_seq));
+			clock_seq &= 0x3FFF;
+		} while (clock_seq == CLOCK_SEQ_CONT);
 		gettimeofday(&last, NULL);
 		last.tv_sec--;
 	}
@@ -286,7 +290,9 @@ try_again:
 	if ((tv.tv_sec < last.tv_sec) ||
 	    ((tv.tv_sec == last.tv_sec) &&
 	     (tv.tv_usec < last.tv_usec))) {
-		clock_seq = (clock_seq+1) & 0x3FFF;
+		do {
+			clock_seq = (clock_seq+1) & 0x3FFF;
+		} while (clock_seq == CLOCK_SEQ_CONT);
 		adjustment = 0;
 		last = tv;
 	} else if ((tv.tv_sec == last.tv_sec) &&
@@ -331,6 +337,56 @@ try_again:
 	return ret;
 }
 
+/*
+ * Get current time in 100ns ticks.
+ */
+static uint64_t get_clock_counter(void)
+{
+	struct timeval tv;
+	uint64_t clock_reg;
+
+	gettimeofday(&tv, NULL);
+	clock_reg = tv.tv_usec*10;
+	clock_reg += ((uint64_t) tv.tv_sec)*10000000;
+
+	return clock_reg;
+}
+
+/*
+ * Get continuous clock value.
+ *
+ * Return -1 if there is no further clock counter available,
+ * otherwise return 0.
+ *
+ * This implementation doesn't deliver clock counters based on
+ * the current time because the last clock counter is only
+ * incremented by the number of requested UUIDs.
+ */
+static int get_clock_cont(uint32_t *clock_high,
+			    uint32_t *clock_low,
+			    int num)
+{
+	/* 100ns based time offset according to RFC 4122. 4.1.4. */
+	const uint64_t reg_offset = (((uint64_t) 0x01B21DD2) << 32) + 0x13814000;
+	THREAD_LOCAL uint64_t last_clock_reg = 0;
+	uint64_t clock_reg;
+
+	if (last_clock_reg == 0)
+		last_clock_reg = get_clock_counter();
+
+	clock_reg = get_clock_counter();
+	clock_reg += MAX_ADJUSTMENT;
+
+	if ((last_clock_reg + num) >= clock_reg)
+		return -1;
+
+	*clock_high = (last_clock_reg + reg_offset) >> 32;
+	*clock_low = last_clock_reg + reg_offset;
+	last_clock_reg += num;
+
+	return 0;
+}
+
 #if defined(HAVE_UUIDD) && defined(HAVE_SYS_UN_H)
 
 /*
@@ -403,7 +459,8 @@ static int get_uuid_via_daemon(int op __attribute__((__unused__)),
 }
 #endif
 
-int __uuid_generate_time(uuid_t out, int *num)
+static int __uuid_generate_time_internal(uuid_t out, int *num,
+					 int cont)
 {
 	static unsigned char node_id[6];
 	static int has_init = 0;
@@ -423,7 +480,14 @@ int __uuid_generate_time(uuid_t out, int *num)
 		}
 		has_init = 1;
 	}
-	ret = get_clock(&clock_mid, &uu.time_low, &uu.clock_seq, num);
+	if (cont) {
+		ret = get_clock_cont(&clock_mid, &uu.time_low, *num);
+		uu.clock_seq = CLOCK_SEQ_CONT;
+		if (ret != 0)	/* falback to previous implpementation */
+			ret = get_clock(&clock_mid, &uu.time_low, &uu.clock_seq, num);
+	} else {
+		ret = get_clock(&clock_mid, &uu.time_low, &uu.clock_seq, num);
+	}
 	uu.clock_seq |= 0x8000;
 	uu.time_mid = (uint16_t) clock_mid;
 	uu.time_hi_and_version = ((clock_mid >> 16) & 0x0FFF) | 0x1000;
@@ -432,6 +496,16 @@ int __uuid_generate_time(uuid_t out, int *num)
 	return ret;
 }
 
+int __uuid_generate_time(uuid_t out, int *num)
+{
+	return __uuid_generate_time_internal(out, num, 0);
+}
+
+int __uuid_generate_time_cont(uuid_t out, int *num)
+{
+	return __uuid_generate_time_internal(out, num, 1);
+}
+
 /*
  * Generate time-based UUID and store it to @out
  *
diff --git a/libuuid/src/libuuid.sym b/libuuid/src/libuuid.sym
index 342453368..96372a857 100644
--- a/libuuid/src/libuuid.sym
+++ b/libuuid/src/libuuid.sym
@@ -60,6 +60,7 @@ global:
 UUIDD_PRIVATE {
 global:
 	__uuid_generate_time;
+	__uuid_generate_time_cont;
 	__uuid_generate_random;
 local:
 	*;
diff --git a/libuuid/src/uuidd.h b/libuuid/src/uuidd.h
index fbe821ff3..1557bb6ca 100644
--- a/libuuid/src/uuidd.h
+++ b/libuuid/src/uuidd.h
@@ -49,6 +49,7 @@
 #define UUIDD_MAX_OP			UUIDD_OP_BULK_RANDOM_UUID
 
 extern int __uuid_generate_time(uuid_t out, int *num);
+extern int __uuid_generate_time_cont(uuid_t out, int *num);
 extern int __uuid_generate_random(uuid_t out, int *num);
 
 #endif /* _UUID_UUID_H */
diff --git a/misc-utils/uuidd.8.adoc b/misc-utils/uuidd.8.adoc
index 49e7b6359..07c01c20b 100644
--- a/misc-utils/uuidd.8.adoc
+++ b/misc-utils/uuidd.8.adoc
@@ -24,6 +24,9 @@ The *uuidd* daemon is used by the UUID library to generate universally unique id
 
 == OPTIONS
 
+*-C*, *--cont-clock*::
+Activate continuous clock handling for time based UUIDs. *uuidd* uses all possible clock values, beginning with the daemon's start time.
+
 *-d*, *--debug*::
 Run *uuidd* in debugging mode. This prevents *uuidd* from running as a daemon.
 
diff --git a/misc-utils/uuidd.c b/misc-utils/uuidd.c
index dfcd1487b..51e4e78a4 100644
--- a/misc-utils/uuidd.c
+++ b/misc-utils/uuidd.c
@@ -75,7 +75,8 @@ struct uuidd_cxt_t {
 	unsigned int	debug: 1,
 			quiet: 1,
 			no_fork: 1,
-			no_sock: 1;
+			no_sock: 1,
+			cont_clock:1;
 };
 
 struct uuidd_options_t {
@@ -106,6 +107,7 @@ static void __attribute__((__noreturn__)) usage(void)
 	fputs(_(" -P, --no-pid            do not create pid file\n"), out);
 	fputs(_(" -F, --no-fork           do not daemonize using double-fork\n"), out);
 	fputs(_(" -S, --socket-activation do not create listening socket\n"), out);
+	fputs(_(" -C, --cont-clock        activate continuous clock handling\n"), out);
 	fputs(_(" -d, --debug             run in debugging mode\n"), out);
 	fputs(_(" -q, --quiet             turn on quiet mode\n"), out);
 	fputs(USAGE_SEPARATOR, out);
@@ -438,6 +440,10 @@ static void server_loop(const char *socket_path, const char *pidfile_path,
 	pfd[POLLFD_SOCKET].fd = s;
 	pfd[POLLFD_SIGNAL].events = pfd[POLLFD_SOCKET].events = POLLIN | POLLERR | POLLHUP;
 
+	num = 1;
+	/* trigger initialization */
+	(void) __uuid_generate_time_cont(uu, &num);
+
 	while (1) {
 		ret = poll(pfd, ARRAY_SIZE(pfd),
 				uuidd_cxt->timeout ?
@@ -494,7 +500,11 @@ static void server_loop(const char *socket_path, const char *pidfile_path,
 			break;
 		case UUIDD_OP_TIME_UUID:
 			num = 1;
-			if (__uuid_generate_time(uu, &num) < 0 && !uuidd_cxt->quiet)
+			if (uuidd_cxt->cont_clock)
+				ret = __uuid_generate_time_cont(uu, &num);
+			else
+				ret = __uuid_generate_time(uu, &num);
+			if (ret < 0 && !uuidd_cxt->quiet)
 				warnx(_("failed to open/lock clock counter"));
 			if (uuidd_cxt->debug) {
 				uuid_unparse(uu, str);
@@ -505,7 +515,11 @@ static void server_loop(const char *socket_path, const char *pidfile_path,
 			break;
 		case UUIDD_OP_RANDOM_UUID:
 			num = 1;
-			if (__uuid_generate_time(uu, &num) < 0 && !uuidd_cxt->quiet)
+			if (uuidd_cxt->cont_clock)
+				ret = __uuid_generate_time_cont(uu, &num);
+			else
+				ret = __uuid_generate_time(uu, &num);
+			if (ret < 0 && !uuidd_cxt->quiet)
 				warnx(_("failed to open/lock clock counter"));
 			if (uuidd_cxt->debug) {
 				uuid_unparse(uu, str);
@@ -515,7 +529,11 @@ static void server_loop(const char *socket_path, const char *pidfile_path,
 			reply_len = sizeof(uu);
 			break;
 		case UUIDD_OP_BULK_TIME_UUID:
-			if (__uuid_generate_time(uu, &num) < 0 && !uuidd_cxt->quiet)
+			if (uuidd_cxt->cont_clock)
+				ret = __uuid_generate_time_cont(uu, &num);
+			else
+				ret = __uuid_generate_time(uu, &num);
+			if (ret < 0 && !uuidd_cxt->quiet)
 				warnx(_("failed to open/lock clock counter"));
 			if (uuidd_cxt->debug) {
 				uuid_unparse(uu, str);
@@ -581,6 +599,7 @@ static void parse_options(int argc, char **argv, struct uuidd_cxt_t *uuidd_cxt,
 		{"no-pid", no_argument, NULL, 'P'},
 		{"no-fork", no_argument, NULL, 'F'},
 		{"socket-activation", no_argument, NULL, 'S'},
+		{"cont-clock", no_argument, NULL, 'c'},
 		{"debug", no_argument, NULL, 'd'},
 		{"quiet", no_argument, NULL, 'q'},
 		{"version", no_argument, NULL, 'V'},
@@ -596,9 +615,12 @@ static void parse_options(int argc, char **argv, struct uuidd_cxt_t *uuidd_cxt,
 	int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
 	int c;
 
-	while ((c = getopt_long(argc, argv, "p:s:T:krtn:PFSdqVh", longopts, NULL)) != -1) {
+	while ((c = getopt_long(argc, argv, "Cp:s:T:krtn:PFSdqVh", longopts, NULL)) != -1) {
 		err_exclusive_options(c, longopts, excl, excl_st);
 		switch (c) {
+		case 'C':
+			uuidd_cxt->cont_clock = 1;
+			break;
 		case 'd':
 			uuidd_cxt->debug = 1;
 			break;
-- 
2.32.0 (Apple Git-132)




[Index of Archives]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux