DMA FENCE

dma-fence.c/h

结构体(dma-fence.c/h)

struct dma_fence

/**
 * struct dma_fence - software synchronization primitive
 * @refcount: refcount for this fence
 * @ops: dma_fence_ops associated with this fence
 * @rcu: used for releasing fence with kfree_rcu
 * @cb_list: list of all callbacks to call
 * @lock: spin_lock_irqsave used for locking
 * @context: execution context this fence belongs to, returned by
 *           dma_fence_context_alloc()
 * @seqno: the sequence number of this fence inside the execution context,
 * can be compared to decide which fence would be signaled later.
 * @flags: A mask of DMA_FENCE_FLAG_* defined below
 * @timestamp: Timestamp when the fence was signaled.
 * @error: Optional, only valid if < 0, must be set before calling
 * dma_fence_signal, indicates that the fence has completed with an error.
 *
 * the flags member must be manipulated and read using the appropriate
 * atomic ops (bit_*), so taking the spinlock will not be needed most
 * of the time.
 *
 * DMA_FENCE_FLAG_SIGNALED_BIT - fence is already signaled
 * DMA_FENCE_FLAG_TIMESTAMP_BIT - timestamp recorded for fence signaling
 * DMA_FENCE_FLAG_ENABLE_SIGNAL_BIT - enable_signaling might have been called
 * DMA_FENCE_FLAG_USER_BITS - start of the unused bits, can be used by the
 * implementer of the fence for its own purposes. Can be used in different
 * ways by different fence implementers, so do not rely on this.
 *
 * Since atomic bitops are used, this is not guaranteed to be the case.
 * Particularly, if the bit was set, but dma_fence_signal was called right
 * before this bit was set, it would have been able to set the
 * DMA_FENCE_FLAG_SIGNALED_BIT, before enable_signaling was called.
 * Adding a check for DMA_FENCE_FLAG_SIGNALED_BIT after setting
 * DMA_FENCE_FLAG_ENABLE_SIGNAL_BIT closes this race, and makes sure that
 * after dma_fence_signal was called, any enable_signaling call will have either
 * been completed, or never called at all.
 */
struct dma_fence {
	spinlock_t *lock;
	const struct dma_fence_ops *ops;
	/*
	 * We clear the callback list on kref_put so that by the time we
	 * release the fence it is unused. No one should be adding to the
	 * cb_list that they don't themselves hold a reference for.
	 *
	 * The lifetime of the timestamp is similarly tied to both the
	 * rcu freelist and the cb_list. The timestamp is only set upon
	 * signaling while simultaneously notifying the cb_list. Ergo, we
	 * only use either the cb_list of timestamp. Upon destruction,
	 * neither are accessible, and so we can use the rcu. This means
	 * that the cb_list is *only* valid until the signal bit is set,
	 * and to read either you *must* hold a reference to the fence,
	 * and not just the rcu_read_lock.
	 *
	 * Listed in chronological order.
	 */
	union {
		struct list_head cb_list;
		/* @cb_list replaced by @timestamp on dma_fence_signal() */
		ktime_t timestamp;
		/* @timestamp replaced by @rcu on dma_fence_release() */
		struct rcu_head rcu;
	};
	u64 context;
	u64 seqno;
	unsigned long flags;
	struct kref refcount;
	int error;
};

struct dma_fence_ops

/**
 * struct dma_fence_ops - operations implemented for fence
 *
 */
struct dma_fence_ops {
	/**
	 * @use_64bit_seqno:
	 *
	 * True if this dma_fence implementation uses 64bit seqno, false
	 * otherwise.
	 */
	bool use_64bit_seqno;

	/**
	 * @get_driver_name:
	 *
	 * Returns the driver name. This is a callback to allow drivers to
	 * compute the name at runtime, without having it to store permanently
	 * for each fence, or build a cache of some sort.
	 *
	 * This callback is mandatory.
	 */
	const char * (*get_driver_name)(struct dma_fence *fence);

