文章目录

  • GPU fence
    • command format
    • EOP event
  • DMA fence
    • 数据结构
      • dma-fence
      • amdgpu-fence
      • dma_fence_ops
      • amdgpu_fence_driver
    • API
      • dma_fence_add_callback
      • dma_fence_signal
  • API
    • amdgpu_fence_driver_init_ring
    • amdgpu_fence_driver_start_ring
    • amdgpu_fence_emit
    • amdgpu_fence_process
  • 应用举例
    • 调度任务
    • 中断注册
    • 中断处理
    • 触发回调

GPU fence

  • AMD GPU的fence机制用来产生一个GPU的事件,用于CPU与GPU之间数据同步。当CPU发送一个fence到GPU某个IP的Ring Buffer后,GPU IP硬件会刷新该Ring上的cache。fence机制可以用来保证用户态程序下发的渲染命令被顺序执行,从而保证上层应用程序渲染相关数据的一致性。实现fence机制的硬件基础是AMD GPU提供的EOP(end-of-pipe event)渲染命令,它和普通的渲染命令使用方法相同,作为渲染命令提交到Ring Buffer,然后被GPU执行。

command format

  • CPU与GPU通过Ring Buffer实现渲染命令的提交,渲染命令作为一个packet被提交到Ring Buffer上,AMD规定packet的格式,它被分为两部分,Header和IT_BODY(information body),EOP渲染命令也有如下通用格式,如下图所示:
  • packet的HEADER最高2bit代表packet的类型,AMD将packet分为了3类,分别是0,2,3。每类packet的头部除了高2bit代表类型以外其它字段与packet类型相关,其中type-0和type-2是用作写寄存器的packet,type-3用做发送渲染命令以及一些特殊操作的命令,比如IB和Fence操作。type-3 packet的格式如下:
  • HEADER由4个字段组成,如下:
  1. PREDICATE:待分析
  2. IT_OPCODE:操作码,用于描述具体的渲染命令类型或者特殊操作,比如Indirect Buffer和事件触发操作
  3. COUNT:描述IT_BODY的大小的字段,4字节为单位。它的值为IT_BODY长度减1。
  4. TYPE:packet类型,这里是3
  • AMD驱动中有如下宏定义type-3类型的packet:
#define      PACKET_TYPE3    3
#define     PACKET3(op, n)  ((PACKET_TYPE3 << 30) |    (((op) & 0xFF) << 8) |    ((n) & 0x3FFF) << 16)
#define     PACKET3_EVENT_WRITE_EOP             0x47/* 组装一个操作码为PACKET3_EVENT_WRITE_EOP的type-3类型的HEADER,并设置其IT_BODY长度为5 DWORD */
PACKET3(PACKET3_EVENT_WRITE_EOP, 4)

EOP event

  • EOP(end-of-pipe)作为一个特殊命令,它的主要功能是设置GPU,在flush cache之后,往CPU可以访问的一个内存地址处写入时间戳或者EOP packet中定义的序列号,然后发送中断通知CPU flush动作完成,CPU在中断处理例程中实现fence完成后的相关软件处理,通知所有感兴趣的上层应用。通过这样的方式,CPU可以确认渲染命令是否已经完成,从而实现同步。AMD GPU定义了一个type-3 packet专门用于实现fence机制,packet格式如下:
  • fence的packet包主要用来指示GPU在end-of-pipe之后应该在哪个内存空间,写入什么值,因此packet中有两个最主要的信息是序列号和地址,GPU在end-of-pipe之后会根据这两个信息产生写内存事件或者中断。下面依次介绍每个字段含义:
  1. HEADER:packet包头部,IT_OPCODE为event_write_eop(0x47),COUNT为4。
  2. event initiator:GPU产生事件是写入VGT_EVENT_INITIATOR寄存器的值
  3. event type:事件类型,GPU提供了几种事件类型,包括Cache Flush,Cache Flush And Tnval,Bottom Of Pipe等,通常情况下选择第二种,在cach flush并且使无效之后产生事件
  4. address_lo/address_hi:事件产生时写入的内存地址
  5. interrupt select:选择以何种方式产生事件,包括Send Interrupt Only,Send Interrupt when Write Confirm is received from the MC
  6. data select:产生事件时发送的数据长度
  7. data_lo/data_hi:事件产生时要写的数据
  • AMD fence机制的常用场景是每次CPU提交渲染命令之后,发送一个fence到GPU,同时启动一个定时器每隔一段事件查询一次EOP中给出的内存地址是否被填入了指定的数据。之后,就可以处理其它事情了。在定时器处理例程中如果查询到内存地址被填入指定数据,说明渲染命令已经处理完成。CPU根据这个信息,就可以继续提交新的渲染命令或者处理一些同步相关事情了。下面是amdgpu驱动中一段填充fence信息到Ring Buffer的操作:
