summaryrefslogtreecommitdiff
path: root/fs/fuse/fuse_dev_i.h
blob: 668c8391d61c033e1d892c44bf7df271c92b14c0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
/* SPDX-License-Identifier: GPL-2.0
 *
 * FUSE: Filesystem in Userspace
 * Copyright (C) 2001-2008  Miklos Szeredi <miklos@szeredi.hu>
 */
#ifndef _FS_FUSE_DEV_I_H
#define _FS_FUSE_DEV_I_H

#include <linux/fuse.h>
#include <linux/types.h>
#include <linux/refcount.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
#include <linux/fs.h>

/* Ordinary requests have even IDs, while interrupts IDs are odd */
#define FUSE_INT_REQ_BIT (1ULL << 0)
#define FUSE_REQ_ID_STEP (1ULL << 1)

struct fuse_arg;
struct fuse_args;
struct fuse_pqueue;
struct fuse_iqueue;

/**
 * enum fuse_req_flag - Request flags
 *
 * @FR_ISREPLY:		set if the request has reply
 * @FR_FORCE:		force sending of the request even if interrupted
 * @FR_BACKGROUND:	request is sent in the background
 * @FR_WAITING:		request is counted as "waiting"
 * @FR_ABORTED:		the request was aborted
 * @FR_INTERRUPTED:	the request has been interrupted
 * @FR_LOCKED:		data is being copied to/from the request
 * @FR_PENDING:		request is not yet in userspace
 * @FR_SENT:		request is in userspace, waiting for an answer
 * @FR_FINISHED:	request is finished
 * @FR_PRIVATE:		request is on private list
 * @FR_ASYNC:		request is asynchronous
 * @FR_URING:		request is handled through fuse-io-uring
 */
enum fuse_req_flag {
	FR_ISREPLY,
	FR_FORCE,
	FR_BACKGROUND,
	FR_WAITING,
	FR_ABORTED,
	FR_INTERRUPTED,
	FR_LOCKED,
	FR_PENDING,
	FR_SENT,
	FR_FINISHED,
	FR_PRIVATE,
	FR_ASYNC,
	FR_URING,
};

/**
 * struct fuse_req - A request to the client
 *
 * .waitq.lock protects the following fields:
 *   - FR_ABORTED
 *   - FR_LOCKED (may also be modified under fpq->lock, tested under both)
 */
struct fuse_req {
	/**
	 * @list: This can be on either pending processing or io lists in
	 * fuse_conn
	 */
	struct list_head list;

	/** @intr_entry: Entry on the interrupts list  */
	struct list_head intr_entry;

	/** @args: Input/output arguments */
	struct fuse_args *args;

	/** @count: refcount */
	refcount_t count;

	/** @flags: Request flags, updated with test/set/clear_bit() */
	unsigned long flags;

	/** @in: The request input header */
	struct {
		/** @in.h: The request input header */
		struct fuse_in_header h;
	} in;

	/** @out: The request output header */
	struct {
		/** @out.h: The request output header */
		struct fuse_out_header h;
	} out;

	/** @waitq: Used to wake up the task waiting for completion of request */
	wait_queue_head_t waitq;

#if IS_ENABLED(CONFIG_VIRTIO_FS)
	/**
	 * @argbuf: virtio-fs's physically contiguous buffer for in and out
	 * args
	 */
	void *argbuf;
#endif

	/** @chan: fuse_chan this request belongs to */
	struct fuse_chan *chan;

#ifdef CONFIG_FUSE_IO_URING
	void *ring_entry;
	void *ring_queue;
#endif
	/** @create_time: When (in jiffies) the request was created */
	unsigned long create_time;
};

/* One forget request */
struct fuse_forget_link {
	struct fuse_forget_one forget_one;
	struct fuse_forget_link *next;
};

/**
 * struct fuse_iqueue_ops - Input queue callbacks
 *
 * Input queue signalling is device-specific.  For example, the /dev/fuse file
 * uses fiq->waitq and fasync to wake processes that are waiting on queue
 * readiness.  These callbacks allow other device types to respond to input
 * queue activity.
 */