	/**
	 * @get_timeline_name:
	 *
	 * Return the name of the context this fence belongs to. This is a
	 * callback to allow drivers to compute the name at runtime, without
	 * having it to store permanently for each fence, or build a cache of
	 * some sort.
	 *
	 * This callback is mandatory.
	 */
	const char * (*get_timeline_name)(struct dma_fence *fence);

	/**
	 * @enable_signaling:
	 *
	 * Enable software signaling of fence.
	 *
	 * For fence implementations that have the capability for hw->hw
	 * signaling, they can implement this op to enable the necessary
	 * interrupts, or insert commands into cmdstream, etc, to avoid these
	 * costly operations for the common case where only hw->hw
	 * synchronization is required.  This is called in the first
	 * dma_fence_wait() or dma_fence_add_callback() path to let the fence
	 * implementation know that there is another driver waiting on the
	 * signal (ie. hw->sw case).
	 *
	 * This function can be called from atomic context, but not
	 * from irq context, so normal spinlocks can be used.
	 *
	 * A return value of false indicates the fence already passed,
	 * or some failure occurred that made it impossible to enable
	 * signaling. True indicates successful enabling.
	 *
	 * &dma_fence.error may be set in enable_signaling, but only when false
	 * is returned.
	 *
	 * Since many implementations can call dma_fence_signal() even when before
	 * @enable_signaling has been called there's a race window, where the
	 * dma_fence_signal() might result in the final fence reference being
	 * released and its memory freed. To avoid this, implementations of this
	 * callback should grab their own reference using dma_fence_get(), to be
	 * released when the fence is signalled (through e.g. the interrupt
	 * handler).
	 *
	 * This callback is optional. If this callback is not present, then the
	 * driver must always have signaling enabled.
	 */
	bool (*enable_signaling)(struct dma_fence *fence);

	/**
	 * @signaled:
	 *
	 * Peek whether the fence is signaled, as a fastpath optimization for
	 * e.g. dma_fence_wait() or dma_fence_add_callback(). Note that this
	 * callback does not need to make any guarantees beyond that a fence
	 * once indicates as signalled must always return true from this
	 * callback. This callback may return false even if the fence has
	 * completed already, in this case information hasn't propogated throug
	 * the system yet. See also dma_fence_is_signaled().
	 *
	 * May set &dma_fence.error if returning true.
	 *
	 * This callback is optional.
	 */
	bool (*signaled)(struct dma_fence *fence);

	/**
	 * @wait:
	 *
	 * Custom wait implementation, defaults to dma_fence_default_wait() if
	 * not set.
	 *
	 * Deprecated and should not be used by new implementations. Only used
	 * by existing implementations which need special handling for their
	 * hardware reset procedure.
	 *
	 * Must return -ERESTARTSYS if the wait is intr = true and the wait was
	 * interrupted, and remaining jiffies if fence has signaled, or 0 if wait
	 * timed out. Can also return other error values on custom implementations,
	 * which should be treated as if the fence is signaled. For example a hardware
	 * lockup could be reported like that.
	 */
	signed long (*wait)(struct dma_fence *fence,
			    bool intr, signed long timeout);

	/**
	 * @release:
	 *
	 * Called on destruction of fence to release additional resources.
	 * Can be called from irq context.  This callback is optional. If it is
	 * NULL, then dma_fence_free() is instead called as the default
	 * implementation.
	 */
	void (*release)(struct dma_fence *fence);

	/**
	 * @fence_value_str:
	 *
	 * Callback to fill in free-form debug info specific to this fence, like
	 * the sequence number.
	 *
	 * This callback is optional.
	 */
	void (*fence_value_str)(struct dma_fence *fence, char *str, int size);