amdgpu_ring_write(ring, PACKET3(PACKET3_EVENT_WRITE_EOP, 4));            /* 1 */
amdgpu_ring_write(ring, (EOP_TCL1_ACTION_EN |                           /* 2 */EOP_TC_ACTION_EN |EOP_TC_WB_ACTION_EN |EVENT_TYPE(CACHE_FLUSH_AND_INV_TS_EVENT) |EVENT_INDEX(5)));
amdgpu_ring_write(ring, addr & 0xfffffffc);                             /* 3 */
amdgpu_ring_write(ring, (upper_32_bits(addr) & 0xffff) |                /* 4 */DATA_SEL(write64bit ? 2 : 1) | INT_SEL(int_sel ? 2 : 0));
amdgpu_ring_write(ring, lower_32_bits(seq));                            /* 5 */
amdgpu_ring_write(ring, upper_32_bits(seq));
1. 组装HEADER,将IT_OPCODE和COUNT填入HEADER中
2. 设置event的类型
3. 写入内存地址,因为内存地址是双字为单位,4字节对齐的,因此低2位会设置为0
4. 写入余下的内存地址,同时设置数据发送的位数已经中断产生方式
5. 填入要往内存地址写入的数据

DMA fence

  • 设想这样一个场景,用户态应用往Ring Buffer提交渲染命令,GPU设备着手处理,渲染命令提交完成后返回到用户态空间,GPU异步执行渲染操作。此时用户态应用继续处理其它事务,它下发ioctl命令让显示设备输出刚刚发给GPU渲染的那一帧画面,通常的做法是,这个ioctl命令陷入内核态等待GPU渲染完成,获取渲染buffer的地址,然后将其设置为显示设备的扫描地址,最后返回用户态空间。整个过程可以归纳为向GPU发送渲染数据,然后让显示设备输出,更高效的做法是在内核空间一次性将这两个事情做完,这样就可以节省一次内核态与用户态上下文切换的开销。出现这种问题的本质是内核态缺乏一种机制,没法让两个硬件单元(GPU和display)同步数据,因此只能返回用户态,让用户态程序来负责这两个事情,间接完成数据同步。
  • dma-fence的设计就是用于解决这类问题,它用来实现多个硬件之间共享buffer的同步,同样是上面的场景,当用户态应用往Ring Buffer提交渲染命令后,将GPU处理的渲染buffer共享给显示设备,同时将一个dma-fence关联到这个buffer上并往dma-fence添加一个回调函数,当GPU处理完buffer之后,触发buffer关联的dma-fence上的回调,回调函数中,就可以将渲染buffer的地址设置为显示设备的扫描地址,完成原本用户态应用负责的事情。
  • dma fence有三个状态,一是初始化后的默认状态;二是使能状态(in-fence),通常在注册fence回调函数的时候被设置成这个状态;三是完成状态(out-fence),通常硬件的fence完成后在中断处理中设置。如下:
enum dma_fence_flag_bits {DMA_FENCE_FLAG_SIGNALED_BIT,                           /* 1 */DMA_FENCE_FLAG_TIMESTAMP_BIT,DMA_FENCE_FLAG_ENABLE_SIGNAL_BIT,                       /* 2 */DMA_FENCE_FLAG_USER_BITS, /* must always be last member */
};
1. out-fence状态,标记着fence完成。GPU发送EOP中断后,CPU响应该中断,并将fence的flag设置为这个状态。
2. in-fence状态,标记渲染命令处于fence状态。CPU发送渲染命令之后,使能fence时将flag标记为此状态。