struct fuse_iqueue_ops {
	/**
	 * @send_forget: Send one forget
	 */
	void (*send_forget)(struct fuse_iqueue *fiq, struct fuse_forget_link *link);

	/**
	 * @send_interrupt: Send interrupt for request
	 */
	void (*send_interrupt)(struct fuse_iqueue *fiq, struct fuse_req *req);

	/**
	 * @send_req: Send one request
	 */
	void (*send_req)(struct fuse_iqueue *fiq, struct fuse_req *req);

	/**
	 * @release: Clean up when fuse_iqueue is destroyed
	 */
	void (*release)(struct fuse_iqueue *fiq);
};

struct fuse_iqueue {
	/** Connection established */
	unsigned connected;

	/** Lock protecting accesses to members of this structure */
	spinlock_t lock;

	/** Readers of the connection are waiting on this */
	wait_queue_head_t waitq;

	/** The next unique request id */
	u64 reqctr;

	/** The list of pending requests */
	struct list_head pending;

	/** Pending interrupts */
	struct list_head interrupts;

	/** Queue of pending forgets */
	struct fuse_forget_link forget_list_head;
	struct fuse_forget_link *forget_list_tail;

	/** Batching of FORGET requests (positive indicates FORGET batch) */
	int forget_batch;

	/** O_ASYNC requests */
	struct fasync_struct *fasync;

	/** Device-specific callbacks */
	const struct fuse_iqueue_ops *ops;

	/** Device-specific state */
	void *priv;
};

struct fuse_chan {
	/** Lock protecting:
	    - devices
	    - connected
	    - ring
	    - ring->queues[qid]
	 */
	spinlock_t lock;

	/* back pointer: fc->chan->conn == fc */
	struct fuse_conn *conn;

	/** Input queue */
	struct fuse_iqueue iq;

	/** List of device instances belonging to this connection */
	struct list_head devices;

	/** Maximum number of outstanding background requests */
	unsigned max_background;

	/** Number of requests currently in the background */
	unsigned num_background;

	/** Number of background requests currently queued for userspace */
	unsigned active_background;

	/** The list of background requests set aside for later queuing */
	struct list_head bg_queue;

	/** Protects: max_background, num_background, active_background, bg_queue, blocked */
	spinlock_t bg_lock;

	/** Flag indicating that INIT reply has been received. Allocating
	 * any fuse request will be suspended until the flag is set */
	int initialized;

	/** Flag indicating if connection is blocked.  This will be
	    the case before the INIT reply is received, and if there
	    are too many outstading backgrounds requests */
	int blocked;

	/** waitq for blocked connection */
	wait_queue_head_t blocked_waitq;

	/** Connection established, cleared on umount, connection
	    abort and device release */
	unsigned connected;

	/** The number of requests waiting for completion */
	atomic_t num_waiting;

	/** Is interrupt not implemented by fs? */
	bool no_interrupt;

	/* Use io_uring for communication */
	unsigned int io_uring;

	/* Negotiated minor version */
	unsigned int minor;

	/* Maximum write size */
	unsigned int max_write;

	/* Maximum number of pages that can be used in a single request */
	unsigned int max_pages;

	/* Before being installed into fud, contains the preallocated pq array*/
	struct list_head *pq_prealloc;

	/** Connection aborted via sysfs, respond with ECONNABORTED on device I/O */
	bool abort_with_err;

#ifdef CONFIG_FUSE_IO_URING
	/**  uring connection information*/
	struct fuse_ring *ring;
#endif

	/** Only used if the connection opts into request timeouts */
	struct {
		/* Worker for checking if any requests have timed out */
		struct delayed_work work;

		/* Request timeout (in jiffies). 0 = no timeout */
		unsigned int req_timeout;
	} timeout;
};

#define FUSE_PQ_HASH_BITS 8
#define FUSE_PQ_HASH_SIZE (1 << FUSE_PQ_HASH_BITS)

struct fuse_pqueue {
	/** Connection established */
	unsigned connected;

	/** Lock protecting accessess to  members of this structure */
	spinlock_t lock;

	/** Hash table of requests being processed */
	struct list_head *processing;

	/** The list of requests under I/O */
	struct list_head io;
};

/**
 * struct fuse_dev - Fuse device instance
 */
