mirror of
https://github.com/torvalds/linux.git
synced 2025-04-06 00:16:18 +00:00
fuse update for 6.15
-----BEGIN PGP SIGNATURE----- iHUEABYKAB0WIQSQHSd0lITzzeNWNm3h3BK/laaZPAUCZ+vB5QAKCRDh3BK/laaZ PGA2AQCVsyLmZFinaNC10S+Bkmx+a7f9MLhX6u+ILbmio8nT1AD7BKCDFD9pucG0 pilz+OaCXjXt/og6doyugM4SW/Q3tA0= =BhLT -----END PGP SIGNATURE----- Merge tag 'fuse-update-6.15' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse Pull fuse updates from Miklos Szeredi: - Allow connection to server to time out (Joanne Koong) - If server doesn't support creating a hard link, return EPERM rather than ENOSYS (Matt Johnston) - Allow file names longer than 1024 chars (Bernd Schubert) - Fix a possible race if request on io_uring queue is interrupted (Bernd Schubert) - Misc fixes and cleanups * tag 'fuse-update-6.15' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse: fuse: remove unneeded atomic set in uring creation fuse: fix uring race condition for null dereference of fc fuse: Increase FUSE_NAME_MAX to PATH_MAX fuse: Allocate only namelen buf memory in fuse_notify_ fuse: add default_request_timeout and max_request_timeout sysctls fuse: add kernel-enforced timeout option for requests fuse: optmize missing FUSE_LINK support fuse: Return EPERM rather than ENOSYS from link() fuse: removed unused function fuse_uring_create() from header fuse: {io-uring} Fix a possible req cancellation race
This commit is contained in:
commit
5e17b5c717
@ -347,3 +347,28 @@ filesystems:
|
||||
``/proc/sys/fs/fuse/max_pages_limit`` is a read/write file for
|
||||
setting/getting the maximum number of pages that can be used for servicing
|
||||
requests in FUSE.
|
||||
|
||||
``/proc/sys/fs/fuse/default_request_timeout`` is a read/write file for
|
||||
setting/getting the default timeout (in seconds) for a fuse server to
|
||||
reply to a kernel-issued request in the event where the server did not
|
||||
specify a timeout at mount. If the server set a timeout,
|
||||
then default_request_timeout will be ignored. The default
|
||||
"default_request_timeout" is set to 0. 0 indicates no default timeout.
|
||||
The maximum value that can be set is 65535.
|
||||
|
||||
``/proc/sys/fs/fuse/max_request_timeout`` is a read/write file for
|
||||
setting/getting the maximum timeout (in seconds) for a fuse server to
|
||||
reply to a kernel-issued request. A value greater than 0 automatically opts
|
||||
the server into a timeout that will be set to at most "max_request_timeout",
|
||||
even if the server did not specify a timeout and default_request_timeout is
|
||||
set to 0. If max_request_timeout is greater than 0 and the server set a timeout
|
||||
greater than max_request_timeout or default_request_timeout is set to a value
|
||||
greater than max_request_timeout, the system will use max_request_timeout as the
|
||||
timeout. 0 indicates no max request timeout. The maximum value that can be set
|
||||
is 65535.
|
||||
|
||||
For timeouts, if the server does not respond to the request by the time
|
||||
the set timeout elapses, then the connection to the fuse server will be aborted.
|
||||
Please note that the timeouts are not 100% precise (eg you may set 60 seconds but
|
||||
the timeout may kick in after 70 seconds). The upper margin of error for the
|
||||
timeout is roughly FUSE_TIMEOUT_TIMER_FREQ seconds.
|
||||
|
162
fs/fuse/dev.c
162
fs/fuse/dev.c
@ -32,6 +32,100 @@ MODULE_ALIAS("devname:fuse");
|
||||
|
||||
static struct kmem_cache *fuse_req_cachep;
|
||||
|
||||
const unsigned long fuse_timeout_timer_freq =
|
||||
secs_to_jiffies(FUSE_TIMEOUT_TIMER_FREQ);
|
||||
|
||||
bool fuse_request_expired(struct fuse_conn *fc, struct list_head *list)
|
||||
{
|
||||
struct fuse_req *req;
|
||||
|
||||
req = list_first_entry_or_null(list, struct fuse_req, list);
|
||||
if (!req)
|
||||
return false;
|
||||
return time_is_before_jiffies(req->create_time + fc->timeout.req_timeout);
|
||||
}
|
||||
|
||||
bool fuse_fpq_processing_expired(struct fuse_conn *fc, struct list_head *processing)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < FUSE_PQ_HASH_SIZE; i++)
|
||||
if (fuse_request_expired(fc, &processing[i]))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if any requests aren't being completed by the time the request timeout
|
||||
* elapses. To do so, we:
|
||||
* - check the fiq pending list
|
||||
* - check the bg queue
|
||||
* - check the fpq io and processing lists
|
||||
*
|
||||
* To make this fast, we only check against the head request on each list since
|
||||
* these are generally queued in order of creation time (eg newer requests get
|
||||
* queued to the tail). We might miss a few edge cases (eg requests transitioning
|
||||
* between lists, re-sent requests at the head of the pending list having a
|
||||
* later creation time than other requests on that list, etc.) but that is fine
|
||||
* since if the request never gets fulfilled, it will eventually be caught.
|
||||
*/
|
||||
void fuse_check_timeout(struct work_struct *work)
|
||||
{
|
||||
struct delayed_work *dwork = to_delayed_work(work);
|
||||
struct fuse_conn *fc = container_of(dwork, struct fuse_conn,
|
||||
timeout.work);
|
||||
struct fuse_iqueue *fiq = &fc->iq;
|
||||
struct fuse_dev *fud;
|
||||
struct fuse_pqueue *fpq;
|
||||
bool expired = false;
|
||||
|
||||
if (!atomic_read(&fc->num_waiting))
|
||||
goto out;
|
||||
|
||||
spin_lock(&fiq->lock);
|
||||
expired = fuse_request_expired(fc, &fiq->pending);
|
||||
spin_unlock(&fiq->lock);
|
||||
if (expired)
|
||||
goto abort_conn;
|
||||
|
||||
spin_lock(&fc->bg_lock);
|
||||
expired = fuse_request_expired(fc, &fc->bg_queue);
|
||||
spin_unlock(&fc->bg_lock);
|
||||
if (expired)
|
||||
goto abort_conn;
|
||||
|
||||
spin_lock(&fc->lock);
|
||||
if (!fc->connected) {
|
||||
spin_unlock(&fc->lock);
|
||||
return;
|
||||
}
|
||||
list_for_each_entry(fud, &fc->devices, entry) {
|
||||
fpq = &fud->pq;
|
||||
spin_lock(&fpq->lock);
|
||||
if (fuse_request_expired(fc, &fpq->io) ||
|
||||
fuse_fpq_processing_expired(fc, fpq->processing)) {
|
||||
spin_unlock(&fpq->lock);
|
||||
spin_unlock(&fc->lock);
|
||||
goto abort_conn;
|
||||
}
|
||||
|
||||
spin_unlock(&fpq->lock);
|
||||
}
|
||||
spin_unlock(&fc->lock);
|
||||
|
||||
if (fuse_uring_request_expired(fc))
|
||||
goto abort_conn;
|
||||
|
||||
out:
|
||||
queue_delayed_work(system_wq, &fc->timeout.work,
|
||||
fuse_timeout_timer_freq);
|
||||
return;
|
||||
|
||||
abort_conn:
|
||||
fuse_abort_conn(fc);
|
||||
}
|
||||
|
||||
static void fuse_request_init(struct fuse_mount *fm, struct fuse_req *req)
|
||||
{
|
||||
INIT_LIST_HEAD(&req->list);
|
||||
@ -40,6 +134,7 @@ static void fuse_request_init(struct fuse_mount *fm, struct fuse_req *req)
|
||||
refcount_set(&req->count, 1);
|
||||
__set_bit(FR_PENDING, &req->flags);
|
||||
req->fm = fm;
|
||||
req->create_time = jiffies;
|
||||
}
|
||||
|
||||
static struct fuse_req *fuse_request_alloc(struct fuse_mount *fm, gfp_t flags)
|
||||
@ -407,6 +502,24 @@ static int queue_interrupt(struct fuse_req *req)
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool fuse_remove_pending_req(struct fuse_req *req, spinlock_t *lock)
|
||||
{
|
||||
spin_lock(lock);
|
||||
if (test_bit(FR_PENDING, &req->flags)) {
|
||||
/*
|
||||
* FR_PENDING does not get cleared as the request will end
|
||||
* up in destruction anyway.
|
||||
*/
|
||||
list_del(&req->list);
|
||||
spin_unlock(lock);
|
||||
__fuse_put_request(req);
|
||||
req->out.h.error = -EINTR;
|
||||
return true;
|
||||
}
|
||||
spin_unlock(lock);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void request_wait_answer(struct fuse_req *req)
|
||||
{
|
||||
struct fuse_conn *fc = req->fm->fc;
|
||||
@ -428,22 +541,20 @@ static void request_wait_answer(struct fuse_req *req)
|
||||
}
|
||||
|
||||
if (!test_bit(FR_FORCE, &req->flags)) {
|
||||
bool removed;
|
||||
|
||||
/* Only fatal signals may interrupt this */
|
||||
err = wait_event_killable(req->waitq,
|
||||
test_bit(FR_FINISHED, &req->flags));
|
||||
if (!err)
|
||||
return;
|
||||
|
||||
spin_lock(&fiq->lock);
|
||||
/* Request is not yet in userspace, bail out */
|
||||
if (test_bit(FR_PENDING, &req->flags)) {
|
||||
list_del(&req->list);
|
||||
spin_unlock(&fiq->lock);
|
||||
__fuse_put_request(req);
|
||||
req->out.h.error = -EINTR;
|
||||
if (test_bit(FR_URING, &req->flags))
|
||||
removed = fuse_uring_remove_pending_req(req);
|
||||
else
|
||||
removed = fuse_remove_pending_req(req, &fiq->lock);
|
||||
if (removed)
|
||||
return;
|
||||
}
|
||||
spin_unlock(&fiq->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1533,14 +1644,10 @@ static int fuse_notify_inval_entry(struct fuse_conn *fc, unsigned int size,
|
||||
struct fuse_copy_state *cs)
|
||||
{
|
||||
struct fuse_notify_inval_entry_out outarg;
|
||||
int err = -ENOMEM;
|
||||
char *buf;
|
||||
int err;
|
||||
char *buf = NULL;
|
||||
struct qstr name;
|
||||
|
||||
buf = kzalloc(FUSE_NAME_MAX + 1, GFP_KERNEL);
|
||||
if (!buf)
|
||||
goto err;
|
||||
|
||||
err = -EINVAL;
|
||||
if (size < sizeof(outarg))
|
||||
goto err;
|
||||
@ -1550,13 +1657,18 @@ static int fuse_notify_inval_entry(struct fuse_conn *fc, unsigned int size,
|
||||
goto err;
|
||||
|
||||
err = -ENAMETOOLONG;
|
||||
if (outarg.namelen > FUSE_NAME_MAX)
|
||||
if (outarg.namelen > fc->name_max)
|
||||
goto err;
|
||||
|
||||
err = -EINVAL;
|
||||
if (size != sizeof(outarg) + outarg.namelen + 1)
|
||||
goto err;
|
||||
|
||||
err = -ENOMEM;
|
||||
buf = kzalloc(outarg.namelen + 1, GFP_KERNEL);
|
||||
if (!buf)
|
||||
goto err;
|
||||
|
||||
name.name = buf;
|
||||
name.len = outarg.namelen;
|
||||
err = fuse_copy_one(cs, buf, outarg.namelen + 1);
|
||||
@ -1581,14 +1693,10 @@ static int fuse_notify_delete(struct fuse_conn *fc, unsigned int size,
|
||||
struct fuse_copy_state *cs)
|
||||
{
|
||||
struct fuse_notify_delete_out outarg;
|
||||
int err = -ENOMEM;
|
||||
char *buf;
|
||||
int err;
|
||||
char *buf = NULL;
|
||||
struct qstr name;
|
||||
|
||||
buf = kzalloc(FUSE_NAME_MAX + 1, GFP_KERNEL);
|
||||
if (!buf)
|
||||
goto err;
|
||||
|
||||
err = -EINVAL;
|
||||
if (size < sizeof(outarg))
|
||||
goto err;
|
||||
@ -1598,13 +1706,18 @@ static int fuse_notify_delete(struct fuse_conn *fc, unsigned int size,
|
||||
goto err;
|
||||
|
||||
err = -ENAMETOOLONG;
|
||||
if (outarg.namelen > FUSE_NAME_MAX)
|
||||
if (outarg.namelen > fc->name_max)
|
||||
goto err;
|
||||
|
||||
err = -EINVAL;
|
||||
if (size != sizeof(outarg) + outarg.namelen + 1)
|
||||
goto err;
|
||||
|
||||
err = -ENOMEM;
|
||||
buf = kzalloc(outarg.namelen + 1, GFP_KERNEL);
|
||||
if (!buf)
|
||||
goto err;
|
||||
|
||||
name.name = buf;
|
||||
name.len = outarg.namelen;
|
||||
err = fuse_copy_one(cs, buf, outarg.namelen + 1);
|
||||
@ -2275,6 +2388,9 @@ void fuse_abort_conn(struct fuse_conn *fc)
|
||||
LIST_HEAD(to_end);
|
||||
unsigned int i;
|
||||
|
||||
if (fc->timeout.req_timeout)
|
||||
cancel_delayed_work(&fc->timeout.work);
|
||||
|
||||
/* Background queuing checks fc->connected under bg_lock */
|
||||
spin_lock(&fc->bg_lock);
|
||||
fc->connected = 0;
|
||||
|
@ -140,6 +140,33 @@ void fuse_uring_abort_end_requests(struct fuse_ring *ring)
|
||||
}
|
||||
}
|
||||
|
||||
bool fuse_uring_request_expired(struct fuse_conn *fc)
|
||||
{
|
||||
struct fuse_ring *ring = fc->ring;
|
||||
struct fuse_ring_queue *queue;
|
||||
int qid;
|
||||
|
||||
if (!ring)
|
||||
return false;
|
||||
|
||||
for (qid = 0; qid < ring->nr_queues; qid++) {
|
||||
queue = READ_ONCE(ring->queues[qid]);
|
||||
if (!queue)
|
||||
continue;
|
||||
|
||||
spin_lock(&queue->lock);
|
||||
if (fuse_request_expired(fc, &queue->fuse_req_queue) ||
|
||||
fuse_request_expired(fc, &queue->fuse_req_bg_queue) ||
|
||||
fuse_fpq_processing_expired(fc, queue->fpq.processing)) {
|
||||
spin_unlock(&queue->lock);
|
||||
return true;
|
||||
}
|
||||
spin_unlock(&queue->lock);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void fuse_uring_destruct(struct fuse_conn *fc)
|
||||
{
|
||||
struct fuse_ring *ring = fc->ring;
|
||||
@ -211,7 +238,6 @@ static struct fuse_ring *fuse_uring_create(struct fuse_conn *fc)
|
||||
ring->nr_queues = nr_queues;
|
||||
ring->fc = fc;
|
||||
ring->max_payload_sz = max_payload_size;
|
||||
atomic_set(&ring->queue_refs, 0);
|
||||
smp_store_release(&fc->ring, ring);
|
||||
|
||||
spin_unlock(&fc->lock);
|
||||
@ -726,8 +752,6 @@ static void fuse_uring_add_req_to_ring_ent(struct fuse_ring_ent *ent,
|
||||
struct fuse_req *req)
|
||||
{
|
||||
struct fuse_ring_queue *queue = ent->queue;
|
||||
struct fuse_conn *fc = req->fm->fc;
|
||||
struct fuse_iqueue *fiq = &fc->iq;
|
||||
|
||||
lockdep_assert_held(&queue->lock);
|
||||
|
||||
@ -737,9 +761,7 @@ static void fuse_uring_add_req_to_ring_ent(struct fuse_ring_ent *ent,
|
||||
ent->state);
|
||||
}
|
||||
|
||||
spin_lock(&fiq->lock);
|
||||
clear_bit(FR_PENDING, &req->flags);
|
||||
spin_unlock(&fiq->lock);
|
||||
ent->fuse_req = req;
|
||||
ent->state = FRRS_FUSE_REQ;
|
||||
list_move(&ent->list, &queue->ent_w_req_queue);
|
||||
@ -1238,6 +1260,8 @@ void fuse_uring_queue_fuse_req(struct fuse_iqueue *fiq, struct fuse_req *req)
|
||||
if (unlikely(queue->stopped))
|
||||
goto err_unlock;
|
||||
|
||||
set_bit(FR_URING, &req->flags);
|
||||
req->ring_queue = queue;
|
||||
ent = list_first_entry_or_null(&queue->ent_avail_queue,
|
||||
struct fuse_ring_ent, list);
|
||||
if (ent)
|
||||
@ -1276,6 +1300,8 @@ bool fuse_uring_queue_bq_req(struct fuse_req *req)
|
||||
return false;
|
||||
}
|
||||
|
||||
set_bit(FR_URING, &req->flags);
|
||||
req->ring_queue = queue;
|
||||
list_add_tail(&req->list, &queue->fuse_req_bg_queue);
|
||||
|
||||
ent = list_first_entry_or_null(&queue->ent_avail_queue,
|
||||
@ -1306,6 +1332,13 @@ bool fuse_uring_queue_bq_req(struct fuse_req *req)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fuse_uring_remove_pending_req(struct fuse_req *req)
|
||||
{
|
||||
struct fuse_ring_queue *queue = req->ring_queue;
|
||||
|
||||
return fuse_remove_pending_req(req, &queue->lock);
|
||||
}
|
||||
|
||||
static const struct fuse_iqueue_ops fuse_io_uring_ops = {
|
||||
/* should be send over io-uring as enhancement */
|
||||
.send_forget = fuse_dev_queue_forget,
|
||||
|
@ -142,6 +142,8 @@ void fuse_uring_abort_end_requests(struct fuse_ring *ring);
|
||||
int fuse_uring_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags);
|
||||
void fuse_uring_queue_fuse_req(struct fuse_iqueue *fiq, struct fuse_req *req);
|
||||
bool fuse_uring_queue_bq_req(struct fuse_req *req);
|
||||
bool fuse_uring_remove_pending_req(struct fuse_req *req);
|
||||
bool fuse_uring_request_expired(struct fuse_conn *fc);
|
||||
|
||||
static inline void fuse_uring_abort(struct fuse_conn *fc)
|
||||
{
|
||||
@ -172,12 +174,6 @@ static inline bool fuse_uring_ready(struct fuse_conn *fc)
|
||||
|
||||
#else /* CONFIG_FUSE_IO_URING */
|
||||
|
||||
struct fuse_ring;
|
||||
|
||||
static inline void fuse_uring_create(struct fuse_conn *fc)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void fuse_uring_destruct(struct fuse_conn *fc)
|
||||
{
|
||||
}
|
||||
@ -200,6 +196,16 @@ static inline bool fuse_uring_ready(struct fuse_conn *fc)
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool fuse_uring_remove_pending_req(struct fuse_req *req)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool fuse_uring_request_expired(struct fuse_conn *fc)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_FUSE_IO_URING */
|
||||
|
||||
#endif /* _FS_FUSE_DEV_URING_I_H */
|
||||
|
@ -370,7 +370,7 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name
|
||||
|
||||
*inode = NULL;
|
||||
err = -ENAMETOOLONG;
|
||||
if (name->len > FUSE_NAME_MAX)
|
||||
if (name->len > fm->fc->name_max)
|
||||
goto out;
|
||||
|
||||
|
||||
@ -1137,6 +1137,9 @@ static int fuse_link(struct dentry *entry, struct inode *newdir,
|
||||
struct fuse_mount *fm = get_fuse_mount(inode);
|
||||
FUSE_ARGS(args);
|
||||
|
||||
if (fm->fc->no_link)
|
||||
goto out;
|
||||
|
||||
memset(&inarg, 0, sizeof(inarg));
|
||||
inarg.oldnodeid = get_node_id(inode);
|
||||
args.opcode = FUSE_LINK;
|
||||
@ -1151,6 +1154,12 @@ static int fuse_link(struct dentry *entry, struct inode *newdir,
|
||||
else if (err == -EINTR)
|
||||
fuse_invalidate_attr(inode);
|
||||
|
||||
if (err == -ENOSYS)
|
||||
fm->fc->no_link = 1;
|
||||
out:
|
||||
if (fm->fc->no_link)
|
||||
return -EPERM;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -61,6 +61,10 @@ int fuse_copy_out_args(struct fuse_copy_state *cs, struct fuse_args *args,
|
||||
void fuse_dev_queue_forget(struct fuse_iqueue *fiq,
|
||||
struct fuse_forget_link *forget);
|
||||
void fuse_dev_queue_interrupt(struct fuse_iqueue *fiq, struct fuse_req *req);
|
||||
bool fuse_remove_pending_req(struct fuse_req *req, spinlock_t *lock);
|
||||
|
||||
bool fuse_request_expired(struct fuse_conn *fc, struct list_head *list);
|
||||
bool fuse_fpq_processing_expired(struct fuse_conn *fc, struct list_head *processing);
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -38,14 +38,34 @@
|
||||
/** Bias for fi->writectr, meaning new writepages must not be sent */
|
||||
#define FUSE_NOWRITE INT_MIN
|
||||
|
||||
/** It could be as large as PATH_MAX, but would that have any uses? */
|
||||
#define FUSE_NAME_MAX 1024
|
||||
/** Maximum length of a filename, not including terminating null */
|
||||
|
||||
/* maximum, small enough for FUSE_MIN_READ_BUFFER*/
|
||||
#define FUSE_NAME_LOW_MAX 1024
|
||||
/* maximum, but needs a request buffer > FUSE_MIN_READ_BUFFER */
|
||||
#define FUSE_NAME_MAX (PATH_MAX - 1)
|
||||
|
||||
/** Number of dentries for each connection in the control filesystem */
|
||||
#define FUSE_CTL_NUM_DENTRIES 5
|
||||
|
||||
/* Frequency (in seconds) of request timeout checks, if opted into */
|
||||
#define FUSE_TIMEOUT_TIMER_FREQ 15
|
||||
|
||||
/** Frequency (in jiffies) of request timeout checks, if opted into */
|
||||
extern const unsigned long fuse_timeout_timer_freq;
|
||||
|
||||
/** Maximum of max_pages received in init_out */
|
||||
extern unsigned int fuse_max_pages_limit;
|
||||
/*
|
||||
* Default timeout (in seconds) for the server to reply to a request
|
||||
* before the connection is aborted, if no timeout was specified on mount.
|
||||
*/
|
||||
extern unsigned int fuse_default_req_timeout;
|
||||
/*
|
||||
* Max timeout (in seconds) for the server to reply to a request before
|
||||
* the connection is aborted.
|
||||
*/
|
||||
extern unsigned int fuse_max_req_timeout;
|
||||
|
||||
/** List of active connections */
|
||||
extern struct list_head fuse_conn_list;
|
||||
@ -378,6 +398,7 @@ struct fuse_io_priv {
|
||||
* FR_FINISHED: request is finished
|
||||
* FR_PRIVATE: request is on private list
|
||||
* FR_ASYNC: request is asynchronous
|
||||
* FR_URING: request is handled through fuse-io-uring
|
||||
*/
|
||||
enum fuse_req_flag {
|
||||
FR_ISREPLY,
|
||||
@ -392,6 +413,7 @@ enum fuse_req_flag {
|
||||
FR_FINISHED,
|
||||
FR_PRIVATE,
|
||||
FR_ASYNC,
|
||||
FR_URING,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -441,7 +463,10 @@ struct fuse_req {
|
||||
|
||||
#ifdef CONFIG_FUSE_IO_URING
|
||||
void *ring_entry;
|
||||
void *ring_queue;
|
||||
#endif
|
||||
/** When (in jiffies) the request was created */
|
||||
unsigned long create_time;
|
||||
};
|
||||
|
||||
struct fuse_iqueue;
|
||||
@ -867,6 +892,9 @@ struct fuse_conn {
|
||||
/* Use pages instead of pointer for kernel I/O */
|
||||
unsigned int use_pages_for_kvec_io:1;
|
||||
|
||||
/* Is link not implemented by fs? */
|
||||
unsigned int no_link:1;
|
||||
|
||||
/* Use io_uring for communication */
|
||||
unsigned int io_uring;
|
||||
|
||||
@ -900,6 +928,9 @@ struct fuse_conn {
|
||||
/** Version counter for evict inode */
|
||||
atomic64_t evict_ctr;
|
||||
|
||||
/* maximum file name length */
|
||||
u32 name_max;
|
||||
|
||||
/** Called on final put */
|
||||
void (*release)(struct fuse_conn *);
|
||||
|
||||
@ -935,6 +966,15 @@ struct fuse_conn {
|
||||
/** uring connection information*/
|
||||
struct fuse_ring *ring;
|
||||
#endif
|
||||
|
||||
/** Only used if the connection opts into request timeouts */
|
||||
struct {
|
||||
/* Worker for checking if any requests have timed out */
|
||||
struct delayed_work work;
|
||||
|
||||
/* Request timeout (in jiffies). 0 = no timeout */
|
||||
unsigned int req_timeout;
|
||||
} timeout;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -1216,6 +1256,9 @@ void fuse_request_end(struct fuse_req *req);
|
||||
void fuse_abort_conn(struct fuse_conn *fc);
|
||||
void fuse_wait_aborted(struct fuse_conn *fc);
|
||||
|
||||
/* Check if any requests timed out */
|
||||
void fuse_check_timeout(struct work_struct *work);
|
||||
|
||||
/**
|
||||
* Invalidate inode attributes
|
||||
*/
|
||||
|
@ -37,6 +37,9 @@ DEFINE_MUTEX(fuse_mutex);
|
||||
static int set_global_limit(const char *val, const struct kernel_param *kp);
|
||||
|
||||
unsigned int fuse_max_pages_limit = 256;
|
||||
/* default is no timeout */
|
||||
unsigned int fuse_default_req_timeout;
|
||||
unsigned int fuse_max_req_timeout;
|
||||
|
||||
unsigned max_user_bgreq;
|
||||
module_param_call(max_user_bgreq, set_global_limit, param_get_uint,
|
||||
@ -979,6 +982,8 @@ void fuse_conn_init(struct fuse_conn *fc, struct fuse_mount *fm,
|
||||
fc->user_ns = get_user_ns(user_ns);
|
||||
fc->max_pages = FUSE_DEFAULT_MAX_PAGES_PER_REQ;
|
||||
fc->max_pages_limit = fuse_max_pages_limit;
|
||||
fc->name_max = FUSE_NAME_LOW_MAX;
|
||||
fc->timeout.req_timeout = 0;
|
||||
|
||||
if (IS_ENABLED(CONFIG_FUSE_PASSTHROUGH))
|
||||
fuse_backing_files_init(fc);
|
||||
@ -1007,6 +1012,8 @@ void fuse_conn_put(struct fuse_conn *fc)
|
||||
|
||||
if (IS_ENABLED(CONFIG_FUSE_DAX))
|
||||
fuse_dax_conn_free(fc);
|
||||
if (fc->timeout.req_timeout)
|
||||
cancel_delayed_work_sync(&fc->timeout.work);
|
||||
if (fiq->ops->release)
|
||||
fiq->ops->release(fiq);
|
||||
put_pid_ns(fc->pid_ns);
|
||||
@ -1257,6 +1264,34 @@ static void process_init_limits(struct fuse_conn *fc, struct fuse_init_out *arg)
|
||||
spin_unlock(&fc->bg_lock);
|
||||
}
|
||||
|
||||
static void set_request_timeout(struct fuse_conn *fc, unsigned int timeout)
|
||||
{
|
||||
fc->timeout.req_timeout = secs_to_jiffies(timeout);
|
||||
INIT_DELAYED_WORK(&fc->timeout.work, fuse_check_timeout);
|
||||
queue_delayed_work(system_wq, &fc->timeout.work,
|
||||
fuse_timeout_timer_freq);
|
||||
}
|
||||
|
||||
static void init_server_timeout(struct fuse_conn *fc, unsigned int timeout)
|
||||
{
|
||||
if (!timeout && !fuse_max_req_timeout && !fuse_default_req_timeout)
|
||||
return;
|
||||
|
||||
if (!timeout)
|
||||
timeout = fuse_default_req_timeout;
|
||||
|
||||
if (fuse_max_req_timeout) {
|
||||
if (timeout)
|
||||
timeout = min(fuse_max_req_timeout, timeout);
|
||||
else
|
||||
timeout = fuse_max_req_timeout;
|
||||
}
|
||||
|
||||
timeout = max(FUSE_TIMEOUT_TIMER_FREQ, timeout);
|
||||
|
||||
set_request_timeout(fc, timeout);
|
||||
}
|
||||
|
||||
struct fuse_init_args {
|
||||
struct fuse_args args;
|
||||
struct fuse_init_in in;
|
||||
@ -1275,6 +1310,7 @@ static void process_init_reply(struct fuse_mount *fm, struct fuse_args *args,
|
||||
ok = false;
|
||||
else {
|
||||
unsigned long ra_pages;
|
||||
unsigned int timeout = 0;
|
||||
|
||||
process_init_limits(fc, arg);
|
||||
|
||||
@ -1338,6 +1374,13 @@ static void process_init_reply(struct fuse_mount *fm, struct fuse_args *args,
|
||||
fc->max_pages =
|
||||
min_t(unsigned int, fc->max_pages_limit,
|
||||
max_t(unsigned int, arg->max_pages, 1));
|
||||
|
||||
/*
|
||||
* PATH_MAX file names might need two pages for
|
||||
* ops like rename
|
||||
*/
|
||||
if (fc->max_pages > 1)
|
||||
fc->name_max = FUSE_NAME_MAX;
|
||||
}
|
||||
if (IS_ENABLED(CONFIG_FUSE_DAX)) {
|
||||
if (flags & FUSE_MAP_ALIGNMENT &&
|
||||
@ -1392,12 +1435,17 @@ static void process_init_reply(struct fuse_mount *fm, struct fuse_args *args,
|
||||
}
|
||||
if (flags & FUSE_OVER_IO_URING && fuse_uring_enabled())
|
||||
fc->io_uring = 1;
|
||||
|
||||
if (flags & FUSE_REQUEST_TIMEOUT)
|
||||
timeout = arg->request_timeout;
|
||||
} else {
|
||||
ra_pages = fc->max_read / PAGE_SIZE;
|
||||
fc->no_lock = 1;
|
||||
fc->no_flock = 1;
|
||||
}
|
||||
|
||||
init_server_timeout(fc, timeout);
|
||||
|
||||
fm->sb->s_bdi->ra_pages =
|
||||
min(fm->sb->s_bdi->ra_pages, ra_pages);
|
||||
fc->minor = arg->minor;
|
||||
@ -1439,7 +1487,8 @@ void fuse_send_init(struct fuse_mount *fm)
|
||||
FUSE_HANDLE_KILLPRIV_V2 | FUSE_SETXATTR_EXT | FUSE_INIT_EXT |
|
||||
FUSE_SECURITY_CTX | FUSE_CREATE_SUPP_GROUP |
|
||||
FUSE_HAS_EXPIRE_ONLY | FUSE_DIRECT_IO_ALLOW_MMAP |
|
||||
FUSE_NO_EXPORT_SUPPORT | FUSE_HAS_RESEND | FUSE_ALLOW_IDMAP;
|
||||
FUSE_NO_EXPORT_SUPPORT | FUSE_HAS_RESEND | FUSE_ALLOW_IDMAP |
|
||||
FUSE_REQUEST_TIMEOUT;
|
||||
#ifdef CONFIG_FUSE_DAX
|
||||
if (fm->fc->dax)
|
||||
flags |= FUSE_MAP_ALIGNMENT;
|
||||
|
@ -13,6 +13,12 @@ static struct ctl_table_header *fuse_table_header;
|
||||
/* Bound by fuse_init_out max_pages, which is a u16 */
|
||||
static unsigned int sysctl_fuse_max_pages_limit = 65535;
|
||||
|
||||
/*
|
||||
* fuse_init_out request timeouts are u16.
|
||||
* This goes up to ~18 hours, which is plenty for a timeout.
|
||||
*/
|
||||
static unsigned int sysctl_fuse_req_timeout_limit = 65535;
|
||||
|
||||
static const struct ctl_table fuse_sysctl_table[] = {
|
||||
{
|
||||
.procname = "max_pages_limit",
|
||||
@ -23,6 +29,24 @@ static const struct ctl_table fuse_sysctl_table[] = {
|
||||
.extra1 = SYSCTL_ONE,
|
||||
.extra2 = &sysctl_fuse_max_pages_limit,
|
||||
},
|
||||
{
|
||||
.procname = "default_request_timeout",
|
||||
.data = &fuse_default_req_timeout,
|
||||
.maxlen = sizeof(fuse_default_req_timeout),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_douintvec_minmax,
|
||||
.extra1 = SYSCTL_ZERO,
|
||||
.extra2 = &sysctl_fuse_req_timeout_limit,
|
||||
},
|
||||
{
|
||||
.procname = "max_request_timeout",
|
||||
.data = &fuse_max_req_timeout,
|
||||
.maxlen = sizeof(fuse_max_req_timeout),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_douintvec_minmax,
|
||||
.extra1 = SYSCTL_ZERO,
|
||||
.extra2 = &sysctl_fuse_req_timeout_limit,
|
||||
},
|
||||
};
|
||||
|
||||
int fuse_sysctl_register(void)
|
||||
|
@ -229,6 +229,9 @@
|
||||
* - FUSE_URING_IN_OUT_HEADER_SZ
|
||||
* - FUSE_URING_OP_IN_OUT_SZ
|
||||
* - enum fuse_uring_cmd
|
||||
*
|
||||
* 7.43
|
||||
* - add FUSE_REQUEST_TIMEOUT
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_FUSE_H
|
||||
@ -264,7 +267,7 @@
|
||||
#define FUSE_KERNEL_VERSION 7
|
||||
|
||||
/** Minor version number of this interface */
|
||||
#define FUSE_KERNEL_MINOR_VERSION 42
|
||||
#define FUSE_KERNEL_MINOR_VERSION 43
|
||||
|
||||
/** The node ID of the root inode */
|
||||
#define FUSE_ROOT_ID 1
|
||||
@ -435,6 +438,8 @@ struct fuse_file_lock {
|
||||
* of the request ID indicates resend requests
|
||||
* FUSE_ALLOW_IDMAP: allow creation of idmapped mounts
|
||||
* FUSE_OVER_IO_URING: Indicate that client supports io-uring
|
||||
* FUSE_REQUEST_TIMEOUT: kernel supports timing out requests.
|
||||
* init_out.request_timeout contains the timeout (in secs)
|
||||
*/
|
||||
#define FUSE_ASYNC_READ (1 << 0)
|
||||
#define FUSE_POSIX_LOCKS (1 << 1)
|
||||
@ -477,11 +482,11 @@ struct fuse_file_lock {
|
||||
#define FUSE_PASSTHROUGH (1ULL << 37)
|
||||
#define FUSE_NO_EXPORT_SUPPORT (1ULL << 38)
|
||||
#define FUSE_HAS_RESEND (1ULL << 39)
|
||||
|
||||
/* Obsolete alias for FUSE_DIRECT_IO_ALLOW_MMAP */
|
||||
#define FUSE_DIRECT_IO_RELAX FUSE_DIRECT_IO_ALLOW_MMAP
|
||||
#define FUSE_ALLOW_IDMAP (1ULL << 40)
|
||||
#define FUSE_OVER_IO_URING (1ULL << 41)
|
||||
#define FUSE_REQUEST_TIMEOUT (1ULL << 42)
|
||||
|
||||
/**
|
||||
* CUSE INIT request/reply flags
|
||||
@ -909,7 +914,8 @@ struct fuse_init_out {
|
||||
uint16_t map_alignment;
|
||||
uint32_t flags2;
|
||||
uint32_t max_stack_depth;
|
||||
uint32_t unused[6];
|
||||
uint16_t request_timeout;
|
||||
uint16_t unused[11];
|
||||
};
|
||||
|
||||
#define CUSE_INIT_INFO_MAX 4096
|
||||
|
Loading…
x
Reference in New Issue
Block a user