2. 实现帧缓冲区创建与销毁

任务目标

在Linux内核DRM子系统中,实现帧缓冲区(drm_framebuffer)的创建与销毁逻辑。创建函数需校验像素格式、尺寸及底层内存对象(drm_gem_object)的有效性,并正确初始化帧缓冲区;销毁函数需完成引用计数释放与资源清理。

核心实现

以下代码实现了一个最小但完整的帧缓冲区创建/销毁闭环,可直接嵌入DRM驱动中。核心逻辑包括:

  • 格式与尺寸校验:通过drm_any_plane_has_format检查格式是否被驱动支持,并拒绝零尺寸。
  • 内存对象绑定:从drm_gem_object_lookup获取GEM对象,验证其实际尺寸不小于请求的帧缓冲大小。
  • 生命周期管理:销毁时先移除用户空间句柄,再通过引用计数释放GEM对象。
// framebuffer.c — 帧缓冲区创建与销毁最小实现
#include <drm/drm_device.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_gem.h>
#include <drm/drm_framebuffer.h>

static int validate_fb_request(struct drm_device *dev,
			       const struct drm_mode_fb_cmd2 *mode_cmd)
{
	uint32_t bpp = drm_format_info_bpp(mode_cmd->pixel_format, 0);

	/* 1. 像素格式必须被至少一个plane支持 */
	if (!drm_any_plane_has_format(dev, mode_cmd->pixel_format,
				      mode_cmd->modifier[0]))
		return -EINVAL;

	/* 2. 尺寸必须非零且不超过硬件限制 */
	if (mode_cmd->width == 0 || mode_cmd->height == 0 ||
	    mode_cmd->width > 16384 || mode_cmd->height > 16384)
		return -EINVAL;

	/* 3. 单缓冲区场景下,仅允许一个plane描述符 */
	if (mode_cmd->num_planes != 1)
		return -EINVAL;

	/* 4. 像素格式的bpp必须与pitches匹配(简化校验) */
	if (mode_cmd->pitches[0] < mode_cmd->width * bpp / 8)
		return -EINVAL;

	return 0;
}

struct drm_framebuffer *my_fb_create(struct drm_device *dev,
				     struct drm_file *file_priv,
				     const struct drm_mode_fb_cmd2 *mode_cmd)
{
	struct drm_framebuffer *fb;
	struct drm_gem_object *gem_obj;
	int ret;

	ret = validate_fb_request(dev, mode_cmd);
	if (ret)
		return ERR_PTR(ret);

	/* 从用户空间句柄获取GEM对象 */
	gem_obj = drm_gem_object_lookup(file_priv, mode_cmd->handles[0]);
	if (!gem_obj)
		return ERR_PTR(-ENOENT);

	/* 验证GEM对象尺寸足够容纳帧缓冲 */
	if (gem_obj->size < mode_cmd->pitches[0] * mode_cmd->height) {
		drm_gem_object_put(gem_obj);
		return ERR_PTR(-EINVAL);
	}

	/* 分配帧缓冲区结构体 */
	fb = kzalloc(sizeof(*fb), GFP_KERNEL);
	if (!fb) {
		drm_gem_object_put(gem_obj);
		return ERR_PTR(-ENOMEM);
	}

	/* 初始化核心字段 */
	fb->dev = dev;
	fb->format = drm_format_info(mode_cmd->pixel_format);
	fb->width = mode_cmd->width;
	fb->height = mode_cmd->height;
	fb->pitches[0] = mode_cmd->pitches[0];
	fb->modifier = mode_cmd->modifier[0];
	fb->obj[0] = gem_obj;

	/* 注册到DRM核心(内部会获取额外引用) */
	ret = drm_framebuffer_init(dev, fb, &drm_framebuffer_funcs);
	if (ret) {
		drm_gem_object_put(gem_obj);
		kfree(fb);
		return ERR_PTR(ret);
	}

	return fb;
}

static void my_fb_destroy(struct drm_framebuffer *fb)
{
	/* 释放GEM对象引用(drm_framebuffer_init时获取的) */
	drm_gem_object_put(fb->obj[0]);
	/* 释放帧缓冲区自身 */
	kfree(fb);
}

static const struct drm_framebuffer_funcs drm_framebuffer_funcs = {
	.destroy = my_fb_destroy,
};

驱动接入与验证

在驱动的drm_driver结构体中注册fb_create回调:

static const struct drm_driver my_driver = {
	...
	.fb_create = my_fb_create,
};

验证路径:

  1. 编译驱动:make drivers/gpu/drm/my_driver.ko,确认无编译错误。
  2. 加载驱动后,使用modetest -M my_driver(libdrm工具)创建测试帧缓冲:modetest -M my_driver -s 0:1920x1080。若创建成功,命令返回0且无内核报错。
  3. 销毁验证:执行modetest -M my_driver -d释放所有资源,观察dmesg无内存泄漏警告(可配合kmemleak扫描)。
  4. 异常路径测试:使用非法格式(如DRM_FORMAT_INVALID)调用创建,应返回EINVAL;使用不存在的GEM句柄应返回ENOENT。

通过标准:正常创建/销毁循环100次无错误,且/sys/kernel/debug/dri/0/framebuffers中无残留节点。