目录
- dma-fence.c/h
- 结构体(dma-fence.c/h)
- 函数解析(dma-fence.c/h)
- dma_fence_init
- dma_fence_context_alloc
- dma_fence_signal_locked
- dma_fence_signal
- dma_fence_wait_timeout
- dma_fence_release
- dma_fence_enable_sw_signaling
- dma_fence_add_callback
- dma_fence_get_status
- dma_fence_remove_callback
- dma_fence_default_wait
- dma_fence_wait_any_timeout
- dma_fence_put
- dma_fence_get
- dma_fence_is_signaled
- dma-fence-array.c/h
- dma-fence-chain.c
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。