数据结构

dma-fence

struct dma_fence {const struct dma_fence_ops *ops;                                   /* 1 */union {struct list_head cb_list;                                     /* 2 *//* @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;                                                      /* 3 */u64 seqno;                                                           /* 4 */unsigned long flags;                                             /* 5 */struct kref refcount;                                                /* 6 */
}
1. 定义dma-fence等待,释放,完成等操作
2. 维护该dma-fence完成后需要触发的回调函数
3. fence所在上下文,同一个上下文可以有多个fence,通过序列号seqno区分,每个fence有唯一一个序列号不同。在不同上下文中fence序列号可以相
同,fence序列号只在同一个上下文起到区分不同fence的作用。对于amdgpu这种应用场景,一个fence上下文就是一个Ring Buffer,一个fence通常关联
到这个Ring Buffer上的一个任务
4. fence关联的序号,当amdgpu驱动提交一个任务到Ring Buffer上时,就增加这个序列号并分配一个dma-fence关联到该任务,用于跟踪任务是否完成
5. 用于标记该fence的状态,当fence完成时会被标记为DMA_FENCE_FLAG_SIGNALED_BIT,表示fence已经完成,其它对该fence感兴趣的模块会据此执行相应的事务
6. 该fence的引用计数,可以有多个硬件模块或者软件驱动引用同一个fence

amdgpu-fence

  • amdgpu驱动使用内核提供的dma-fence机制实现Host驱动和GPU的同步,amdgpu驱动将dma-fence附加到一个GPU IP的Ring Buffer上,因此封装了一个amdgpu_fence结构体用于将dma-fence与Ring Buffer关联起来。注意,一个Ring Buffer上可以包含若干个fence,因此多个amdgpu_fence的ring成员可能指向同一个环。
struct amdgpu_fence {struct dma_fence base;          /* Ring Buffer上的dma-fence *//* RB, DMA, etc. */struct amdgpu_ring       *ring;  /* amdgpu fence所在的Ring Buffer,amdgpu fence与amdgpu ring是多对一的关系 */
};

dma_fence_ops

  • 内核的dma-fence只提供框架,可以让内核其它模块定制自己的实现,包括如何等待fence完成,fence完成时如何触发相应的回调,fence如何被释放等等,这个定制接口通过定义如下结构体dma_fence_ops来实现
struct dma_fence_ops {bool (*enable_signaling)(struct dma_fence *fence);         /* 使能fence,fence初始化后到完成之前,都处于使能状态 */bool (*signaled)(struct dma_fence *fence);                    /* 向fence发信号,标记fence完成,通常这是在硬件fence完成时中断处理中实现 */signed long (*wait)(struct dma_fence *fence,              /* 等待fence完成 */bool intr, signed long timeout);void (*release)(struct dma_fence *fence);                    /* 释放fence */......
}
/* amdgpu 驱动定制的fence接口 */
static const struct dma_fence_ops amdgpu_fence_ops = {.get_driver_name = amdgpu_fence_get_driver_name,.get_timeline_name = amdgpu_fence_get_timeline_name,.enable_signaling = amdgpu_fence_enable_signaling,.release = amdgpu_fence_release,
};

amdgpu_fence_driver

struct amdgpu_fence_driver {uint64_t         gpu_addr;                                   /* 1 */volatile uint32_t        *cpu_addr;                              /* 2 *//* sync_seq is protected by ring emission lock */uint32_t            sync_seq;                                   /* 3 */atomic_t         last_seq;                                   /* 4 */struct amdgpu_irq_src        *irq_src;                           /* 5 */unsigned         irq_type;struct timer_list      fallback_timer;                         /* 6 */unsigned         num_fences_mask;                            /* 7 */struct dma_fence     **fences;                               /* 8 */
};
1. GPU可访问的内存地址,该地址是GPU产生fence事件时将序列号写入的地址,GPU写gpu_addr地址,CPU从cpu_addr地址读出值
2. fence机制中gpu地址对应的cpu地址,驱动侧通过读取这个地址的内容,与序列号比较,如果相同,说明GPU fence事件产生
3. GPU往gpu_addr写入的序列号,单调递增。初始值为0,CPU每执行一个job,就会往GPU ring上发送一个fence,序列号就加1。fence的序列号用来
区分不同job。假设CPU往ring buffer上先后放了两个job,此时产生了GPU产生了end-of-pipe中断,CPU的中断处理函数例程中可以通过序列号区分是哪
个job对应的渲染命令被GPU处理完成。
4. 从cpu_addr中取出的GPU写入的序列号
5. 待分析
6. 定时器,用于每隔一段事件查询是否有fence被通知(signaled)
7. fence个数掩码,用于查询fences数组中的dma_fence,使其不越界
8. fences数组,调度器每提交一个job,相应地会增加fence的序列号,同时会生成一个dma_fence对象放到fences数组中。

API

