16881f68fSopenharmony_ci/*
26881f68fSopenharmony_ci  FUSE: Filesystem in Userspace
36881f68fSopenharmony_ci  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
46881f68fSopenharmony_ci
56881f68fSopenharmony_ci  Implementation of the multi-threaded FUSE session loop.
66881f68fSopenharmony_ci
76881f68fSopenharmony_ci  This program can be distributed under the terms of the GNU LGPLv2.
86881f68fSopenharmony_ci  See the file COPYING.LIB.
96881f68fSopenharmony_ci*/
106881f68fSopenharmony_ci
116881f68fSopenharmony_ci#include "fuse_config.h"
126881f68fSopenharmony_ci#include "fuse_lowlevel.h"
136881f68fSopenharmony_ci#include "fuse_misc.h"
146881f68fSopenharmony_ci#include "fuse_kernel.h"
156881f68fSopenharmony_ci#include "fuse_i.h"
166881f68fSopenharmony_ci
176881f68fSopenharmony_ci#include <stdio.h>
186881f68fSopenharmony_ci#include <stdlib.h>
196881f68fSopenharmony_ci#include <string.h>
206881f68fSopenharmony_ci#include <unistd.h>
216881f68fSopenharmony_ci#include <signal.h>
226881f68fSopenharmony_ci#include <semaphore.h>
236881f68fSopenharmony_ci#include <errno.h>
246881f68fSopenharmony_ci#include <sys/time.h>
256881f68fSopenharmony_ci#include <sys/ioctl.h>
266881f68fSopenharmony_ci#include <assert.h>
276881f68fSopenharmony_ci#include <limits.h>
286881f68fSopenharmony_ci
296881f68fSopenharmony_ci/* Environment var controlling the thread stack size */
306881f68fSopenharmony_ci#define ENVNAME_THREAD_STACK "FUSE_THREAD_STACK"
316881f68fSopenharmony_ci
326881f68fSopenharmony_ci#define FUSE_LOOP_MT_V2_IDENTIFIER	 INT_MAX - 2
336881f68fSopenharmony_ci#define FUSE_LOOP_MT_DEF_CLONE_FD	 0
346881f68fSopenharmony_ci#define FUSE_LOOP_MT_DEF_MAX_THREADS 10
356881f68fSopenharmony_ci#define FUSE_LOOP_MT_DEF_IDLE_THREADS -1 /* thread destruction is disabled
366881f68fSopenharmony_ci                                          * by default */
376881f68fSopenharmony_ci
386881f68fSopenharmony_ci/* an arbitrary large value that cannot be valid */
396881f68fSopenharmony_ci#define FUSE_LOOP_MT_MAX_THREADS      (100U * 1000)
406881f68fSopenharmony_ci
416881f68fSopenharmony_cistruct fuse_worker {
426881f68fSopenharmony_ci	struct fuse_worker *prev;
436881f68fSopenharmony_ci	struct fuse_worker *next;
446881f68fSopenharmony_ci	pthread_t thread_id;
456881f68fSopenharmony_ci
466881f68fSopenharmony_ci	// We need to include fuse_buf so that we can properly free
476881f68fSopenharmony_ci	// it when a thread is terminated by pthread_cancel().
486881f68fSopenharmony_ci	struct fuse_buf fbuf;
496881f68fSopenharmony_ci	struct fuse_chan *ch;
506881f68fSopenharmony_ci	struct fuse_mt *mt;
516881f68fSopenharmony_ci};
526881f68fSopenharmony_ci
536881f68fSopenharmony_cistruct fuse_mt {
546881f68fSopenharmony_ci	pthread_mutex_t lock;
556881f68fSopenharmony_ci	int numworker;
566881f68fSopenharmony_ci	int numavail;
576881f68fSopenharmony_ci	struct fuse_session *se;
586881f68fSopenharmony_ci	struct fuse_worker main;
596881f68fSopenharmony_ci	sem_t finish;
606881f68fSopenharmony_ci	int exit;
616881f68fSopenharmony_ci	int error;
626881f68fSopenharmony_ci	int clone_fd;
636881f68fSopenharmony_ci	int max_idle;
646881f68fSopenharmony_ci	int max_threads;
656881f68fSopenharmony_ci};
666881f68fSopenharmony_ci
676881f68fSopenharmony_cistatic struct fuse_chan *fuse_chan_new(int fd)
686881f68fSopenharmony_ci{
696881f68fSopenharmony_ci	struct fuse_chan *ch = (struct fuse_chan *) malloc(sizeof(*ch));
706881f68fSopenharmony_ci	if (ch == NULL) {
716881f68fSopenharmony_ci		fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate channel\n");
726881f68fSopenharmony_ci		return NULL;
736881f68fSopenharmony_ci	}
746881f68fSopenharmony_ci
756881f68fSopenharmony_ci	memset(ch, 0, sizeof(*ch));
766881f68fSopenharmony_ci	ch->fd = fd;
776881f68fSopenharmony_ci	ch->ctr = 1;
786881f68fSopenharmony_ci	pthread_mutex_init(&ch->lock, NULL);
796881f68fSopenharmony_ci
806881f68fSopenharmony_ci	return ch;
816881f68fSopenharmony_ci}
826881f68fSopenharmony_ci
836881f68fSopenharmony_cistruct fuse_chan *fuse_chan_get(struct fuse_chan *ch)
846881f68fSopenharmony_ci{
856881f68fSopenharmony_ci	assert(ch->ctr > 0);
866881f68fSopenharmony_ci	pthread_mutex_lock(&ch->lock);
876881f68fSopenharmony_ci	ch->ctr++;
886881f68fSopenharmony_ci	pthread_mutex_unlock(&ch->lock);
896881f68fSopenharmony_ci
906881f68fSopenharmony_ci	return ch;
916881f68fSopenharmony_ci}
926881f68fSopenharmony_ci
936881f68fSopenharmony_civoid fuse_chan_put(struct fuse_chan *ch)
946881f68fSopenharmony_ci{
956881f68fSopenharmony_ci	if (ch == NULL)
966881f68fSopenharmony_ci		return;
976881f68fSopenharmony_ci	pthread_mutex_lock(&ch->lock);
986881f68fSopenharmony_ci	ch->ctr--;
996881f68fSopenharmony_ci	if (!ch->ctr) {
1006881f68fSopenharmony_ci		pthread_mutex_unlock(&ch->lock);
1016881f68fSopenharmony_ci		close(ch->fd);
1026881f68fSopenharmony_ci		pthread_mutex_destroy(&ch->lock);
1036881f68fSopenharmony_ci		free(ch);
1046881f68fSopenharmony_ci	} else
1056881f68fSopenharmony_ci		pthread_mutex_unlock(&ch->lock);
1066881f68fSopenharmony_ci}
1076881f68fSopenharmony_ci
1086881f68fSopenharmony_cistatic void list_add_worker(struct fuse_worker *w, struct fuse_worker *next)
1096881f68fSopenharmony_ci{
1106881f68fSopenharmony_ci	struct fuse_worker *prev = next->prev;
1116881f68fSopenharmony_ci	w->next = next;
1126881f68fSopenharmony_ci	w->prev = prev;
1136881f68fSopenharmony_ci	prev->next = w;
1146881f68fSopenharmony_ci	next->prev = w;
1156881f68fSopenharmony_ci}
1166881f68fSopenharmony_ci
1176881f68fSopenharmony_cistatic void list_del_worker(struct fuse_worker *w)
1186881f68fSopenharmony_ci{
1196881f68fSopenharmony_ci	struct fuse_worker *prev = w->prev;
1206881f68fSopenharmony_ci	struct fuse_worker *next = w->next;
1216881f68fSopenharmony_ci	prev->next = next;
1226881f68fSopenharmony_ci	next->prev = prev;
1236881f68fSopenharmony_ci}
1246881f68fSopenharmony_ci
1256881f68fSopenharmony_cistatic int fuse_loop_start_thread(struct fuse_mt *mt);
1266881f68fSopenharmony_ci
1276881f68fSopenharmony_cistatic void *fuse_do_work(void *data)
1286881f68fSopenharmony_ci{
1296881f68fSopenharmony_ci	struct fuse_worker *w = (struct fuse_worker *) data;
1306881f68fSopenharmony_ci	struct fuse_mt *mt = w->mt;
1316881f68fSopenharmony_ci
1326881f68fSopenharmony_ci	while (!fuse_session_exited(mt->se)) {
1336881f68fSopenharmony_ci		int isforget = 0;
1346881f68fSopenharmony_ci		int res;
1356881f68fSopenharmony_ci
1366881f68fSopenharmony_ci		pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
1376881f68fSopenharmony_ci		res = fuse_session_receive_buf_int(mt->se, &w->fbuf, w->ch);
1386881f68fSopenharmony_ci		pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
1396881f68fSopenharmony_ci		if (res == -EINTR)
1406881f68fSopenharmony_ci			continue;
1416881f68fSopenharmony_ci		if (res <= 0) {
1426881f68fSopenharmony_ci			if (res < 0) {
1436881f68fSopenharmony_ci				fuse_session_exit(mt->se);
1446881f68fSopenharmony_ci				mt->error = res;
1456881f68fSopenharmony_ci			}
1466881f68fSopenharmony_ci			break;
1476881f68fSopenharmony_ci		}
1486881f68fSopenharmony_ci
1496881f68fSopenharmony_ci		pthread_mutex_lock(&mt->lock);
1506881f68fSopenharmony_ci		if (mt->exit) {
1516881f68fSopenharmony_ci			pthread_mutex_unlock(&mt->lock);
1526881f68fSopenharmony_ci			return NULL;
1536881f68fSopenharmony_ci		}
1546881f68fSopenharmony_ci
1556881f68fSopenharmony_ci		/*
1566881f68fSopenharmony_ci		 * This disgusting hack is needed so that zillions of threads
1576881f68fSopenharmony_ci		 * are not created on a burst of FORGET messages
1586881f68fSopenharmony_ci		 */
1596881f68fSopenharmony_ci		if (!(w->fbuf.flags & FUSE_BUF_IS_FD)) {
1606881f68fSopenharmony_ci			struct fuse_in_header *in = w->fbuf.mem;
1616881f68fSopenharmony_ci
1626881f68fSopenharmony_ci			if (in->opcode == FUSE_FORGET ||
1636881f68fSopenharmony_ci			    in->opcode == FUSE_BATCH_FORGET)
1646881f68fSopenharmony_ci				isforget = 1;
1656881f68fSopenharmony_ci		}
1666881f68fSopenharmony_ci
1676881f68fSopenharmony_ci		if (!isforget)
1686881f68fSopenharmony_ci			mt->numavail--;
1696881f68fSopenharmony_ci		if (mt->numavail == 0 && mt->numworker < mt->max_threads)
1706881f68fSopenharmony_ci			fuse_loop_start_thread(mt);
1716881f68fSopenharmony_ci		pthread_mutex_unlock(&mt->lock);
1726881f68fSopenharmony_ci
1736881f68fSopenharmony_ci		fuse_session_process_buf_int(mt->se, &w->fbuf, w->ch);
1746881f68fSopenharmony_ci
1756881f68fSopenharmony_ci		pthread_mutex_lock(&mt->lock);
1766881f68fSopenharmony_ci		if (!isforget)
1776881f68fSopenharmony_ci			mt->numavail++;
1786881f68fSopenharmony_ci
1796881f68fSopenharmony_ci		/* creating and destroying threads is rather expensive - and there is
1806881f68fSopenharmony_ci		 * not much gain from destroying existing threads. It is therefore
1816881f68fSopenharmony_ci		 * discouraged to set max_idle to anything else than -1. If there
1826881f68fSopenharmony_ci		 * is indeed a good reason to destruct threads it should be done
1836881f68fSopenharmony_ci		 * delayed, a moving average might be useful for that.
1846881f68fSopenharmony_ci		 */
1856881f68fSopenharmony_ci		if (mt->max_idle != -1 && mt->numavail > mt->max_idle && mt->numworker > 1) {
1866881f68fSopenharmony_ci			if (mt->exit) {
1876881f68fSopenharmony_ci				pthread_mutex_unlock(&mt->lock);
1886881f68fSopenharmony_ci				return NULL;
1896881f68fSopenharmony_ci			}
1906881f68fSopenharmony_ci			list_del_worker(w);
1916881f68fSopenharmony_ci			mt->numavail--;
1926881f68fSopenharmony_ci			mt->numworker--;
1936881f68fSopenharmony_ci			pthread_mutex_unlock(&mt->lock);
1946881f68fSopenharmony_ci
1956881f68fSopenharmony_ci			pthread_detach(w->thread_id);
1966881f68fSopenharmony_ci			free(w->fbuf.mem);
1976881f68fSopenharmony_ci			fuse_chan_put(w->ch);
1986881f68fSopenharmony_ci			free(w);
1996881f68fSopenharmony_ci			return NULL;
2006881f68fSopenharmony_ci		}
2016881f68fSopenharmony_ci		pthread_mutex_unlock(&mt->lock);
2026881f68fSopenharmony_ci	}
2036881f68fSopenharmony_ci
2046881f68fSopenharmony_ci	sem_post(&mt->finish);
2056881f68fSopenharmony_ci
2066881f68fSopenharmony_ci	return NULL;
2076881f68fSopenharmony_ci}
2086881f68fSopenharmony_ci
2096881f68fSopenharmony_ciint fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg)
2106881f68fSopenharmony_ci{
2116881f68fSopenharmony_ci	sigset_t oldset;
2126881f68fSopenharmony_ci	sigset_t newset;
2136881f68fSopenharmony_ci	int res;
2146881f68fSopenharmony_ci	pthread_attr_t attr;
2156881f68fSopenharmony_ci	char *stack_size;
2166881f68fSopenharmony_ci
2176881f68fSopenharmony_ci	/* Override default stack size
2186881f68fSopenharmony_ci	 * XXX: This should ideally be a parameter option. It is rather
2196881f68fSopenharmony_ci	 *      well hidden here.
2206881f68fSopenharmony_ci	 */
2216881f68fSopenharmony_ci	pthread_attr_init(&attr);
2226881f68fSopenharmony_ci	stack_size = getenv(ENVNAME_THREAD_STACK);
2236881f68fSopenharmony_ci	if (stack_size && pthread_attr_setstacksize(&attr, atoi(stack_size)))
2246881f68fSopenharmony_ci		fuse_log(FUSE_LOG_ERR, "fuse: invalid stack size: %s\n", stack_size);
2256881f68fSopenharmony_ci
2266881f68fSopenharmony_ci	/* Disallow signal reception in worker threads */
2276881f68fSopenharmony_ci	sigemptyset(&newset);
2286881f68fSopenharmony_ci	sigaddset(&newset, SIGTERM);
2296881f68fSopenharmony_ci	sigaddset(&newset, SIGINT);
2306881f68fSopenharmony_ci	sigaddset(&newset, SIGHUP);
2316881f68fSopenharmony_ci	sigaddset(&newset, SIGQUIT);
2326881f68fSopenharmony_ci	pthread_sigmask(SIG_BLOCK, &newset, &oldset);
2336881f68fSopenharmony_ci	res = pthread_create(thread_id, &attr, func, arg);
2346881f68fSopenharmony_ci	pthread_sigmask(SIG_SETMASK, &oldset, NULL);
2356881f68fSopenharmony_ci	pthread_attr_destroy(&attr);
2366881f68fSopenharmony_ci	if (res != 0) {
2376881f68fSopenharmony_ci		fuse_log(FUSE_LOG_ERR, "fuse: error creating thread: %s\n",
2386881f68fSopenharmony_ci			strerror(res));
2396881f68fSopenharmony_ci		return -1;
2406881f68fSopenharmony_ci	}
2416881f68fSopenharmony_ci
2426881f68fSopenharmony_ci	return 0;
2436881f68fSopenharmony_ci}
2446881f68fSopenharmony_ci
2456881f68fSopenharmony_cistatic struct fuse_chan *fuse_clone_chan(struct fuse_mt *mt)
2466881f68fSopenharmony_ci{
2476881f68fSopenharmony_ci	int res;
2486881f68fSopenharmony_ci	int clonefd;
2496881f68fSopenharmony_ci	uint32_t masterfd;
2506881f68fSopenharmony_ci	struct fuse_chan *newch;
2516881f68fSopenharmony_ci	const char *devname = "/dev/fuse";
2526881f68fSopenharmony_ci
2536881f68fSopenharmony_ci#ifndef O_CLOEXEC
2546881f68fSopenharmony_ci#define O_CLOEXEC 0
2556881f68fSopenharmony_ci#endif
2566881f68fSopenharmony_ci	clonefd = open(devname, O_RDWR | O_CLOEXEC);
2576881f68fSopenharmony_ci	if (clonefd == -1) {
2586881f68fSopenharmony_ci		fuse_log(FUSE_LOG_ERR, "fuse: failed to open %s: %s\n", devname,
2596881f68fSopenharmony_ci			strerror(errno));
2606881f68fSopenharmony_ci		return NULL;
2616881f68fSopenharmony_ci	}
2626881f68fSopenharmony_ci	fcntl(clonefd, F_SETFD, FD_CLOEXEC);
2636881f68fSopenharmony_ci
2646881f68fSopenharmony_ci	masterfd = mt->se->fd;
2656881f68fSopenharmony_ci	res = ioctl(clonefd, FUSE_DEV_IOC_CLONE, &masterfd);
2666881f68fSopenharmony_ci	if (res == -1) {
2676881f68fSopenharmony_ci		fuse_log(FUSE_LOG_ERR, "fuse: failed to clone device fd: %s\n",
2686881f68fSopenharmony_ci			strerror(errno));
2696881f68fSopenharmony_ci		close(clonefd);
2706881f68fSopenharmony_ci		return NULL;
2716881f68fSopenharmony_ci	}
2726881f68fSopenharmony_ci	newch = fuse_chan_new(clonefd);
2736881f68fSopenharmony_ci	if (newch == NULL)
2746881f68fSopenharmony_ci		close(clonefd);
2756881f68fSopenharmony_ci
2766881f68fSopenharmony_ci	return newch;
2776881f68fSopenharmony_ci}
2786881f68fSopenharmony_ci
2796881f68fSopenharmony_cistatic int fuse_loop_start_thread(struct fuse_mt *mt)
2806881f68fSopenharmony_ci{
2816881f68fSopenharmony_ci	int res;
2826881f68fSopenharmony_ci
2836881f68fSopenharmony_ci	struct fuse_worker *w = malloc(sizeof(struct fuse_worker));
2846881f68fSopenharmony_ci	if (!w) {
2856881f68fSopenharmony_ci		fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate worker structure\n");
2866881f68fSopenharmony_ci		return -1;
2876881f68fSopenharmony_ci	}
2886881f68fSopenharmony_ci	memset(w, 0, sizeof(struct fuse_worker));
2896881f68fSopenharmony_ci	w->fbuf.mem = NULL;
2906881f68fSopenharmony_ci	w->mt = mt;
2916881f68fSopenharmony_ci
2926881f68fSopenharmony_ci	w->ch = NULL;
2936881f68fSopenharmony_ci	if (mt->clone_fd) {
2946881f68fSopenharmony_ci		w->ch = fuse_clone_chan(mt);
2956881f68fSopenharmony_ci		if(!w->ch) {
2966881f68fSopenharmony_ci			/* Don't attempt this again */
2976881f68fSopenharmony_ci			fuse_log(FUSE_LOG_ERR, "fuse: trying to continue "
2986881f68fSopenharmony_ci				"without -o clone_fd.\n");
2996881f68fSopenharmony_ci			mt->clone_fd = 0;
3006881f68fSopenharmony_ci		}
3016881f68fSopenharmony_ci	}
3026881f68fSopenharmony_ci
3036881f68fSopenharmony_ci	res = fuse_start_thread(&w->thread_id, fuse_do_work, w);
3046881f68fSopenharmony_ci	if (res == -1) {
3056881f68fSopenharmony_ci		fuse_chan_put(w->ch);
3066881f68fSopenharmony_ci		free(w);
3076881f68fSopenharmony_ci		return -1;
3086881f68fSopenharmony_ci	}
3096881f68fSopenharmony_ci	list_add_worker(w, &mt->main);
3106881f68fSopenharmony_ci	mt->numavail ++;
3116881f68fSopenharmony_ci	mt->numworker ++;
3126881f68fSopenharmony_ci
3136881f68fSopenharmony_ci	return 0;
3146881f68fSopenharmony_ci}
3156881f68fSopenharmony_ci
3166881f68fSopenharmony_cistatic void fuse_join_worker(struct fuse_mt *mt, struct fuse_worker *w)
3176881f68fSopenharmony_ci{
3186881f68fSopenharmony_ci	pthread_join(w->thread_id, NULL);
3196881f68fSopenharmony_ci	pthread_mutex_lock(&mt->lock);
3206881f68fSopenharmony_ci	list_del_worker(w);
3216881f68fSopenharmony_ci	pthread_mutex_unlock(&mt->lock);
3226881f68fSopenharmony_ci	free(w->fbuf.mem);
3236881f68fSopenharmony_ci	fuse_chan_put(w->ch);
3246881f68fSopenharmony_ci	free(w);
3256881f68fSopenharmony_ci}
3266881f68fSopenharmony_ci
3276881f68fSopenharmony_ciint fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config);
3286881f68fSopenharmony_ciFUSE_SYMVER("fuse_session_loop_mt_312", "fuse_session_loop_mt@@FUSE_3.12")
3296881f68fSopenharmony_ciint fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config)
3306881f68fSopenharmony_ci{
3316881f68fSopenharmony_ciint err;
3326881f68fSopenharmony_ci	struct fuse_mt mt;
3336881f68fSopenharmony_ci	struct fuse_worker *w;
3346881f68fSopenharmony_ci	int created_config = 0;
3356881f68fSopenharmony_ci
3366881f68fSopenharmony_ci	if (config) {
3376881f68fSopenharmony_ci		err = fuse_loop_cfg_verify(config);
3386881f68fSopenharmony_ci		if (err)
3396881f68fSopenharmony_ci			return err;
3406881f68fSopenharmony_ci	} else {
3416881f68fSopenharmony_ci		/* The caller does not care about parameters - use the default */
3426881f68fSopenharmony_ci		config = fuse_loop_cfg_create();
3436881f68fSopenharmony_ci		created_config = 1;
3446881f68fSopenharmony_ci	}
3456881f68fSopenharmony_ci
3466881f68fSopenharmony_ci
3476881f68fSopenharmony_ci	memset(&mt, 0, sizeof(struct fuse_mt));
3486881f68fSopenharmony_ci	mt.se = se;
3496881f68fSopenharmony_ci	mt.clone_fd = config->clone_fd;
3506881f68fSopenharmony_ci	mt.error = 0;
3516881f68fSopenharmony_ci	mt.numworker = 0;
3526881f68fSopenharmony_ci	mt.numavail = 0;
3536881f68fSopenharmony_ci	mt.max_idle = config->max_idle_threads;
3546881f68fSopenharmony_ci	mt.max_threads = config->max_threads;
3556881f68fSopenharmony_ci	mt.main.thread_id = pthread_self();
3566881f68fSopenharmony_ci	mt.main.prev = mt.main.next = &mt.main;
3576881f68fSopenharmony_ci	sem_init(&mt.finish, 0, 0);
3586881f68fSopenharmony_ci	pthread_mutex_init(&mt.lock, NULL);
3596881f68fSopenharmony_ci
3606881f68fSopenharmony_ci	pthread_mutex_lock(&mt.lock);
3616881f68fSopenharmony_ci	err = fuse_loop_start_thread(&mt);
3626881f68fSopenharmony_ci	pthread_mutex_unlock(&mt.lock);
3636881f68fSopenharmony_ci	if (!err) {
3646881f68fSopenharmony_ci		/* sem_wait() is interruptible */
3656881f68fSopenharmony_ci		while (!fuse_session_exited(se))
3666881f68fSopenharmony_ci			sem_wait(&mt.finish);
3676881f68fSopenharmony_ci
3686881f68fSopenharmony_ci		pthread_mutex_lock(&mt.lock);
3696881f68fSopenharmony_ci		for (w = mt.main.next; w != &mt.main; w = w->next)
3706881f68fSopenharmony_ci			pthread_cancel(w->thread_id);
3716881f68fSopenharmony_ci		mt.exit = 1;
3726881f68fSopenharmony_ci		pthread_mutex_unlock(&mt.lock);
3736881f68fSopenharmony_ci
3746881f68fSopenharmony_ci		while (mt.main.next != &mt.main)
3756881f68fSopenharmony_ci			fuse_join_worker(&mt, mt.main.next);
3766881f68fSopenharmony_ci
3776881f68fSopenharmony_ci		err = mt.error;
3786881f68fSopenharmony_ci	}
3796881f68fSopenharmony_ci
3806881f68fSopenharmony_ci	pthread_mutex_destroy(&mt.lock);
3816881f68fSopenharmony_ci	sem_destroy(&mt.finish);
3826881f68fSopenharmony_ci	if(se->error != 0)
3836881f68fSopenharmony_ci		err = se->error;
3846881f68fSopenharmony_ci	fuse_session_reset(se);
3856881f68fSopenharmony_ci
3866881f68fSopenharmony_ci	if (created_config) {
3876881f68fSopenharmony_ci		fuse_loop_cfg_destroy(config);
3886881f68fSopenharmony_ci		config = NULL;
3896881f68fSopenharmony_ci	}
3906881f68fSopenharmony_ci
3916881f68fSopenharmony_ci	return err;
3926881f68fSopenharmony_ci}
3936881f68fSopenharmony_ci
3946881f68fSopenharmony_ciint fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config_v1 *config_v1);
3956881f68fSopenharmony_ciFUSE_SYMVER("fuse_session_loop_mt_32", "fuse_session_loop_mt@FUSE_3.2")
3966881f68fSopenharmony_ciint fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config_v1 *config_v1)
3976881f68fSopenharmony_ci{
3986881f68fSopenharmony_ci	int err;
3996881f68fSopenharmony_ci	struct fuse_loop_config *config = NULL;
4006881f68fSopenharmony_ci
4016881f68fSopenharmony_ci	if (config_v1 != NULL) {
4026881f68fSopenharmony_ci		/* convert the given v1 config */
4036881f68fSopenharmony_ci		config = fuse_loop_cfg_create();
4046881f68fSopenharmony_ci		if (config == NULL)
4056881f68fSopenharmony_ci			return ENOMEM;
4066881f68fSopenharmony_ci
4076881f68fSopenharmony_ci		fuse_loop_cfg_convert(config, config_v1);
4086881f68fSopenharmony_ci	}
4096881f68fSopenharmony_ci
4106881f68fSopenharmony_ci	err = fuse_session_loop_mt_312(se, config);
4116881f68fSopenharmony_ci
4126881f68fSopenharmony_ci	fuse_loop_cfg_destroy(config);
4136881f68fSopenharmony_ci
4146881f68fSopenharmony_ci	return err;
4156881f68fSopenharmony_ci}
4166881f68fSopenharmony_ci
4176881f68fSopenharmony_ci
4186881f68fSopenharmony_ciint fuse_session_loop_mt_31(struct fuse_session *se, int clone_fd);
4196881f68fSopenharmony_ciFUSE_SYMVER("fuse_session_loop_mt_31", "fuse_session_loop_mt@FUSE_3.0")
4206881f68fSopenharmony_ciint fuse_session_loop_mt_31(struct fuse_session *se, int clone_fd)
4216881f68fSopenharmony_ci{
4226881f68fSopenharmony_ci	struct fuse_loop_config *config = fuse_loop_cfg_create();
4236881f68fSopenharmony_ci	if (clone_fd > 0)
4246881f68fSopenharmony_ci		 fuse_loop_cfg_set_clone_fd(config, clone_fd);
4256881f68fSopenharmony_ci	return fuse_session_loop_mt_312(se, config);
4266881f68fSopenharmony_ci}
4276881f68fSopenharmony_ci
4286881f68fSopenharmony_cistruct fuse_loop_config *fuse_loop_cfg_create(void)
4296881f68fSopenharmony_ci{
4306881f68fSopenharmony_ci	struct fuse_loop_config *config = calloc(1, sizeof(*config));
4316881f68fSopenharmony_ci	if (config == NULL)
4326881f68fSopenharmony_ci		return NULL;
4336881f68fSopenharmony_ci
4346881f68fSopenharmony_ci	config->version_id       = FUSE_LOOP_MT_V2_IDENTIFIER;
4356881f68fSopenharmony_ci	config->max_idle_threads = FUSE_LOOP_MT_DEF_IDLE_THREADS;
4366881f68fSopenharmony_ci	config->max_threads      = FUSE_LOOP_MT_DEF_MAX_THREADS;
4376881f68fSopenharmony_ci	config->clone_fd         = FUSE_LOOP_MT_DEF_CLONE_FD;
4386881f68fSopenharmony_ci
4396881f68fSopenharmony_ci	return config;
4406881f68fSopenharmony_ci}
4416881f68fSopenharmony_ci
4426881f68fSopenharmony_civoid fuse_loop_cfg_destroy(struct fuse_loop_config *config)
4436881f68fSopenharmony_ci{
4446881f68fSopenharmony_ci	free(config);
4456881f68fSopenharmony_ci}
4466881f68fSopenharmony_ci
4476881f68fSopenharmony_ciint fuse_loop_cfg_verify(struct fuse_loop_config *config)
4486881f68fSopenharmony_ci{
4496881f68fSopenharmony_ci	if (config->version_id != FUSE_LOOP_MT_V2_IDENTIFIER)
4506881f68fSopenharmony_ci		return -EINVAL;
4516881f68fSopenharmony_ci
4526881f68fSopenharmony_ci	return 0;
4536881f68fSopenharmony_ci}
4546881f68fSopenharmony_ci
4556881f68fSopenharmony_civoid fuse_loop_cfg_convert(struct fuse_loop_config *config,
4566881f68fSopenharmony_ci			   struct fuse_loop_config_v1 *v1_conf)
4576881f68fSopenharmony_ci{
4586881f68fSopenharmony_ci	fuse_loop_cfg_set_idle_threads(config, v1_conf->max_idle_threads);
4596881f68fSopenharmony_ci
4606881f68fSopenharmony_ci	fuse_loop_cfg_set_clone_fd(config, v1_conf->clone_fd);
4616881f68fSopenharmony_ci}
4626881f68fSopenharmony_ci
4636881f68fSopenharmony_civoid fuse_loop_cfg_set_idle_threads(struct fuse_loop_config *config,
4646881f68fSopenharmony_ci				    unsigned int value)
4656881f68fSopenharmony_ci{
4666881f68fSopenharmony_ci	if (value > FUSE_LOOP_MT_MAX_THREADS) {
4676881f68fSopenharmony_ci		if (value != UINT_MAX)
4686881f68fSopenharmony_ci			fuse_log(FUSE_LOG_ERR,
4696881f68fSopenharmony_ci				 "Ignoring invalid max threads value "
4706881f68fSopenharmony_ci				 "%u > max (%u).\n", value,
4716881f68fSopenharmony_ci				 FUSE_LOOP_MT_MAX_THREADS);
4726881f68fSopenharmony_ci		return;
4736881f68fSopenharmony_ci	}
4746881f68fSopenharmony_ci	config->max_idle_threads = value;
4756881f68fSopenharmony_ci}
4766881f68fSopenharmony_ci
4776881f68fSopenharmony_civoid fuse_loop_cfg_set_max_threads(struct fuse_loop_config *config,
4786881f68fSopenharmony_ci				   unsigned int value)
4796881f68fSopenharmony_ci{
4806881f68fSopenharmony_ci	config->max_threads = value;
4816881f68fSopenharmony_ci}
4826881f68fSopenharmony_ci
4836881f68fSopenharmony_civoid fuse_loop_cfg_set_clone_fd(struct fuse_loop_config *config,
4846881f68fSopenharmony_ci				unsigned int value)
4856881f68fSopenharmony_ci{
4866881f68fSopenharmony_ci	config->clone_fd = value;
4876881f68fSopenharmony_ci}
4886881f68fSopenharmony_ci
489