| /* SPDX-License-Identifier: GPL-2.0 |
| * |
| * FUSE: Filesystem in Userspace |
| * Copyright (c) 2023-2024 DataDirect Networks. |
| */ |
| |
| #ifndef _FS_FUSE_DEV_URING_I_H |
| #define _FS_FUSE_DEV_URING_I_H |
| |
| #include "fuse_i.h" |
| |
| #ifdef CONFIG_FUSE_IO_URING |
| |
| #define FUSE_URING_TEARDOWN_TIMEOUT (5 * HZ) |
| #define FUSE_URING_TEARDOWN_INTERVAL (HZ/20) |
| |
| enum fuse_ring_req_state { |
| FRRS_INVALID = 0, |
| |
| /* The ring entry received from userspace and it is being processed */ |
| FRRS_COMMIT, |
| |
| /* The ring entry is waiting for new fuse requests */ |
| FRRS_AVAILABLE, |
| |
| /* The ring entry got assigned a fuse req */ |
| FRRS_FUSE_REQ, |
| |
| /* The ring entry is in or on the way to user space */ |
| FRRS_USERSPACE, |
| |
| /* The ring entry is in teardown */ |
| FRRS_TEARDOWN, |
| |
| /* The ring entry is released, but not freed yet */ |
| FRRS_RELEASED, |
| }; |
| |
| /** A fuse ring entry, part of the ring queue */ |
| struct fuse_ring_ent { |
| /* userspace buffer */ |
| struct fuse_uring_req_header __user *headers; |
| void __user *payload; |
| |
| /* the ring queue that owns the request */ |
| struct fuse_ring_queue *queue; |
| |
| /* fields below are protected by queue->lock */ |
| |
| struct io_uring_cmd *cmd; |
| |
| struct list_head list; |
| |
| enum fuse_ring_req_state state; |
| |
| struct fuse_req *fuse_req; |
| }; |
| |
| struct fuse_ring_queue { |
| /* |
| * back pointer to the main fuse uring structure that holds this |
| * queue |
| */ |
| struct fuse_ring *ring; |
| |
| /* queue id, corresponds to the cpu core */ |
| unsigned int qid; |
| |
| /* |
| * queue lock, taken when any value in the queue changes _and_ also |
| * a ring entry state changes. |
| */ |
| spinlock_t lock; |
| |
| /* available ring entries (struct fuse_ring_ent) */ |
| struct list_head ent_avail_queue; |
| |
| /* |
| * entries in the process of being committed or in the process |
| * to be sent to userspace |
| */ |
| struct list_head ent_w_req_queue; |
| struct list_head ent_commit_queue; |
| |
| /* entries in userspace */ |
| struct list_head ent_in_userspace; |
| |
| /* entries that are released */ |
| struct list_head ent_released; |
| |
| /* fuse requests waiting for an entry slot */ |
| struct list_head fuse_req_queue; |
| |
| /* background fuse requests */ |
| struct list_head fuse_req_bg_queue; |
| |
| struct fuse_pqueue fpq; |
| |
| unsigned int active_background; |
| |
| bool stopped; |
| }; |
| |
| /** |
| * Describes if uring is for communication and holds alls the data needed |
| * for uring communication |
| */ |
| struct fuse_ring { |
| /* back pointer */ |
| struct fuse_conn *fc; |
| |
| /* number of ring queues */ |
| size_t nr_queues; |
| |
| /* maximum payload/arg size */ |
| size_t max_payload_sz; |
| |
| struct fuse_ring_queue **queues; |
| |
| /* |
| * Log ring entry states on stop when entries cannot be released |
| */ |
| unsigned int stop_debug_log : 1; |
| |
| wait_queue_head_t stop_waitq; |
| |
| /* async tear down */ |
| struct delayed_work async_teardown_work; |
| |
| /* log */ |
| unsigned long teardown_time; |
| |
| atomic_t queue_refs; |
| |
| bool ready; |
| }; |
| |
| bool fuse_uring_enabled(void); |
| void fuse_uring_destruct(struct fuse_conn *fc); |
| void fuse_uring_stop_queues(struct fuse_ring *ring); |
| void fuse_uring_abort_end_requests(struct fuse_ring *ring); |
| int fuse_uring_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags); |
| void fuse_uring_queue_fuse_req(struct fuse_iqueue *fiq, struct fuse_req *req); |
| bool fuse_uring_queue_bq_req(struct fuse_req *req); |
| bool fuse_uring_remove_pending_req(struct fuse_req *req); |
| bool fuse_uring_request_expired(struct fuse_conn *fc); |
| |
| static inline void fuse_uring_abort(struct fuse_conn *fc) |
| { |
| struct fuse_ring *ring = fc->ring; |
| |
| if (ring == NULL) |
| return; |
| |
| if (atomic_read(&ring->queue_refs) > 0) { |
| fuse_uring_abort_end_requests(ring); |
| fuse_uring_stop_queues(ring); |
| } |
| } |
| |
| static inline void fuse_uring_wait_stopped_queues(struct fuse_conn *fc) |
| { |
| struct fuse_ring *ring = fc->ring; |
| |
| if (ring) |
| wait_event(ring->stop_waitq, |
| atomic_read(&ring->queue_refs) == 0); |
| } |
| |
| static inline bool fuse_uring_ready(struct fuse_conn *fc) |
| { |
| return fc->ring && fc->ring->ready; |
| } |
| |
| #else /* CONFIG_FUSE_IO_URING */ |
| |
| static inline void fuse_uring_destruct(struct fuse_conn *fc) |
| { |
| } |
| |
| static inline bool fuse_uring_enabled(void) |
| { |
| return false; |
| } |
| |
| static inline void fuse_uring_abort(struct fuse_conn *fc) |
| { |
| } |
| |
| static inline void fuse_uring_wait_stopped_queues(struct fuse_conn *fc) |
| { |
| } |
| |
| static inline bool fuse_uring_ready(struct fuse_conn *fc) |
| { |
| return false; |
| } |
| |
| static inline bool fuse_uring_remove_pending_req(struct fuse_req *req) |
| { |
| return false; |
| } |
| |
| static inline bool fuse_uring_request_expired(struct fuse_conn *fc) |
| { |
| return false; |
| } |
| |
| #endif /* CONFIG_FUSE_IO_URING */ |
| |
| #endif /* _FS_FUSE_DEV_URING_I_H */ |