	/**
	 * @timeline_value_str:
	 *
	 * Fills in the current value of the timeline as a string, like the
	 * sequence number. Note that the specific fence passed to this function
	 * should not matter, drivers should only use it to look up the
	 * corresponding timeline structures.
	 */
	void (*timeline_value_str)(struct dma_fence *fence,
				   char *str, int size);
};

struct dma_fence_cb

用来给dma-fence做唤醒回调的,默认的callback调用了wake_up来唤醒当前task,使用add_callback函数来添加自定义唤醒函数。

/**
 * struct dma_fence_cb - callback for dma_fence_add_callback()
 * @node: used by dma_fence_add_callback() to append this struct to fence::cb_list
 * @func: dma_fence_func_t to call
 *
 * This struct will be initialized by dma_fence_add_callback(), additional
 * data can be passed along by embedding dma_fence_cb in another struct.
 */
struct dma_fence_cb {
	struct list_head node;
	dma_fence_func_t func;
};

函数解析(dma-fence.c/h)

dma_fence_init

函数原型:
void dma_fence_init(struct dma_fence *fence, const struct dma_fence_ops *ops, spinlock_t *lock, u64 context, u64 seqno)
初始化dma-fence。
fence需要自己传入,并且内部成员需要自己构造。

dma_fence_context_alloc

函数原型:
u64 dma_fence_context_alloc(unsigned num)
分配dma-fence的context

dma_fence_signal_locked

函数原型:
int dma_fence_signal_locked(struct dma_fence *fence)
唤醒调用dma_fence_wait睡眠的进程,并调用挂载到当前fence上所有的callback函数。
调用该函数之后,cb_list成员会被timestamp覆盖,内部所有的节点会被销毁重新INIT。

dma_fence_signal

函数原型:
int dma_fence_signal(struct dma_fence *fence)
唤醒调用dma_fence_wait睡眠的进程,并调用挂载到当前fence上所有的callback函数。
该函数会调用dma_fence_signal_locked,除此之外,还会调用dma_fence_begin/end_signalling和spin_lock/unlock_irqsave函数。

dma_fence_wait_timeout

函数原型:
signed long dma_fence_wait_timeout(struct dma_fence *fence, bool intr, signed long timeout)
睡眠直到当前fence被唤醒(signaled)。
如果用户自定义了睡眠函数,则调用自定义睡眠函数,否则调用默认的睡眠函数。
入参intr指示该wait会不会被中断。

dma_fence_release

函数原型:
void dma_fence_release(struct kref *kref)
释放dma-fence。
如果dma-fence没有被signaled,会先signaled当前fence,然后调用dma_fence_free释放fence->rcu,如果用户自定义了release函数,则调用自定义release函数。
驱动不应该调用这个函数,而是应该调用dma_fence_put函数来释放fence。

dma_fence_enable_sw_signaling

函数原型:
void dma_fence_enable_sw_signaling(struct dma_fence *fence)
调用函数__dma_fence_enable_sw_signaling,判断当前fence有没有被signaled,如果没有,调用用户自定义函数: ops->enable_signaling函数,如果使能失败,则signal当前fence。
返回false表示失败或者已经被使能,返回true表示使能成功。
用于在有能力支持hw->hw通知能力的时候,用户自定义通知函数的使能。

dma_fence_add_callback

函数原型:
int dma_fence_add_callback(struct dma_fence *fence, struct dma_fence_cb *cb, dma_fence_func_t func)
构造dma_fence_cb和回调函数,通过这个接口把cb加入到fence的callback列表中。
会先enable_signaling,成功之后再添加callback,否则会加入失败。

dma_fence_get_status

函数原型:
int dma_fence_get_status(struct dma_fence *fence)
获取dma-fence的状态
0:fence还没被signaled
1:fence被signaled,并且没错误发生
负数:fence被signaled,但是有错误发生

dma_fence_remove_callback

函数原型:
bool dma_fence_remove_callback(struct dma_fence *fence, struct dma_fence_cb *cb)
删除callback节点

dma_fence_default_wait

