root/fs/fuse/dev_uring_i.h
/* 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 */