mirror of
https://github.com/torvalds/linux
synced 2026-07-22 10:10:47 +09:00
- rename fuse_abort_conn() to fuse_chan_abort(), pass fuse_chan pointer instead of fuse_conn - pass an abort_with_err argument that tells fuse_dev_(read|write) to return with ECONNABORTED instead of ENODEV - move fc->aborted to fch->abort_with_err - rename fuse_wait_aborted() to fuse_chan_wait_aborted() Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
149 lines
3.9 KiB
C
149 lines
3.9 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
#include "dev.h"
|
|
#include "sysctl.h"
|
|
#include "fuse_dev_i.h"
|
|
#include "dev_uring_i.h"
|
|
|
|
/* 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 */
|
|
static const unsigned long fuse_timeout_timer_freq =
|
|
secs_to_jiffies(FUSE_TIMEOUT_TIMER_FREQ);
|
|
|
|
/*
|
|
* Default timeout (in seconds) for the server to reply to a request
|
|
* before the connection is aborted, if no timeout was specified on mount.
|
|
*
|
|
* Exported via sysctl
|
|
*/
|
|
unsigned int fuse_default_req_timeout;
|
|
|
|
/*
|
|
* Max timeout (in seconds) for the server to reply to a request before
|
|
* the connection is aborted.
|
|
*
|
|
* Exported via sysctl
|
|
*/
|
|
unsigned int fuse_max_req_timeout;
|
|
|
|
bool fuse_request_expired(struct fuse_chan *fch, 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 + fch->timeout.req_timeout);
|
|
}
|
|
|
|
static bool fuse_fpq_processing_expired(struct fuse_chan *fch, struct list_head *processing)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < FUSE_PQ_HASH_SIZE; i++)
|
|
if (fuse_request_expired(fch, &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.
|
|
*/
|
|
static void fuse_check_timeout(struct work_struct *work)
|
|
{
|
|
struct delayed_work *dwork = to_delayed_work(work);
|
|
struct fuse_chan *fch = container_of(dwork, struct fuse_chan, timeout.work);
|
|
struct fuse_iqueue *fiq = &fch->iq;
|
|
struct fuse_dev *fud;
|
|
struct fuse_pqueue *fpq;
|
|
bool expired = false;
|
|
|
|
if (!atomic_read(&fch->num_waiting))
|
|
goto out;
|
|
|
|
spin_lock(&fiq->lock);
|
|
expired = fuse_request_expired(fch, &fiq->pending);
|
|
spin_unlock(&fiq->lock);
|
|
if (expired)
|
|
goto chan_abort;
|
|
|
|
spin_lock(&fch->bg_lock);
|
|
expired = fuse_request_expired(fch, &fch->bg_queue);
|
|
spin_unlock(&fch->bg_lock);
|
|
if (expired)
|
|
goto chan_abort;
|
|
|
|
spin_lock(&fch->lock);
|
|
if (!fch->connected) {
|
|
spin_unlock(&fch->lock);
|
|
return;
|
|
}
|
|
list_for_each_entry(fud, &fch->devices, entry) {
|
|
fpq = &fud->pq;
|
|
spin_lock(&fpq->lock);
|
|
if (fuse_request_expired(fch, &fpq->io) ||
|
|
fuse_fpq_processing_expired(fch, fpq->processing)) {
|
|
spin_unlock(&fpq->lock);
|
|
spin_unlock(&fch->lock);
|
|
goto chan_abort;
|
|
}
|
|
|
|
spin_unlock(&fpq->lock);
|
|
}
|
|
spin_unlock(&fch->lock);
|
|
|
|
if (fuse_uring_request_expired(fch))
|
|
goto chan_abort;
|
|
|
|
out:
|
|
queue_delayed_work(system_percpu_wq, &fch->timeout.work,
|
|
fuse_timeout_timer_freq);
|
|
return;
|
|
|
|
chan_abort:
|
|
fuse_chan_abort(fch, false);
|
|
}
|
|
|
|
static void set_request_timeout(struct fuse_chan *fch, unsigned int timeout)
|
|
{
|
|
fch->timeout.req_timeout = secs_to_jiffies(timeout);
|
|
INIT_DELAYED_WORK(&fch->timeout.work, fuse_check_timeout);
|
|
queue_delayed_work(system_percpu_wq, &fch->timeout.work,
|
|
fuse_timeout_timer_freq);
|
|
}
|
|
|
|
void fuse_init_server_timeout(struct fuse_chan *fch, 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(fch, timeout);
|
|
}
|
|
|