projects/linux·blob master · refs

142 lines · 3.7 KiB

1// SPDX-License-Identifier: GPL-2.0-only
2
3#include "dev.h"
4#include "sysctl.h"
5#include "fuse_dev_i.h"
6#include "dev_uring_i.h"
7
8/* Frequency (in seconds) of request timeout checks, if opted into */
9#define FUSE_TIMEOUT_TIMER_FREQ 15
10
11/* Frequency (in jiffies) of request timeout checks, if opted into */
12static const unsigned long fuse_timeout_timer_freq =
13 secs_to_jiffies(FUSE_TIMEOUT_TIMER_FREQ);
14
15/*
16 * Default timeout (in seconds) for the server to reply to a request
17 * before the connection is aborted, if no timeout was specified on mount.
18 *
19 * Exported via sysctl
20 */
21unsigned int fuse_default_req_timeout;
22
23/*
24 * Max timeout (in seconds) for the server to reply to a request before
25 * the connection is aborted.
26 *
27 * Exported via sysctl
28 */
29unsigned int fuse_max_req_timeout;
30
31bool fuse_request_expired(struct fuse_chan *fch, struct list_head *list)
32{
33 struct fuse_req *req;
34
35 req = list_first_entry_or_null(list, struct fuse_req, list);
36 if (!req)
37 return false;
38 return time_is_before_jiffies(req->create_time + fch->timeout.req_timeout);
39}
40
41static bool fuse_fpq_processing_expired(struct fuse_chan *fch, struct list_head *processing)
42{
43 int i;
44
45 for (i = 0; i < FUSE_PQ_HASH_SIZE; i++)
46 if (fuse_request_expired(fch, &processing[i]))
47 return true;
48
49 return false;
50}
51
52/*
53 * Check if any requests aren't being completed by the time the request timeout
54 * elapses. To do so, we:
55 * - check the fiq pending list
56 * - check the bg queue
57 * - check the fpq io and processing lists
58 *
59 * To make this fast, we only check against the head request on each list since
60 * these are generally queued in order of creation time (eg newer requests get
61 * queued to the tail). We might miss a few edge cases (eg requests transitioning
62 * between lists, re-sent requests at the head of the pending list having a
63 * later creation time than other requests on that list, etc.) but that is fine
64 * since if the request never gets fulfilled, it will eventually be caught.
65 */
66static void fuse_check_timeout(struct work_struct *work)
67{
68 struct delayed_work *dwork = to_delayed_work(work);
69 struct fuse_chan *fch = container_of(dwork, struct fuse_chan, timeout.work);
70 struct fuse_iqueue *fiq = &fch->iq;
71 struct fuse_dev *fud;
72 struct fuse_pqueue *fpq;
73 bool expired = false;
74
75 if (!atomic_read(&fch->num_waiting))
76 goto out;
77
78 spin_lock(&fiq->lock);
79 expired = fuse_request_expired(fch, &fiq->pending);
80 spin_unlock(&fiq->lock);
81 if (expired)
82 goto chan_abort;
83
84 spin_lock(&fch->bg_lock);
85 expired = fuse_request_expired(fch, &fch->bg_queue);
86 spin_unlock(&fch->bg_lock);
87 if (expired)
88 goto chan_abort;
89
90 spin_lock(&fch->lock);
91 if (!fch->connected) {
92 spin_unlock(&fch->lock);
93 return;
94 }
95 list_for_each_entry(fud, &fch->devices, entry) {
96 fpq = &fud->pq;
97 spin_lock(&fpq->lock);
98 if (fuse_request_expired(fch, &fpq->io) ||
99 fuse_fpq_processing_expired(fch, fpq->processing)) {
100 spin_unlock(&fpq->lock);
101 spin_unlock(&fch->lock);
102 goto chan_abort;
103 }
104
105 spin_unlock(&fpq->lock);
106 }
107 spin_unlock(&fch->lock);
108
109 if (fuse_uring_request_expired(fch))
110 goto chan_abort;
111
112out:
113 queue_delayed_work(system_percpu_wq, &fch->timeout.work,
114 fuse_timeout_timer_freq);
115 return;
116
117chan_abort:
118 fuse_chan_abort(fch, false);
119}
120
121static void set_request_timeout(struct fuse_chan *fch, unsigned int timeout)
122{
123 fch->timeout.req_timeout = secs_to_jiffies(timeout);
124 INIT_DELAYED_WORK(&fch->timeout.work, fuse_check_timeout);
125 queue_delayed_work(system_percpu_wq, &fch->timeout.work,
126 fuse_timeout_timer_freq);
127}
128
129void fuse_init_server_timeout(struct fuse_chan *fch, unsigned int timeout)
130{
131 if (!timeout)
132 timeout = fuse_default_req_timeout;
133
134 timeout = min_not_zero(timeout, fuse_max_req_timeout);
135 if (!timeout)
136 return;
137
138 timeout = max(FUSE_TIMEOUT_TIMER_FREQ, timeout);
139
140 set_request_timeout(fch, timeout);
141}
142