函数原型:
signed long dma_fence_default_wait(struct dma_fence *fence, bool intr, signed long timeout)
dma-fence的默认等待函数,会检查当前进程有没有被信号中断,然后挂起等待,直到被signaled或者被信号中断。
fence可以被信号中断的前提是入参intr为true。

dma_fence_wait_any_timeout

函数原型:
signed long dma_fence_wait_any_timeout(struct dma_fence **fences, uint32_t count, bool intr, signed long timeout, uint32_t *idx)
输入一簇fence,等待这些fence中某一个fence被唤醒,并返回被唤醒fence的idx。

dma_fence_put

函数原型:
static inline void dma_fence_put(struct dma_fence *fence)
fence的引用计数减1,当减到0时,调用release函数释放fence

dma_fence_get

函数原型:
static inline struct dma_fence *dma_fence_get(struct dma_fence *fence)
fence的引用计数加1。

dma_fence_is_signaled

函数原型:
static inline bool dma_fence_is_signaled(struct dma_fence *fence)
判断fence有没有被signaled,因为这里面可能会调用dma_fence_signal函数,所以一般只在资源回收时,put之前调用该函数判断一下,如果fence未被signaled,则不能put。

dma-fence-array.c/h

对dma-fence.c的封装。

结构体(dma-fence-array.c/h)

struct dma_fence_array_cb

/**
 * struct dma_fence_array_cb - callback helper for fence array
 * @cb: fence callback structure for signaling
 * @array: reference to the parent fence array object
 */
struct dma_fence_array_cb {
	struct dma_fence_cb cb;
	struct dma_fence_array *array;
};

struct dma_fence_array

/**
 * struct dma_fence_array - fence to represent an array of fences
 * @base: fence base class
 * @lock: spinlock for fence handling
 * @num_fences: number of fences in the array
 * @num_pending: fences in the array still pending
 * @fences: array of the fences
 * @work: internal irq_work function
 */
struct dma_fence_array {
	struct dma_fence base;

	spinlock_t lock;
	unsigned num_fences;
	atomic_t num_pending;
	struct dma_fence **fences;

	struct irq_work work;
};

函数解析(dma-fence-array.c/h)

dma_fence_array_create

函数原型:
struct dma_fence_array *dma_fence_array_create(int num_fences, struct dma_fence **fences, u64 context, unsigned seqno, bool signal_on_any)
创建一组fences,并返回dma-fence-array指针。
这一组fences由用户创建。
signal_on_any入参为true,则fences里面有一个被signal,array就会被signal,否则当全部fences被signal之后,array才会被signal。
该函数创建了一个base fence作为基础fence,注册到dma-fence框架中。
需要注意的是,因为创建array之后,是将base指针注册到dma-fence框架,框架调用ops函数的入参是struct dma_fence,然后通过container_of获取array指针,所以其实是有一层语意转换的。

dma_fence_array_ops

const struct dma_fence_ops dma_fence_array_ops = {
	.get_driver_name = dma_fence_array_get_driver_name,
	.get_timeline_name = dma_fence_array_get_timeline_name,
	.enable_signaling = dma_fence_array_enable_signaling,
	.signaled = dma_fence_array_signaled,
	.release = dma_fence_array_release,
};
EXPORT_SYMBOL(dma_fence_array_ops);

dma_fence_array_enable_signaling

函数原型:
static bool dma_fence_array_enable_signaling(struct dma_fence *fence)
给fence添加callback函数。
该函数会get base_fence,使ref加1。

dma_fence_array_signaled

函数原型:
static bool dma_fence_array_signaled(struct dma_fence *fence)
通过判断num_pending的大小是否小于等于0来判断该array是否被signaled。

dma_fence_array_release

函数原型:
static void dma_fence_array_release(struct dma_fence *fence)
释放fences数组,并且free创建的fence-array。
会调用dma_fence_free释放base fence。

dma_fence_array_cb_func