  • 对于amdgpu来说,一个Ring Buffer就是一个共享buffer,它上面可以关联dma-fence,当GPU执行完Ring Buffer上的渲染命令时,通过附加在buffer上面的dma-fence,就可以通知其它硬件模块并触发相应回调函数

dma_fence_add_callback

  • fence被发送后,发送fence的线程就可以转而去处理其它事务,当fence完成时会触发相应的回调函数,一个dmp-fence可以注册多个回调函数,但一个回调函数只能注册到一个dmap-fence上,即dma-fence与callback时一对多的关系。amdgpu驱动中fence的回调函数注册在dma_fence_add_callback中实现,如下:
int dma_fence_add_callback(struct dma_fence *fence, struct dma_fence_cb *cb,dma_fence_func_t func)
{if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags)) {     /* 1 */INIT_LIST_HEAD(&cb->node);return -ENOENT;}if (__dma_fence_enable_signaling(fence)) {                      /* 2 */cb->func = func;list_add_tail(&cb->node, &fence->cb_list);}......
}
1. 检测fence是否已经完成,如果已经完成,直接返回,调用者跳过回调函数的注册,直接执行就可以了
2. 判断fence是否注册了触发fence完成的函数,如果有,满足条件,将回调函数添加到dma-fence的回调函数链表中。

dma_fence_signal

dma_fence_signaldma_fence_signal_locked
int dma_fence_signal_locked(struct dma_fence *fence)
{if (unlikely(test_and_set_bit(DMA_FENCE_FLAG_SIGNALED_BIT,     /* 1 */&fence->flags)))return -EINVAL;list_for_each_entry_safe(cur, tmp, &cb_list, node) {           /* 2 */INIT_LIST_HEAD(&cur->node);cur->func(fence, cur);}......
}
1. 设置fence状态标记为SIGNEALED,其余接口通过查询此状态标记检查fence是否处于out-fence状态
2. 对fence感兴趣的其它模块通过向cb_list添加回调函数,可以让fence在完成后执行该回调。这里就是fence完成后的回调

API

amdgpu_fence_driver_init_ring

  • 初始化一个GPU IP上Buffer Ring的fence_driver,每个Buffer Ring上维护这一个fence driver,它用来记录提交到Ring Buffer上的job的完成情况。为什么要用这个才能记录?因为CPU提交渲染到Ring Buffer让GPU执行是异步,只能通过fence机制来确认job的完成情况。fence driver为上层渲染命令的同步提供底层基础,否则上层是无法知道渲染命令是否完成,当上层应用下发的渲染命令前后有依赖时,无法保证顺序
