148 lines · 3.9 KB · C
RawDownload
// 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);}