| /* Copyright (c) 2016 Google Inc. | 
 |  * Barret Rhoden <brho@cs.berkeley.edu> | 
 |  * See LICENSE for details. | 
 |  * | 
 |  * Implementation of glibc's timerfd interface on top of #alarm. | 
 |  * | 
 |  * Like sockets, timerfd is really an alarm directory under the hood, but the | 
 |  * user gets a single FD that they will read and epoll on.  This FD will be for | 
 |  * the 'count' file.  Other operations will require opening other FDs, given the | 
 |  * 'count' fd.  This is basically the Rock lookup problem. */ | 
 |  | 
 | #include <sys/timerfd.h> | 
 | #include <sys/types.h> | 
 | #include <sys/stat.h> | 
 | #include <fcntl.h> | 
 | #include <stdio.h> | 
 | #include <stdlib.h> | 
 | #include <unistd.h> | 
 | #include <syscall.h> | 
 | #include <string.h> | 
 | #include <parlib/timing.h> | 
 | #include <time.h> | 
 | #include <sys/plan9_helpers.h> | 
 |  | 
 | int timerfd_create(int clockid, int flags) | 
 | { | 
 | 	int ctlfd, countfd, ret; | 
 | 	char id[20]; | 
 | 	char path[MAX_PATH_LEN]; | 
 | 	int count_oflags = O_RDWR; | 
 |  | 
 | 	ctlfd = open("#alarm/clone", O_RDWR); | 
 | 	if (ctlfd < 0) | 
 | 		return -1; | 
 | 	/* TODO: if we want to support clocks like CLOCK_REALTIME, do it here, | 
 | 	 * either at attach time (with a .spec), or with a ctl message. */ | 
 | 	/* fd2path doesn't work on cloned files, so open the count manually. */ | 
 | 	ret = read(ctlfd, id, sizeof(id) - 1); | 
 | 	if (ret <= 0) | 
 | 		return -1; | 
 | 	id[ret] = 0; | 
 | 	snprintf(path, sizeof(path), "#alarm/a%s/count", id); | 
 | 	count_oflags |= (flags & TFD_NONBLOCK ? O_NONBLOCK : 0); | 
 | 	count_oflags |= (flags & TFD_CLOEXEC ? O_CLOEXEC : 0); | 
 | 	countfd = open(path, count_oflags); | 
 | 	close(ctlfd); | 
 | 	return countfd; | 
 | } | 
 |  | 
 | static int set_period(int periodfd, uint64_t period) | 
 | { | 
 | 	return write_hex_to_fd(periodfd, period); | 
 | } | 
 |  | 
 | static int set_timer(int timerfd, uint64_t abs_ticks) | 
 | { | 
 | 	return write_hex_to_fd(timerfd, abs_ticks); | 
 | } | 
 |  | 
 | static uint64_t timespec2tsc(const struct timespec *ts) | 
 | { | 
 | 	return nsec2tsc(ts->tv_sec * 1000000000ULL + ts->tv_nsec); | 
 | } | 
 |  | 
 | static void tsc2timespec(uint64_t tsc, struct timespec *ts) | 
 | { | 
 | 	uint64_t nsec = tsc2nsec(tsc); | 
 |  | 
 | 	ts->tv_sec = nsec / 1000000000; | 
 | 	ts->tv_nsec = nsec % 1000000000; | 
 | } | 
 |  | 
 | static int __timerfd_gettime(int timerfd, int periodfd, | 
 |                              struct itimerspec *curr_value) | 
 | { | 
 | 	char buf[20]; | 
 | 	uint64_t timer_tsc, now_tsc, period_tsc; | 
 |  | 
 | 	if (read(periodfd, buf, sizeof(buf) <= 0)) | 
 | 		return -1; | 
 | 	period_tsc = strtoul(buf, 0, 0); | 
 | 	tsc2timespec(period_tsc, &curr_value->it_interval); | 
 | 	if (read(timerfd, buf, sizeof(buf) <= 0)) | 
 | 		return -1; | 
 | 	timer_tsc = strtoul(buf, 0, 0); | 
 | 	/* If 0 (disabled), we'll return 0 for 'it_value'.  o/w we need to | 
 | 	 * return the relative time. */ | 
 | 	if (timer_tsc) { | 
 | 		now_tsc = read_tsc(); | 
 | 		if (timer_tsc > now_tsc) { | 
 | 			timer_tsc -= now_tsc; | 
 | 		} else { | 
 | 			/* it's possible that timer_tsc is in the past, and that | 
 | 			 * we lost the race.  The alarm fired since we looked at | 
 | 			 * it, and it might be disabled.  It might have fired | 
 | 			 * multiple times too. */ | 
 | 			if (!period_tsc) { | 
 | 				/* if there was no period and the alarm fired, | 
 | 				 * then it should be disabled.  This is racy, if | 
 | 				 * there are other people setting the timer. */ | 
 | 				timer_tsc = 0; | 
 | 			} else { | 
 | 				while (timer_tsc < now_tsc) | 
 | 					timer_tsc += period_tsc; | 
 | 			} | 
 | 		} | 
 | 	} | 
 | 	tsc2timespec(timer_tsc, &curr_value->it_value); | 
 | 	return 0; | 
 | } | 
 |  | 
 | int timerfd_settime(int fd, int flags, | 
 |                     const struct itimerspec *new_value, | 
 |                     struct itimerspec *old_value) | 
 | { | 
 | 	int timerfd, periodfd; | 
 | 	int ret; | 
 | 	uint64_t period; | 
 | 	struct timespec now_timespec = {0}; | 
 | 	struct timespec rel_timespec; | 
 |  | 
 | 	timerfd = get_sibling_fd(fd, "timer"); | 
 | 	if (timerfd < 0) | 
 | 		return -1; | 
 | 	periodfd = get_sibling_fd(fd, "period"); | 
 | 	if (periodfd < 0) { | 
 | 		close(timerfd); | 
 | 		return -1; | 
 | 	} | 
 | 	if (old_value) { | 
 | 		if (__timerfd_gettime(timerfd, periodfd, old_value)) { | 
 | 			ret = -1; | 
 | 			goto out; | 
 | 		} | 
 | 	} | 
 | 	if (!new_value->it_value.tv_sec && !new_value->it_value.tv_nsec) { | 
 | 		ret = set_timer(timerfd, 0); | 
 | 		goto out; | 
 | 	} | 
 | 	period = timespec2tsc(&new_value->it_interval); | 
 | 	ret = set_period(periodfd, period); | 
 | 	if (ret < 0) | 
 | 		goto out; | 
 | 	/* So the caller is asking for timespecs in wall-clock time (depending | 
 | 	 * on the clock, actually, (TODO)), and the kernel expects TSC ticks | 
 | 	 * from boot.  If !ABSTIME, then it's just relative to now.  If it is | 
 | 	 * ABSTIME, then they are asking in terms of real-world time, which | 
 | 	 * means ABS - NOW to get the rel time, then convert to tsc ticks. */ | 
 | 	if (flags & TFD_TIMER_ABSTIME) { | 
 | 		ret = clock_gettime(CLOCK_MONOTONIC, &now_timespec); | 
 | 		if (ret < 0) | 
 | 			goto out; | 
 | 		subtract_timespecs(&rel_timespec, &new_value->it_value, | 
 | 				   &now_timespec); | 
 | 	} else { | 
 | 		rel_timespec = new_value->it_value; | 
 | 	} | 
 | 	ret = set_timer(timerfd, timespec2tsc(&rel_timespec) + read_tsc()); | 
 | 	/* fall-through */ | 
 | out: | 
 | 	close(timerfd); | 
 | 	close(periodfd); | 
 | 	return ret; | 
 | } | 
 |  | 
 | int timerfd_gettime(int fd, struct itimerspec *curr_value) | 
 | { | 
 | 	int timerfd, periodfd; | 
 | 	int ret; | 
 |  | 
 | 	timerfd = get_sibling_fd(fd, "timer"); | 
 | 	if (timerfd < 0) | 
 | 		return -1; | 
 | 	periodfd = get_sibling_fd(fd, "period"); | 
 | 	if (periodfd < 0) { | 
 | 		close(timerfd); | 
 | 		return -1; | 
 | 	} | 
 | 	ret = __timerfd_gettime(timerfd, periodfd, curr_value); | 
 | 	close(timerfd); | 
 | 	close(periodfd); | 
 | 	return ret; | 
 | } |