int amdgpu_fence_driver_init_ring(struct amdgpu_ring *ring,unsigned num_hw_submission)
{       ring->fence_drv.sync_seq = 0;                                               /* 1 */atomic_set(&ring->fence_drv.last_seq, 0);ring->fence_drv.initialized = false;                                         /* 2 */timer_setup(&ring->fence_drv.fallback_timer, amdgpu_fence_fallback, 0);       /* 3 */ring->fence_drv.num_fences_mask = num_hw_submission * 2 - 1;             /* 4 */ring->fence_drv.fences = kcalloc(num_hw_submission * 2, sizeof(void *),      /* 5 */GFP_KERNEL);......
}
1. 将当前序列号和上一次的序列号初始化为0,sync_seq在真正发送fence的时候会加1,因此sync_seq从1开始,last_seq从0开始。
2. fence driver是否启动,这里设置为false,fence driver在启动start ring接口中设置为true
3. 设置定时器,该定时器用于查询是否有完成的fence。除了GPU IP核中断例程中会处理完成的fence外,这里注册的定时器回调函数也会周期性查询fence是否完成。
4. 设置fence个数的掩码,amd gpu调度器中允许同时有num_hw_submission个任务提交到Ring Buffer。调度器每提交一个任务,至少要发送一个fence用于跟踪当前任务是否完成。同时在提交任务之前,可能刷GPU的缓存,这时也会发送一个fence用于跟踪当前缓存是否刷新完成,因此一个任务最多可能会有2个fence发送。所以设置的fence个数掩码为num_hw_submission * 2 - 1。注意,num_hw_submission是2的幂级数。
5. 为fence driver的fences结构体分配空间,个数为num_hw_submission的2倍

amdgpu_fence_driver_start_ring

  • fence driver的初始化主要完成基本成员的赋初值,但关键的gpu和cpu地址没有设置,这个设置在start ring中完成。
int amdgpu_fence_driver_start_ring(struct amdgpu_ring *ring,struct amdgpu_irq_src *irq_src,unsigned irq_type)
{       ring->fence_drv.cpu_addr = &adev->wb.wb[ring->fence_offs];                        /* 1 */ring->fence_drv.gpu_addr = adev->wb.gpu_addr + (ring->fence_offs * 4);amdgpu_fence_write(ring, atomic_read(&ring->fence_drv.last_seq));                /* 2 */amdgpu_irq_get(adev, irq_src, irq_type);ring->fence_drv.irq_src = irq_src;ring->fence_drv.irq_type = irq_type;ring->fence_drv.initialized = true;                                                /* 3 */......
}
1. 设置fence driver的gpu和cpu地址
2. 将last_seq序列号写入fence driver的cpu地址中,由于last_seq初始值为0,因此cpu地址中读出的序列号为0,在提交任务时,当前序列号sync_seq会
先加1再发送fence,中断处理函数和定时器函数中通过判断sync_seq与last_seq是否相等,来判断是否有任务在执行或者有flush正在刷新
3. 将fence driver的初始化设置为true

amdgpu_fence_emit

  • 向GPU发送一个fence,具体实现是往Ring Buffer上填入fence内容。真正地发送在设置Ring Buffer写偏移时触发。
int amdgpu_fence_emit(struct amdgpu_ring *ring, struct dma_fence **f,unsigned flags)
{struct amdgpu_fence *fence;fence = kmem_cache_alloc(amdgpu_fence_slab, GFP_KERNEL);                       /* 1 */seq = ++ring->fence_drv.sync_seq;                                              /* 2 */fence->ring = ring;                                                              /* 3 */dma_fence_init(&fence->base, &amdgpu_fence_ops,&ring->fence_drv.lock,adev->fence_context + ring->idx,seq);amdgpu_ring_emit_fence(ring, ring->fence_drv.gpu_addr,                         /* 4 */seq, flags | AMDGPU_FENCE_FLAG_INT);ptr = &ring->fence_drv.fences[seq & ring->fence_drv.num_fences_mask];         /* 5 */if (unlikely(rcu_dereference_protected(*ptr, 1))) {                          struct dma_fence *old;old = dma_fence_get_rcu_safe(ptr);dma_fence_wait(old, false);                                                    /* 6 */dma_fence_put(old);                                                          /* 7 */}rcu_assign_pointer(*ptr, dma_fence_get(&fence->base));                           /* 8 */......
}
1. 为amdgpu-fence结构体分配内存
2. 增加序列号,每发送一次fence,序列号自增一次
3. 设置fence所在的环
4. 往Ring Buffer中填入fence信息,即之前介绍的EOP packet
5. 取出序列号在fence数组中对应元素,判断是否为空,如果为空,将该元素指向新分配的fence。通常情况下,fence数组对应位置不会存在fence,如果存在,我们认为它是之前未完成的fence,我们等待fence完成,然后再释放老的fence。
6. 等到老的fence完成
7. 完成后释放老的fence,如果是最后一个fence的引用被释放,则删除老的fence
8. 设置fence数组对应元素指向即将发送的新fence。

