• DRM中render-node编号的分配


    DRM系统

    DRM是direct rendering manager的简称。DRM是linux kernel中与负责video cards功能的GPU打交道的子系统。DRM给出了一组API,可以供用户程序来发送命令和数据给GPU设备从而来控制比如display、render等功能。

    render-node由来

    在以前,DRM子系统中给每个DRM device注册的device-node就是:/dev/dri/cardX ,通过该节点来作mode-setting和rendering的控制。后来发现这么做有问题:

    • mode-setting和rendering是通过同一个文件节点node来控制的

    • 单卡的mode-setting资源不能在多个graphics-servers之间切分使用

    • 在多cards之间共享display-controller实现过于复杂

    然后就有了一些改进来解决这些问题。最终是mode-setting 和 render节点分家

    render node

    render-node架构大概是2009年左右提出来的。站在用户程序角度来看,render node的作用是用来加速computing和rendering,render node可以通过 /dev/dri/renderDX 被访问,并且提供了基本的DRM rendering interface。与 /dev/dri/cardX 节点相比,/dev/dri/renderDX 少了一些特性:

    • 没有mode-setting(KMS)ioctls功能

    • 用dma-buf替换掉gem-flink(非安全的)

    • 不再需要DRM-auth认证

    • 不再支持pre-KMS DRM-API

    这样一来,当应用程序需要hardware-acclerated rendering、访问GPGPU、offscreen rendering等时,就不需要通过DRI或者wl_drm来访问graphics-server,而是直接打开某个render node就开始使用即可。对于render node的访问权限控制则通过标注的file-system modes来控制了。

    render-node并没有提供新的API,它们只是将原有的DRM-API分出了一部分到一个新的device-node,原来的node也保留了下来用于如mode-setting等控制。

    render-node也没有和任何一个card进行绑定,它是由原有node的同一个driver创建的,所以尝试在原有node和render-node之间进行connect连接通信没有意义。当应用程序需要和graphics-server进行通信时,可以通过dma-buf

    mode-setting node

    虽然从原有的node中分离出一个render-node,简化了应用程序的访问,但对于mode-setting程序的访问没有简化。当一个graphics-server想要编程一个display-controller时,它需要是给定card的DRM-Master,可以通过drmSetMaster()接口来获得身份,但同时只能有一个DRM-Master,而且必须是由CAP_SYS_ADMIN特权的程序才能成为DRM-Master,这会带来问题:

    • 不能以非root权限运行XServer

    • 不能在同一个card上使用两个不同的XServer来控制两个不同的独立的显示器

    首先想到的解决办法就是分离出mode-setting node,类似render-node的方式,/dev/dri/modesetD1 和 /dev/dri/modesetD2 节点,来分割KMS CRTC和Connector资源。

    另一种方法是将所有的mode-setting资源绑定到一个DRM-Master对象,然后谁要访问mode-setting资源就可以通过访问该DRM-Master对象来实现。

    DRM infrastructure

    不管是render-node还是mode-setting-node,在kernel角度是如何体现的?

    如果hardware没有display-controller,则可以不设置DRIVER_MODESET flag只设置DRIVER_RENDER flag,这样内核DRM只会创建render-node。如果一个hardware只有display-controller而没有rendering hardware,可以设置DRIVER_MODESET而不设置DRIVER_RENDER

    大概回顾了下render node的由来。那么render node由kernel来负责创建,其编号为何从128开始,答案估计还要到kernel中寻找。

    drm_dev_init

    linux kernel中 drivers/gpu/drm/drm_drv.c中定义了drm_dev_init()函数,其中创建drm设备编号的代码如下

    	if (drm_core_check_feature(dev, DRIVER_COMPUTE_ACCEL)) {
    		ret = drm_minor_alloc(dev, DRM_MINOR_ACCEL);
    		if (ret)
    			goto err;
    	} else {
    		if (drm_core_check_feature(dev, DRIVER_RENDER)) {
    			ret = drm_minor_alloc(dev, DRM_MINOR_RENDER);
    			if (ret)
    				goto err;
    		}
    
    		ret = drm_minor_alloc(dev, DRM_MINOR_PRIMARY);
    		if (ret)
    			goto err;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    涉及到两个枚举类型结构体

    enum drm_driver_feature {
    	DRIVER_GEM			= BIT(0),
    	DRIVER_MODESET			= BIT(1),
    	DRIVER_RENDER			= BIT(3),
    	DRIVER_ATOMIC			= BIT(4),
    	DRIVER_SYNCOBJ                  = BIT(5),
    	DRIVER_SYNCOBJ_TIMELINE         = BIT(6),
    	DRIVER_COMPUTE_ACCEL            = BIT(7),
    	DRIVER_USE_AGP			= BIT(25),
    	DRIVER_LEGACY			= BIT(26),
    	DRIVER_PCI_DMA			= BIT(27),
    	DRIVER_SG			= BIT(28),
    	DRIVER_HAVE_DMA			= BIT(29),
    	DRIVER_HAVE_IRQ			= BIT(30),
    };
    
    enum drm_minor_type {
    	DRM_MINOR_PRIMARY,
    	DRM_MINOR_CONTROL,
    	DRM_MINOR_RENDER,
    	DRM_MINOR_ACCEL = 32,
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    当drm core检查到device设置了DRIVER_RENDER标签时,就通过drm_minor_alloc(dev, DRM_MINOR_RENDER)来分配ID,而drm_minor_alloc()函数中最终分配ID是通过idr_alloc()函数来实现,这里的type传入的就是DRM_MINOR_RENDER,也就是2。

    r = idr_alloc(&drm_minors_idr,
    	    NULL,
    		64 * type,
    		64 * (type + 1),
    		GFP_NOWAIT);
    
    //idr_alloc又是调用idr_alloc_u32来实现		
    int idr_alloc(struct idr *idr, void *ptr, int start, int end, gfp_t gfp)
    {
    	u32 id = start;
    	int ret;
    
    	if (WARN_ON_ONCE(start < 0))
    		return -EINVAL;
    
    	ret = idr_alloc_u32(idr, ptr, &id, end > 0 ? end - 1 : INT_MAX, gfp);
    	if (ret)
    		return ret;
    
    	return id;
    }
    EXPORT_SYMBOL_GPL(idr_alloc);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
     * idr_alloc() - Allocate an ID.
     * @idr: IDR handle.
     * @ptr: Pointer to be associated with the new ID.
     * @start: The minimum ID (inclusive).
     * @end: The maximum ID (exclusive).
     * @gfp: Memory allocation flags.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    idr_alloc()的底3个参数就是ID的start,第4个参数是ID范围的end。所以,对于DRIVER_RENDER属性的device来说,其ID范围是

    DRM_MINOR_RENDER * 64 = 128 至 (DRM_MINOR_RENDER + 1) * 64 = 192 之间。

    所以才有renderD128、renderD129。

    内核如何管理render-node的编号

    问题:DRM中如何做到一个已经分配了的ID比如128,下一个device来分配时就不使用128而是129呢?换句话说kernel中如何记忆ID的分配结果的?

    int idr_alloc_u32(struct idr *idr, void *ptr, u32 *nextid,
    			unsigned long max, gfp_t gfp)
    {
    	struct radix_tree_iter iter;
    	void __rcu **slot;
    	unsigned int base = idr->idr_base;
    	unsigned int id = *nextid;
    
    	if (WARN_ON_ONCE(!(idr->idr_rt.xa_flags & ROOT_IS_IDR)))
    		idr->idr_rt.xa_flags |= IDR_RT_MARKER;
    
    	id = (id < base) ? 0 : id - base;
    	radix_tree_iter_init(&iter, id);
    	slot = idr_get_free(&idr->idr_rt, &iter, gfp, max - base);
    	if (IS_ERR(slot))
    		return PTR_ERR(slot);
    
    	*nextid = iter.index + base;
    	/* there is a memory barrier inside radix_tree_iter_replace() */
    	radix_tree_iter_replace(&idr->idr_rt, &iter, slot, ptr);
    	radix_tree_iter_tag_clear(&idr->idr_rt, &iter, IDR_FREE);
    
    	return 0;
    }
    EXPORT_SYMBOL_GPL(idr_alloc_u32);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    这里使用了基数树(radix-tree)。

    两个显卡其分配render-node的顺序

    比如我这个pc上有两个显卡intel UHD Graphics 630和Nvidia GTX 1050 Ti,然后/dev/dri/下也有两个render-node,renderD128和renderD129,内核分配它们的顺序是如何确定的?

    应该是扫描PCI设备的时候就确定顺序了。

    ubuntu上查看哪个card和哪个GPU绑定:

    drm_info    #该命令可以查看/dev/dri/card0对应的GPU驱动
    
    • 1

    比如我这里两个显卡,card0对应intel 630, card1对应nvidia GTX 1050Ti。

    那么如何确定render node和GPU对应关系?

    ls /sys/class/drm/card0/device/drm/
    #可以看到card0中有card0, controlD64, renderD128
    ls /sys/class/drm/card1/device/drm/
    #可以看到card1中有card1, controlD65, renderD129
    
    • 1
    • 2
    • 3
    • 4

    参考:
    DRM render node number

  • 相关阅读:
    【HarmonyOS】鸿蒙传感器采样评率、鸿蒙设置屏幕常亮问题小结
    想买GPT4会员却只能排队?来看看背后的故事!
    Vim的使用
    React Native调用Android的原生功能
    计算机毕业设计Java-ssmLM美食推荐网源码+系统+数据库+lw文档
    【数据结构】自动机全家桶(AC、回文、后缀自动机)
    vue3拖拽排序 使用 vuedraggable
    2024.3.11 C++作业
    R语言编写用户自定义函数:R语言编写用户自定义函数计算多个输入参数的最大公约数(两个输入为数值)
    微信小程序——数据绑定
  • 原文地址:https://blog.csdn.net/qq_23662505/article/details/133910987