libfuse/lib/fuse_i.h
Joanne Koong 0e0f43b79b Reallocate fuse_session buffer transparently for extended max writes
A previous PR supported extended max writes (eg write requests larger than 1 MB)
by initializing the fuse session buffer size to use the max_pages_limit set in
/proc/sys/fs/fuse. However, this is a huge problem for machines where multiple
fuse servers may be running but only one server needs large writes. In this case,
a lot of memory will be wasted and will lead to OOM issues.

This PR does a reallocation of the session buffer transparently if the server set
 "se->conn.max_write" to a value larger than 1 MiB. This is only for buffers that
are "owned" by libfuse - if the server wishes to provide its own allocated buffer
for receiving/processing requests, then it should ensure that buffer is allocated
to the proper size from the start.

Local testing showed:
echo 65535 | sudo tee /proc/sys/fs/fuse/max_pages_limit
dd if=/dev/urandom of=hello_file bs=6M count=2

write requests:
write request size is 5242880
write request size is 1048576
write request size is 5242880
write request size is 1048576
2024-11-21 19:18:03 +01:00

221 lines
5.7 KiB
C

/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU LGPLv2.
See the file COPYING.LIB
*/
#include "fuse.h"
#include "fuse_lowlevel.h"
#include <stdbool.h>
#define MIN(a, b) \
({ \
typeof(a) _a = (a); \
typeof(b) _b = (b); \
_a < _b ? _a : _b; \
})
struct mount_opts;
struct fuse_req {
struct fuse_session *se;
uint64_t unique;
_Atomic int ref_cnt;
pthread_mutex_t lock;
struct fuse_ctx ctx;
struct fuse_chan *ch;
int interrupted;
unsigned int ioctl_64bit : 1;
union {
struct {
uint64_t unique;
} i;
struct {
fuse_interrupt_func_t func;
void *data;
} ni;
} u;
struct fuse_req *next;
struct fuse_req *prev;
};
struct fuse_notify_req {
uint64_t unique;
void (*reply)(struct fuse_notify_req *, fuse_req_t, fuse_ino_t,
const void *, const struct fuse_buf *);
struct fuse_notify_req *next;
struct fuse_notify_req *prev;
};
struct fuse_session {
char *mountpoint;
volatile int exited;
int fd;
struct fuse_custom_io *io;
struct mount_opts *mo;
int debug;
int deny_others;
struct fuse_lowlevel_ops op;
int got_init;
struct cuse_data *cuse_data;
void *userdata;
uid_t owner;
struct fuse_conn_info conn;
struct fuse_req list;
struct fuse_req interrupts;
pthread_mutex_t lock;
int got_destroy;
pthread_key_t pipe_key;
int broken_splice_nonblock;
uint64_t notify_ctr;
struct fuse_notify_req notify_list;
size_t bufsize;
int error;
/* This is useful if any kind of ABI incompatibility is found at
* a later version, to 'fix' it at run time.
*/
struct libfuse_version version;
bool buf_reallocable;
};
struct fuse_chan {
pthread_mutex_t lock;
int ctr;
int fd;
};
/**
* Filesystem module
*
* Filesystem modules are registered with the FUSE_REGISTER_MODULE()
* macro.
*
*/
struct fuse_module {
char *name;
fuse_module_factory_t factory;
struct fuse_module *next;
struct fusemod_so *so;
int ctr;
};
/**
* Configuration parameters passed to fuse_session_loop_mt() and
* fuse_loop_mt().
*
* Internal API to avoid exposing the plain data structure and
* causing compat issues after adding or removing struct members.
*
*/
#if FUSE_USE_VERSION >= FUSE_MAKE_VERSION(3, 12)
struct fuse_loop_config
{
/* verififier that a correct struct was was passed. This is especially
* needed, as versions below (3, 12) were using a public struct
* (now called fuse_loop_config_v1), which was hard to extend with
* additional parameters, without risking that file system implementations
* would not have noticed and might either pass uninitialized members
* or even too small structs.
* fuse_loop_config_v1 has clone_fd at this offset, which should be either 0
* or 1. v2 or even higher version just need to set a value here
* which not conflicting and very unlikely as having been set by
* file system implementation.
*/
int version_id;
/**
* whether to use separate device fds for each thread
* (may increase performance)
*/
int clone_fd;
/**
* The maximum number of available worker threads before they
* start to get deleted when they become idle. If not
* specified, the default is 10.
*
* Adjusting this has performance implications; a very small number
* of threads in the pool will cause a lot of thread creation and
* deletion overhead and performance may suffer. When set to 0, a new
* thread will be created to service every operation.
* The special value of -1 means that this parameter is disabled.
*/
int max_idle_threads;
/**
* max number of threads taking and processing kernel requests
*
* As of now threads are created dynamically
*/
unsigned int max_threads;
};
#endif
/* ----------------------------------------------------------- *
* Channel interface (when using -o clone_fd) *
* ----------------------------------------------------------- */
/**
* Obtain counted reference to the channel
*
* @param ch the channel
* @return the channel
*/
struct fuse_chan *fuse_chan_get(struct fuse_chan *ch);
/**
* Drop counted reference to a channel
*
* @param ch the channel
*/
void fuse_chan_put(struct fuse_chan *ch);
struct mount_opts *parse_mount_opts(struct fuse_args *args);
void destroy_mount_opts(struct mount_opts *mo);
void fuse_mount_version(void);
unsigned get_max_read(struct mount_opts *o);
void fuse_kern_unmount(const char *mountpoint, int fd);
int fuse_kern_mount(const char *mountpoint, struct mount_opts *mo);
int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov,
int count);
void fuse_free_req(fuse_req_t req);
void cuse_lowlevel_init(fuse_req_t req, fuse_ino_t nodeide, const void *inarg);
int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg);
int fuse_session_receive_buf_int(struct fuse_session *se, struct fuse_buf *buf,
struct fuse_chan *ch);
void fuse_session_process_buf_int(struct fuse_session *se,
const struct fuse_buf *buf, struct fuse_chan *ch);
struct fuse *fuse_new_31(struct fuse_args *args, const struct fuse_operations *op,
size_t op_size, void *private_data);
int fuse_loop_mt_312(struct fuse *f, struct fuse_loop_config *config);
int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config);
/**
* Internal verifier for the given config.
*
* @return negative standard error code or 0 on success
*/
int fuse_loop_cfg_verify(struct fuse_loop_config *config);
/*
* This can be changed dynamically on recent kernels through the
* /proc/sys/fs/fuse/max_pages_limit interface.
*
* Older kernels will always use the default value.
*/
#define FUSE_DEFAULT_MAX_PAGES_LIMIT 256
#define FUSE_DEFAULT_MAX_PAGES_PER_REQ 32
/* room needed in buffer to accommodate header */
#define FUSE_BUFFER_HEADER_SIZE 0x1000