Something went wrong. Try again.
Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149// 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);}