amdgpu_fence_process

  • 当GPU完成渲染任务后,会触发EOP中断,amdgpu在中断处理函数中会比较EOP packet中填入的序列号和GPU往GPU地址空间写入的值是否相同,如果相同,说明GPU完成了渲染操作或者完成的flush cache的操作,过程如下:
bool amdgpu_fence_process(struct amdgpu_ring *ring)
{struct amdgpu_fence_driver *drv = &ring->fence_drv;uint32_t seq, last_seq;do {last_seq = atomic_read(&ring->fence_drv.last_seq);                               /* 1 */seq = amdgpu_fence_read(ring);                                              } while (atomic_cmpxchg(&drv->last_seq, last_seq, seq) != last_seq);                /* 2 */if (unlikely(seq == last_seq))                                                     /* 3 */return false;last_seq &= drv->num_fences_mask;                                                   /* 4 */seq &= drv->num_fences_mask;do {struct dma_fence *fence, **ptr;++last_seq;                                                                     /* 5 */last_seq &= drv->num_fences_mask;ptr = &drv->fences[last_seq];/* There is always exactly one thread signaling this fence slot */fence = rcu_dereference_protected(*ptr, 1);RCU_INIT_POINTER(*ptr, NULL);dma_fence_signal(fence);                                    /* 6 */dma_fence_put(fence);} while (last_seq != seq);......
}
1. 取出上一次fence的序列号last_seq和这一次fence的序列号seq
2. 用这一次fence的序列号更新上次任务的序列号,原子操作
3. 如果两次fence的序列号相等,说明当前没有fence完成,直接返回false,表示fence还处在未完成阶段
4. 计算序号在fences数组中的索引
5. 逐次增加last_seq,从fences数组中取出对应的dma-fence,更新fence的状态,最终触发fence上的回调,直到last_seq等于seq。在fence完成的回调
处理中,多个fence可能合并一次性处理。
6. 更新fence的状态为完成(signaled)
7. 释放fence

应用举例

  • GPU调度器通过fence机制为上层应用提供了不同硬件之间数据的同步方法,GPU调度器本身也通过fence机制跟踪任务的完成情况。下面通过分析GPU调度器执行任务的过程,进一步理解fence机制

调度任务

  • 调度器按从高到低的顺序,依次从对应的运行队列中选择合适的调度实体,取出调度实体的任务,将任务关联IB包含的渲染命令提交到Ring Buffer上。调度任务的执行在drm_sched_main函数中实现,GPU调度器利用fence跟踪渲染任务是否执行完,还可以利用fence机制注册回调函数,让GPU渲染任务执行完成后触发任务相关的收尾工作。处理其中fence相关的操作如下:
static int drm_sched_main(void *param)
{while (!kthread_should_stop()) {......fence = sched->ops->run_job(sched_job);                           /* 1 */     if (!IS_ERR_OR_NULL(fence)) {r = dma_fence_add_callback(fence, &sched_job->cb,          /* 2 */drm_sched_process_job);if (r == -ENOENT)drm_sched_process_job(fence, &sched_job->cb);           /* 3 */dma_fence_put(fence);                                        /* 4 */}......}
}
1. 执行渲染任务,该动作的实质是将渲染命令放到Ring Buffer上,然后让GPU异步处理,run_job返回一个fence,上层通过此fence注册回调函数
2. 往fence上注册回调函数,当该fence完成时,执行drm_sched_process_job函数
3. 如果此时fence已经完成,说明GPU已经将渲染任务完成,不再需要注册回调,直接运行回调函数即可
4. 释放fence

