From 3ea25890c18014af29f29022af58cae61ef860eb Mon Sep 17 00:00:00 2001 From: Alexander Mikhalitsyn Date: Wed, 12 Apr 2023 20:29:38 +0200 Subject: [PATCH 1/3] lxcfs: preparation for FUSE_INTERRUPT support This commit prepares lxcfs for FUSE_INTERRUPT support by enabling interrupt on libfuse side and setting dummy signal handler with SA_RESTART flag. SA_RESTART is very imporant there, otherwise we can break something accidentally. This commit has no user-visible effects. Signed-off-by: Alexander Mikhalitsyn --- src/bindings.h | 14 ++++++++++++++ src/lxcfs.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/src/bindings.h b/src/bindings.h index 8d9d6eb1..e2463bac 100644 --- a/src/bindings.h +++ b/src/bindings.h @@ -78,6 +78,20 @@ enum lxcfs_virt_t { #define LXCFS_TYPE_SYS(type) (type >= LXC_TYPE_SYS && type <= LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_ONLINE) #define LXCFS_TYPE_OK(type) (type >= LXC_TYPE_CGDIR && type < LXC_TYPE_MAX) +/* + * This signal will be used to signal fuse request processing thread that + * request was interrupted (FUSE_INTERRUPT came from the kernel). + * + * It's not imporant which signal num is used, but it should not intersect with + * any signals those are already handled and used somewhere. + * Since, SIGUSR1 and SIGUSR2 are already utilized by lxcfs, let it be SIGTTOU. + * + * See also: + * ("interrupt support") + * https://github.com/libfuse/libfuse/commit/288ed4ebcea335c77793ee3d207c7466d55c4f71 + */ +#define LXCFS_INTR_SIGNAL SIGTTOU + struct file_info { char *controller; char *cgroup; diff --git a/src/lxcfs.c b/src/lxcfs.c index 92ed9912..ef35460d 100644 --- a/src/lxcfs.c +++ b/src/lxcfs.c @@ -1051,6 +1051,42 @@ int lxcfs_chmod(const char *path, mode_t mode) return -ENOENT; } +#if HAVE_FUSE3 +static void fuse_intr_sighandler(int sig) +{ + (void) sig; + /* Nothing to do */ +} + +static int fuse_init_intr_signal(int signum) +{ + struct sigaction old_sa; + struct sigaction sa; + + if (sigaction(signum, NULL, &old_sa) == -1) + return log_error(-1, "cannot get old signal handler\n"); + + if (old_sa.sa_handler != SIG_DFL) + return log_error(-1, "%d has non-default handler\n", signum); + + memset(&sa, 0, sizeof(struct sigaction)); + + /* + * We *must* enable SA_RESTART, otherwise we may accidentally + * break some code which is not ready to signals/fuse interrupt. + */ + sa.sa_flags = SA_RESTART; + + sa.sa_handler = fuse_intr_sighandler; + sigemptyset(&sa.sa_mask); + + if (sigaction(signum, &sa, NULL) == -1) + return log_error(-1, "cannot set interrupt signal handler\n"); + + return 0; +} +#endif + #if HAVE_FUSE3 static void *lxcfs_init(struct fuse_conn_info *conn, struct fuse_config *cfg) #else @@ -1062,6 +1098,8 @@ static void *lxcfs_init(struct fuse_conn_info *conn) #if HAVE_FUSE3 cfg->direct_io = 1; + cfg->intr = 1; + cfg->intr_signal = LXCFS_INTR_SIGNAL; #endif return fuse_get_context()->private_data; @@ -1402,6 +1440,13 @@ int main(int argc, char *argv[]) goto out; } +#if HAVE_FUSE3 + if (fuse_init_intr_signal(LXCFS_INTR_SIGNAL)) { + lxcfs_error("Failed to install fuse interrupt signal handler"); + goto out; + } +#endif + if (!pidfile) { snprintf(pidfile_buf, sizeof(pidfile_buf), "%s/lxcfs.pid", RUNTIME_PATH); pidfile = pidfile_buf; From 913c12e333bc3e6c24be6c5f13fe153bb7b3f958 Mon Sep 17 00:00:00 2001 From: Alexander Mikhalitsyn Date: Wed, 12 Apr 2023 23:38:01 +0200 Subject: [PATCH 2/3] lxcfs: add fuse interruptible locks Adds a few helper functions which represents fuse interruptible versions of a classical pthread locking primitives: extern int mutex_lock_interruptible(pthread_mutex_t *l); extern int rwlock_rdlock_interruptible(pthread_rwlock_t *l); extern int rwlock_wrlock_interruptible(pthread_rwlock_t *l); Does not change behavior. Signed-off-by: Alexander Mikhalitsyn --- src/bindings.c | 19 +++++++++++++++++++ src/bindings.h | 4 ++++ 2 files changed, 23 insertions(+) diff --git a/src/bindings.c b/src/bindings.c index 13259c1b..27c08c38 100644 --- a/src/bindings.c +++ b/src/bindings.c @@ -149,6 +149,25 @@ static inline void store_unlock(void) mutex_unlock(&pidns_store_mutex); } +#define define_interruptible_lock(type, lockname, lockfn) \ + int lockname##_interruptible(type *l) \ + { \ + int ret = ETIMEDOUT; \ + while (!fuse_interrupted() && (ret == ETIMEDOUT)) { \ + struct timespec deadline; \ + clock_gettime(CLOCK_REALTIME, &deadline); \ + deadline.tv_sec += 1; \ + ret = lockfn(l, &deadline); \ + } \ + return -ret; \ + } + +define_interruptible_lock(pthread_mutex_t, mutex_lock, pthread_mutex_timedlock) +define_interruptible_lock(pthread_rwlock_t, rwlock_rdlock, pthread_rwlock_timedrdlock) +define_interruptible_lock(pthread_rwlock_t, rwlock_wrlock, pthread_rwlock_timedwrlock) + +#undef define_interruptible_lock + /* /proc/ = 6 * + * = INTTYPE_TO_STRLEN(pid_t) diff --git a/src/bindings.h b/src/bindings.h index e2463bac..617179df 100644 --- a/src/bindings.h +++ b/src/bindings.h @@ -92,6 +92,10 @@ enum lxcfs_virt_t { */ #define LXCFS_INTR_SIGNAL SIGTTOU +extern int mutex_lock_interruptible(pthread_mutex_t *l); +extern int rwlock_rdlock_interruptible(pthread_rwlock_t *l); +extern int rwlock_wrlock_interruptible(pthread_rwlock_t *l); + struct file_info { char *controller; char *cgroup; From ecb10408a925af74efdc1e480fd1b184f8dbaf69 Mon Sep 17 00:00:00 2001 From: Alexander Mikhalitsyn Date: Thu, 13 Apr 2023 00:16:32 +0200 Subject: [PATCH 3/3] cpuview: start to use interruptible lock primitives Let's start using fuse-interruptible locks in cpuview. It's better to start from one place instead of converting everything at once to prevent global degradations. Signed-off-by: Alexander Mikhalitsyn --- src/proc_cpuview.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/proc_cpuview.c b/src/proc_cpuview.c index f5464de7..14afdd0b 100644 --- a/src/proc_cpuview.c +++ b/src/proc_cpuview.c @@ -286,7 +286,8 @@ static void prune_proc_stat_history(void) time_t now = time(NULL); for (int i = 0; i < CPUVIEW_HASH_SIZE; i++) { - pthread_rwlock_wrlock(&proc_stat_history[i]->lock); + if (rwlock_wrlock_interruptible(&proc_stat_history[i]->lock)) + continue; if ((proc_stat_history[i]->lastcheck + PROC_STAT_PRUNE_INTERVAL) > now) { pthread_rwlock_unlock(&proc_stat_history[i]->lock); @@ -308,7 +309,8 @@ static struct cg_proc_stat *find_proc_stat_node(struct cg_proc_stat_head *head, struct cg_proc_stat *node; prune_proc_stat_history(); - pthread_rwlock_rdlock(&head->lock); + if (rwlock_rdlock_interruptible(&head->lock)) + return NULL; if (!head->next) { pthread_rwlock_unlock(&head->lock); @@ -319,7 +321,13 @@ static struct cg_proc_stat *find_proc_stat_node(struct cg_proc_stat_head *head, do { if (strcmp(cg, node->cg) == 0) { - pthread_mutex_lock(&node->lock); + /* + * If we are failed to take a lock OR + * fuse request was interrupted then + * just return NULL and exit gracefully. + */ + if (mutex_lock_interruptible(&node->lock)) + node = NULL; goto out; } } while ((node = node->next)); @@ -340,6 +348,10 @@ static struct cg_proc_stat *find_or_create_proc_stat_node(struct cpuacct_usage * node = find_proc_stat_node(head, cg); if (!node) { + /* safe place to exit */ + if (fuse_interrupted()) + return NULL; + node = new_proc_stat_node(usage, cpu_count, cg); if (!node) return NULL;