/* Copyright (C) 2014 Stony Brook University This file is part of Graphene Library OS. Graphene Library OS 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 3 of the License, or (at your option) any later version. Graphene Library OS 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 program. If not, see . */ /* * shim_alarm.c * * Implementation of system call "alarm", "setitmer" and "getitimer". */ #include #include #include #include #include static void signal_alarm(IDTYPE target, void* arg) { // Kept for API compatibility wtih signal_itimer __UNUSED(arg); debug("alarm goes off, signaling thread %u\n", target); struct shim_thread* thread = lookup_thread(target); if (!thread) return; lock(&thread->lock); append_signal(thread, SIGALRM, NULL, true); unlock(&thread->lock); } int shim_do_alarm(unsigned int seconds) { uint64_t usecs = 1000000ULL * seconds; int64_t ret = install_async_event(NULL, usecs, &signal_alarm, NULL); if (ret < 0) return ret; uint64_t usecs_left = (uint64_t)ret; int secs = usecs_left / 1000000ULL; if (usecs_left % 1000000ULL) secs++; return secs; } static struct { unsigned long timeout; unsigned long reset; } real_itimer; void signal_itimer(IDTYPE target, void* arg) { // XXX: Can we simplify this code or streamline with the other callback? __UNUSED(target); MASTER_LOCK(); if (real_itimer.timeout != (unsigned long)arg) { MASTER_UNLOCK(); return; } real_itimer.timeout += real_itimer.reset; real_itimer.reset = 0; MASTER_UNLOCK(); } #ifndef ITIMER_REAL #define ITIMER_REAL 0 #endif int shim_do_setitimer(int which, struct __kernel_itimerval* value, struct __kernel_itimerval* ovalue) { if (which != ITIMER_REAL) return -ENOSYS; if (!value) return -EFAULT; if (test_user_memory(value, sizeof(*value), false)) return -EFAULT; if (ovalue && test_user_memory(ovalue, sizeof(*ovalue), true)) return -EFAULT; unsigned long setup_time = DkSystemTimeQuery(); unsigned long next_value = value->it_value.tv_sec * 1000000 + value->it_value.tv_usec; unsigned long next_reset = value->it_interval.tv_sec * 1000000 + value->it_interval.tv_usec; MASTER_LOCK(); unsigned long current_timeout = real_itimer.timeout > setup_time ? real_itimer.timeout - setup_time : 0; unsigned long current_reset = real_itimer.reset; int64_t ret = install_async_event(NULL, next_value, &signal_itimer, (void*)(setup_time + next_value)); if (ret < 0) { MASTER_UNLOCK(); return ret; } real_itimer.timeout = setup_time + next_value; real_itimer.reset = next_reset; MASTER_UNLOCK(); if (ovalue) { ovalue->it_interval.tv_sec = current_reset / 1000000; ovalue->it_interval.tv_usec = current_reset % 1000000; ovalue->it_value.tv_sec = current_timeout / 1000000; ovalue->it_value.tv_usec = current_timeout % 1000000; } return 0; } int shim_do_getitimer(int which, struct __kernel_itimerval* value) { if (which != ITIMER_REAL) return -ENOSYS; if (!value) return -EFAULT; if (test_user_memory(value, sizeof(*value), true)) return -EFAULT; unsigned long setup_time = DkSystemTimeQuery(); MASTER_LOCK(); unsigned long current_timeout = real_itimer.timeout > setup_time ? real_itimer.timeout - setup_time : 0; unsigned long current_reset = real_itimer.reset; MASTER_UNLOCK(); value->it_interval.tv_sec = current_reset / 1000000; value->it_interval.tv_usec = current_reset % 1000000; value->it_value.tv_sec = current_timeout / 1000000; value->it_value.tv_usec = current_timeout % 1000000; return 0; }