中断注册

  • 渲染任务完成后,GPU硬件模块产生中断,CPU响应中断后触发中断服务例程,理论上上面的回调函数源头是中断服务例程触发的。我们跟踪一下GPU中断服务例程的注册,以GFX模块为例。中断注册在amdgpu驱动中完成,大致流程如下:
amdgpu_initpci_register_driver(&amdgpu_kms_pci_driver)amdgpu_kms_pci_driver.probe    <=>  amdgpu_pci_probeamdgpu_driver_load_kmsamdgpu_device_initamdgpu_device_ip_early_initvi_set_ip_blockscase CHIP_TONGA:amdgpu_device_ip_block_add(adev, &gfx_v8_0_ip_block)
const struct amdgpu_ip_block_version gfx_v8_0_ip_block =
{.......funcs = &gfx_v8_0_ip_funcs,
};
static const struct amd_ip_funcs gfx_v8_0_ip_funcs = {.name = "gfx_v8_0",.early_init = gfx_v8_0_early_init,......
}
gfx_v8_0_early_initgfx_v8_0_set_irq_funcs(adev);adev->gfx.eop_irq.funcs = &gfx_v8_0_eop_irq_funcs;static const struct amdgpu_irq_src_funcs gfx_v8_0_eop_irq_funcs = {.set = gfx_v8_0_set_eop_interrupt_state,     /* 1 */.process = gfx_v8_0_eop_irq,                    /* 2 */
};
1. 设置中断状态的操作,和具体的硬件编程接口有关对于amdgpu来说,主要时使能中断和禁止中断这两个功能
2. EOP中断例程处理函数

中断处理

  • GPU的中断处理通过drm_driver注册,drm_driver提供统一的中断框架用于实现GPU中断处理例程的注册,内核公共的中断处理函数最终会调用到drm_driver的中断处理函数irq_handler,如下:
static struct drm_driver kms_driver = {.irq_handler = amdgpu_irq_handler,......
}
  • 当GPU硬件产生中断并提交到CPU后,CPU根据中断向量表跳转到对应的服务例程,最终调用amdgpu_irq_handler,看下其具体流程:
amdgpu_irq_handleramdgpu_ih_processamdgpu_irq_dispatchsrc->funcs->process(adev, src, &entry)   <=>  gfx_v8_0_eop_irqamdgpu_fence_processdma_fence_signaldma_fence_signal_locked

触发回调

  • 从上面的流程中可以看到,渲染任务执行完了会触发drm_sched_process_job回调,该函数会将调度相关的fence设置为完成,从而链式地触发其它监听模块的回调,具体如下:
static void drm_sched_process_job(struct dma_fence *f, struct dma_fence_cb *cb)
{struct drm_sched_job *s_job = container_of(cb, struct drm_sched_job, cb);struct drm_sched_fence *s_fence = s_job->s_fence;                        /* 获取job关联的dma-fence */dma_fence_get(&s_fence->finished);                                        /* 引用finished dma-fence */drm_sched_fence_finished(s_fence);                                        /* 通知监听finished dma-fence的其它模块,job已经处理完成,可以触发对应回调了 */dma_fence_put(&s_fence->finished);                                        /* 释放finished dma-fence */
}

