blob: befd7456279cb5dd47beb633347d1773169f9987 [file] [log] [blame]
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* GCIP-integrated IIF driver fence.
*
* Copyright (C) 2023 Google LLC
*/
#ifndef __IIF_IIF_FENCE_H__
#define __IIF_IIF_FENCE_H__
#include <linux/kref.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <gcip/iif/iif-manager.h>
#include <gcip/iif/iif.h>
struct iif_fence;
struct iif_fence_ops;
struct iif_fence_poll_cb;
struct iif_fence_all_signaler_submitted_cb;
/*
* The callback which will be called when all signalers have signaled @fence.
*
* It will be called while @fence->signaled_signalers_lock is held and it is safe to read
* @fence->signal_error inside.
*/
typedef void (*iif_fence_poll_cb_t)(struct iif_fence *fence, struct iif_fence_poll_cb *cb);
/*
* The callback which will be called when all signalers have been submitted to @fence.
*
* It will be called while @fence->submitted_signalers_lock is held and it is safe to read
* @fence->all_signaler_submitted_error inside.
*/
typedef void (*iif_fence_all_signaler_submitted_cb_t)(
struct iif_fence *fence, struct iif_fence_all_signaler_submitted_cb *cb);
/*
* The state of a fence object.
* The state transition is
* INITED {-> FILE_CREATED -> FILE_RELEASED} -> RETIRED
* i.e. Sync file creation is optional.
*/
enum iif_fence_state {
/* Initial state. */
IIF_FENCE_STATE_INITIALIZED,
/* The fence ID has been retired. */
IIF_FENCE_STATE_RETIRED,
};
/* The fence object. */
struct iif_fence {
/* IIF manager. */
struct iif_manager *mgr;
/* Fence ID. */
int id;
/* Signaler IP type. */
enum iif_ip_type signaler_ip;
/* The number of total signalers to be submitted. */
uint16_t total_signalers;
/* The number of submitted signalers. */
uint16_t submitted_signalers;
/*
* Protects @submitted_signalers, @all_signaler_submitted_cb_list and
* @all_signaler_submitted_error.
*/
spinlock_t submitted_signalers_lock;
/* The interrupt state before holding @submitted_signalers_lock. */
unsigned long submitted_signalers_lock_flags;
/* The number of signaled signalers. */
uint16_t signaled_signalers;
/* Protects @signaled_signalers, @poll_cb_list and @signal_error. */
spinlock_t signaled_signalers_lock;
/* The number of outstanding waiters. */
uint16_t outstanding_waiters;
/* Protects @outstanding_waiters. */
spinlock_t outstanding_waiters_lock;
/* Reference count. */
struct kref kref;
/* Operators. */
const struct iif_fence_ops *ops;
/* State of this fence object. */
enum iif_fence_state state;
/* List of callbacks which will be called when the fence is signaled. */
struct list_head poll_cb_list;
/* List of callbacks which will be called when all signalers have been submitted. */
struct list_head all_signaler_submitted_cb_list;
/* Will be set to a negative errno if the fence is signaled with an error. */
int signal_error;
/* Will be set to a negative errno if waiting the signaler submission fails. */
int all_signaler_submitted_error;
/* The number of sync_file(s) bound to the fence. */
atomic_t num_sync_file;
};
/* Operators of `struct iif_fence`. */
struct iif_fence_ops {
/*
* Called on destruction of @fence to release additional resources when its reference count
* becomes zero.
*
* This callback is optional.
* Context: normal and in_interrupt().
*/
void (*on_release)(struct iif_fence *fence);
};
/*
* Contains the callback function which will be called when all signalers have signaled the fence.
*
* The callback can be registered to the fence by the `iif_fence_add_poll_callback` function.
*/
struct iif_fence_poll_cb {
/* Node to be added to the list. */
struct list_head node;
/* Actual callback function to be called. */
iif_fence_poll_cb_t func;
};
/*
* Contains the callback function which will be called when all signalers have been submitted.
*
* The callback will be registered to the fence when the `iif_fence_submit_waiter` function fails
* in the submission.
*/
struct iif_fence_all_signaler_submitted_cb {
/* Node to be added to the list. */
struct list_head node;
/* Actual callback function to be called. */
iif_fence_all_signaler_submitted_cb_t func;
/* The number of remaining signalers to be submitted. */
int remaining_signalers;
};
/*
* Initializes @fence which will be signaled by @signaler_ip IP. @total_signalers is the number of
* signalers which must be submitted to the fence. Its initial reference count is 1.
*
* The initialized fence will be assigned an ID which depends on @signaler_ip. Each IP will have at
* most `IIF_NUM_FENCES_PER_IP` number of fences and the assigned fence ID for IP[i] will be one of
* [i * IIF_NUM_FENCES_PER_IP ~ (i + 1) * IIF_NUM_FENCES_PER_IP - 1].
*/
int iif_fence_init(struct iif_manager *mgr, struct iif_fence *fence,
const struct iif_fence_ops *ops, enum iif_ip_type signaler_ip,
uint16_t total_signalers);
/*
* Opens a file which syncs with @fence and returns its FD. The file will hold a reference to
* @fence until it is closed.
*/
int iif_fence_install_fd(struct iif_fence *fence);
/*
* Has @fence know the sync file bound to it is about to be released. This function would try to
* retire the fence if applicable.
*/
void iif_fence_on_sync_file_release(struct iif_fence *fence);
/* Increases the reference count of @fence. */
struct iif_fence *iif_fence_get(struct iif_fence *fence);
/*
* Gets a fence from @fd and increments its reference count of the file pointer.
*
* Returns the fence pointer, if @fd is for IIF. Otherwise, returns a negative errno.
*/
struct iif_fence *iif_fence_fdget(int fd);
/* Decreases the reference count of @fence and if it becomes 0, releases @fence. */
void iif_fence_put(struct iif_fence *fence);
/*
* Submits a signaler. @fence->submitted_signalers will be incremented by 1.
*
* This function can be called in the IRQ context.
*
* Returns 0 if the submission succeeds. Otherwise, returns a negative errno.
*/
int iif_fence_submit_signaler(struct iif_fence *fence);
/*
* Its functionality is the same with the `iif_fence_submit_signaler` function, but the caller
* is holding @fence->submitted_signalers_lock.
*/
int iif_fence_submit_signaler_locked(struct iif_fence *fence);
/*
* Submits a waiter of @ip IP. @fence->outstanding_waiters will be incremented by 1.
* Note that the waiter submission will not be done when not all signalers have been submitted.
* (i.e., @fence->submitted_signalers < @fence->total_signalers)
*
* This function can be called in the IRQ context.
*
* Returns the number of remaining signalers to be submitted (i.e., returning 0 means the submission
* actually succeeded). Otherwise, returns a negative errno if it fails with other reasons.
*/
int iif_fence_submit_waiter(struct iif_fence *fence, enum iif_ip_type ip);
/* Signals @fence. If all signalers have signaled, it will notify polling FDs. */
void iif_fence_signal(struct iif_fence *fence);
/*
* Sets @fence->signal_error to let the user know that @fence has been signaled with an error.
*
* Drivers can supply an optional error status before they signal @fence to indicate that @fence
* was signaled due to an error rather than success.
*/
void iif_fence_set_signal_error(struct iif_fence *fence, int error);
/*
* Returns the signal status of @fence.
*
* Returns 0 if the fence hasn't been signaled yet, 1 if the fence has been signaled without any
* error, or a negative errno if the fence has been completed with an error.
*/
int iif_fence_get_signal_status(struct iif_fence *fence);
/*
* Returns whether all signalers have signaled @fence.
*
* As this function doesn't require to hold any lock, even if this function returns false, @fence
* can be signaled right after this function returns. One should care about this and may not use
* this function directly. This function will be mostly used when iif_sync_file is polling @fence.
*/
bool iif_fence_is_signaled(struct iif_fence *fence);
/* Notifies the driver that a waiter finished waiting on @fence. */
void iif_fence_waited(struct iif_fence *fence);
/*
* Registers a callback which will be called when all signalers of @fence signaled. Once the
* callback is called, it will be automatically unregistered from @fence. The @func can be called
* in the IRQ context.
*
* Returns 0 if succeeded. Otherwise, returns a negative errno on failure. Note that even when
* @fence is already signaled, it won't add the callback and return -EPERM.
*/
int iif_fence_add_poll_callback(struct iif_fence *fence, struct iif_fence_poll_cb *poll_cb,
iif_fence_poll_cb_t func);
/*
* Unregisters the callback from @fence.
*
* Returns true if the callback is removed before @fence is signaled.
*/
bool iif_fence_remove_poll_callback(struct iif_fence *fence, struct iif_fence_poll_cb *poll_cb);
/*
* Registers a callback which will be called when all signalers are submitted for @fence and
* returns the number of remaining signalers to be submitted to @cb->remaining_signalers. Once the
* callback is called, it will be automatically unregistered from @fence.
*
* Returns 0 if succeeded. If all signalers are already submitted, returns -EPERM.
*/
int iif_fence_add_all_signaler_submitted_callback(struct iif_fence *fence,
struct iif_fence_all_signaler_submitted_cb *cb,
iif_fence_all_signaler_submitted_cb_t func);
/*
* Unregisters the callback which is registered by the callback above.
*
* Returns true if the callback is removed before its being called.
*/
bool iif_fence_remove_all_signaler_submitted_callback(
struct iif_fence *fence, struct iif_fence_all_signaler_submitted_cb *cb);
/*
* Returns the number of signalers or waiters information accordingly.
*
* Note that these functions hold required locks internally and read the value. Therefore, the value
* of them can be changed after the function returns. The one must use these functions only for the
* debugging purpose.
*
* These functions can be called in the IRQ context.
*/
int iif_fence_unsubmitted_signalers(struct iif_fence *fence);
int iif_fence_submitted_signalers(struct iif_fence *fence);
int iif_fence_signaled_signalers(struct iif_fence *fence);
int iif_fence_outstanding_waiters(struct iif_fence *fence);
/*
* Returns true if a waiter or a signaler is submittable to @fence.
*
* The caller must hold @fence->submitted_signalers_lock.
*/
bool iif_fence_is_waiter_submittable_locked(struct iif_fence *fence);
bool iif_fence_is_signaler_submittable_locked(struct iif_fence *fence);
/* Holds @fence->submitted_signalers_lock. */
static inline void iif_fence_submitted_signalers_lock(struct iif_fence *fence)
{
spin_lock_irqsave(&fence->submitted_signalers_lock, fence->submitted_signalers_lock_flags);
}
/* Releases @fence->submitted_signalers_lock. */
static inline void iif_fence_submitted_signalers_unlock(struct iif_fence *fence)
{
spin_unlock_irqrestore(&fence->submitted_signalers_lock,
fence->submitted_signalers_lock_flags);
}
#endif /* __IIF_IIF_FENCE_H__ */