root/fs/fuse/req_timeout.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);
}