struct fuse_dev {
	/** @ref: Reference count of this object */
	refcount_t ref;

	/** @sync_init: Issue FUSE_INIT synchronously */
	bool sync_init;

	/** @chan: Fuse channel for this device */
	struct fuse_chan *chan;

	/** @pq: Processing queue */
	struct fuse_pqueue pq;

	/** @entry: list entry on fch->devices */
	struct list_head entry;
};

struct fuse_copy_state {
	struct fuse_req *req;
	struct iov_iter *iter;
	struct pipe_buffer *pipebufs;
	struct pipe_buffer *currbuf;
	struct pipe_inode_info *pipe;
	unsigned long nr_segs;
	struct page *pg;
	unsigned int len;
	unsigned int offset;
	bool write:1;
	bool move_folios:1;
	bool is_uring:1;
	struct {
		unsigned int copied_sz; /* copied size into the user buffer */
	} ring;
};

/* fud->chan gets assigned to this value when /dev/fuse is closed */
#define FUSE_DEV_CHAN_DISCONNECTED ((struct fuse_chan *) 1)

/*
 * Lockless access is OK, because fud->chan is set once during mount and is valid
 * until the file is released.
 *
 * fud->chan is set to FUSE_DEV_CHAN_DISCONNECTED only after the containing file is
 * released, so result is safe to dereference in most cases.  Exceptions are:
 * fuse_dev_put() and fuse_fill_super_common().
 */
static inline struct fuse_chan *fuse_dev_chan_get(struct fuse_dev *fud)
{
	/* Pairs with xchg() in fuse_dev_install() */
	return smp_load_acquire(&fud->chan);
}

static inline struct fuse_dev *fuse_file_to_fud(struct file *file)
{
	return file->private_data;
}

static inline struct fuse_dev *__fuse_get_dev(struct file *file)
{
	struct fuse_dev *fud = fuse_file_to_fud(file);

	if (!fuse_dev_chan_get(fud))
		return NULL;

	return fud;
}

void fuse_iqueue_init(struct fuse_iqueue *fiq, const struct fuse_iqueue_ops *ops, void *priv);

struct fuse_dev *fuse_get_dev(struct file *file);

unsigned int fuse_req_hash(u64 unique);
struct fuse_req *fuse_request_find(struct fuse_pqueue *fpq, u64 unique);

void fuse_dev_end_requests(struct list_head *head);
void fuse_request_bg_finish(struct fuse_chan *fch, struct fuse_req *req);

void fuse_copy_init(struct fuse_copy_state *cs, bool write,
			   struct iov_iter *iter);
/*
 * Return the number of bytes in an arguments list
 */
unsigned int fuse_len_args(unsigned int numargs, struct fuse_arg *args);

int fuse_copy_args(struct fuse_copy_state *cs, unsigned int numargs,
		   unsigned int argpages, struct fuse_arg *args,
		   int zeroing);
int fuse_copy_out_args(struct fuse_copy_state *cs, struct fuse_args *args,
		       unsigned int nbytes);
void fuse_dev_queue_forget(struct fuse_iqueue *fiq,
			   struct fuse_forget_link *forget);
void fuse_dev_queue_interrupt(struct fuse_iqueue *fiq, struct fuse_req *req);
bool fuse_remove_pending_req(struct fuse_req *req, spinlock_t *lock);

bool fuse_request_expired(struct fuse_chan *fch, struct list_head *list);

/*
 * Assign a unique id to a fuse request
 */
void fuse_request_assign_unique(struct fuse_iqueue *fiq, struct fuse_req *req);

/*
 * Get the next unique ID for a request
 */
u64 fuse_get_unique(struct fuse_iqueue *fiq);

struct fuse_dev *fuse_dev_alloc_install(struct fuse_chan *fch);
struct fuse_dev *fuse_dev_alloc(void);

int fuse_dev_release(struct inode *inode, struct file *file);

struct list_head *fuse_pqueue_alloc(void);

/*
 * Initialize the fuse processing queue
 */
void fuse_pqueue_init(struct fuse_pqueue *fpq);

/*
 * End a finished request
 */
void fuse_request_end(struct fuse_req *req);

#endif