AMD GPU任务调度(3) —— fence机制相关推荐

  1. AMD GPU内存管理(1):概览

    参考内核版本:Linux-6.1.8 HMM 待更新...... dumb buffer create/map 在AMDGPU的Graphics业务中,用到了GEM(Graphics Executio ...

  2. Android SurfaceFlinger中Fence机制--个人理解整理

    1 Fence 是什么? Fence中文是栅栏/围墙的意思,理解成分界/界限的东西.android中的一个资源锁机制.(i.e. a kind of memory barrier) 下面链接是engl ...

  3. android fence机制,Android中的GraphicBuffer同步机制-Fence

    Fence是一种同步机制,在Android里主要用于图形系统中GraphicBuffer的同步.那它和已有同步机制相比有什么特点呢?它主要被用来处理跨硬件的情况.尤其是CPU.GPU和HWC之间的同步 ...

  4. Ubuntu 安装 AMD GPU 驱动

    基于环境 Ubuntu18.04 AMD-RX580 显卡 AMD 官网下载驱动 https://www.amd.com/en/support 将驱动上传到 Ubuntu 系统并解压 $ cd ~/D ...

  5. AMD GPU驱动,ROCM,Pytorch安装教程(A卡6700xt)

    我用的操作系统为ubuntu20.04,其他系统应该类似,只是命令稍有不同. 安装AMD GPU驱动 AMD驱动下载地址:https://www.amd.com/en/support/kb/relea ...

  6. AMD GPU的断点指令

    很多人都知道x86 CPU的断点指令,即著名的INT 3,机器码为0xCC.在Nvidia的GPU中,比如著名的伏特微架构,也有一条断点指令,叫BPT,是Breakpoint的缩写. 那么,在AMD ...

  7. 【Stable Diffusion/NovelAI Diffusion的AMD GPU加速推理探索】

    测试机子配置: 1:AMD RX6600(显存8g)+i5 12600KF 16g内存 (台式机) 2:RTX 3070 laptop(显存8g)+i7 10870H 32g内存 (HP暗夜精灵笔记本 ...

  8. fence机制 linux_集群 Ricciluci Fence机制

    一 Ricci&&luci Server1和server4做同样的操作, (1)配置yum源 [HighAvailability] name=HighAvailability base ...

  9. android fence机制,Android4.4 fence机制分析

    在任何一个系统中,无可避免的都会跟各种buffers打交道,最经典的模式就是消费-生产者模式,一个独立的buffer在它们之间的交换等操作都需要一个机制来控制每个buffer的"生命周期&q ...

最新文章

  1. 【专访英特尔高级首席工程师戴金权】普通数据工程师,如何玩转深度学习?
  2. 会用python把linux命令写一遍的人,进大厂有多容易?
  3. IPad开发之有帮助的开发工具
  4. 判断文件是否损坏_称重传感器好坏的判断方法,看完秒懂!
  5. 小程序分享到朋友圈_如何给小程序添加分享朋友圈
  6. java 持续集成工具_Jenkins-Jenkins(持续集成工具)下载 v2.249.2官方版--pc6下载站
  7. 软件工具组功能逆向工程设想
  8. 字符级中文文本分类-CNN基于TensorFlow实现
  9. mysql 分割后循环,mysql实现字符串分割SPLIT函数的四种方法
  10. FreeRTOS 教程指南 学习笔记 第五章 软件计时器
  11. 卫星地图-resolution和scale解析
  12. Unity LOGO流光效果
  13. VBA--类模块学习
  14. Tor出现需要控制密码的解决办法
  15. FormulaR1C1是EXCEL中单元格公式输入方法
  16. 如何设置阿里云安全组?开放和关闭端口很简单
  17. Python经典练习题——求水仙花数
  18. 一步拿下抖音+微信生态圈,让内容变现再次加速
  19. 71java并发编程不得不知道的几件事
  20. 【调剂经验】19年一战东南大学计算机专硕调剂蒙纳士,初复试经验调剂经验分享!...

热门文章

  1. Telephony模块中的Log使用
  2. linux的 vi的各种命令(超级好用)
  3. Real-time hatching報告+實現代碼和效果
  4. ES搜索--轻量搜索语法
  5. oracle复制另一个字段,【学习笔记】Oracle存储过程 表中列不同时动态复制表中数据到另一个表中...
  6. linux iw 命令
  7. 经典批处理实现自动关机.BAT
  8. Android平板电脑刷机包简单解释
  9. Python案例笔记 | 用python群发邮件
  10. NNs(Neural Networks,神经网络)和Polynomial Regression(多项式回归)等价性之思考,以及深度模型可解释性原理研究与案例...