函数原型:
static void dma_fence_array_cb_func(struct dma_fence *f, struct dma_fence_cb *cb)
dma-fence-array的回调函数。
在该函数中会递减num_pending,当大于0时,会调用dma_fence_put将base fence的引用计数减1,等于0时会调用irq_work_queue来唤醒下半部。

irq_dma_fence_array_work

函数原型:
static void irq_dma_fence_array_work(struct irq_work *wrk)
callback函数下半部,会调用dma_fence_signal去signal base fence。
在dma_fence_array_cb_func函数中被唤醒。

dma-fence-chain.c

数据结构(dma-fence-chain.c/h)

struct dma_fence_chain

/**
 * struct dma_fence_chain - fence to represent an node of a fence chain
 * @base: fence base class
 * @prev: previous fence of the chain
 * @prev_seqno: original previous seqno before garbage collection
 * @fence: encapsulated fence
 * @lock: spinlock for fence handling
 */
struct dma_fence_chain {
	struct dma_fence base;
	struct dma_fence __rcu *prev;
	u64 prev_seqno;
	struct dma_fence *fence;
	union {
		/**
		 * @cb: callback for signaling
		 *
		 * This is used to add the callback for signaling the
		 * complection of the fence chain. Never used at the same time
		 * as the irq work.
		 */
		struct dma_fence_cb cb;

		/**
		 * @work: irq work item for signaling
		 *
		 * Irq work structure to allow us to add the callback without
		 * running into lock inversion. Never used at the same time as
		 * the callback.
		 */
		struct irq_work work;
	};
	spinlock_t lock;
};

函数解析(dma-fence-chain.c/h)

dma_fence_chain_walk

函数原型:
struct dma_fence *dma_fence_chain_walk(struct dma_fence *fence)
遍历fence-chain并依次判断是否signaled,并且将signaled的节点删掉。

dma_fence_chain_find_seqno

函数原型:
int dma_fence_chain_find_seqno(struct dma_fence **pfence, uint64_t seqno)
遍历fence-chain并signal seqno比指定seqno大的fence节点。

dma_fence_chain_init

函数原型:
void dma_fence_chain_init(struct dma_fence_chain *chain, struct dma_fence *prev, struct dma_fence *fence, uint64_t seqno)
初始化一个fence-chain。
chain最好按照顺序添加,不然该函数会重新指定context和seqno。

dma_fence_chain_ops

const struct dma_fence_ops dma_fence_chain_ops = {
	.use_64bit_seqno = true,
	.get_driver_name = dma_fence_chain_get_driver_name,
	.get_timeline_name = dma_fence_chain_get_timeline_name,
	.enable_signaling = dma_fence_chain_enable_signaling,
	.signaled = dma_fence_chain_signaled,
	.release = dma_fence_chain_release,
};

dma_fence_chain_enable_signaling

函数原型:
static bool dma_fence_chain_enable_signaling(struct dma_fence *fence)
依次给fence-chain上的节点添加callback函数。
callback函数:dma_fence_chain_cb。
会顺便删除signaled的节点。

dma_fence_chain_signaled

函数原型:
static bool dma_fence_chain_signaled(struct dma_fence *fence)
依次判断chain上的节点有没有被signaled,如果有未被signaled的节点,则返回false。
会顺便删除signaled的节点。

dma_fence_chain_release

函数原型:
static void dma_fence_chain_release(struct dma_fence *fence)
遍历chain上所有节点,put所有节点,最后把新建的base节点删掉。

dma_fence_chain_cb

函数原型:
static void dma_fence_chain_cb(struct dma_fence *f, struct dma_fence_cb *cb)
signaling时的回调函数,会唤醒work queue。

函数原型:
static void dma_fence_chain_irq_work(struct irq_work *work)
上述函数唤醒的work queue回调函数,在create chain的时候初始化的。
在这个函数里面会执行dma_fence_chain_enable_signaling函数并signal base fence。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值