VIRTIO-BLK设备SERIAL ID

目录

一、需求

二、实现

1、VIRTIO_BLK驱动的serial属性

2、REQ_OP_DRV_IN请求

3、UDEV命名规则


一、需求

从云的管理面来看,对每个设备的管理都要有一个id来作为唯一标识,这个id会通过管理面下发给每个终端,后续使用该id对终端的设备进行管理。比如net设备的端口号,block设备的by-id。

当前的需求,云管控面向DPU卡的管理面发起创建block设备的请求,同时传入block设备的管理ID,要求主机侧实际看到的block设备在/dev/disk/by-id下绑定下发的管理ID。

云管理面下发ID:

主机侧显示ID:

二、实现

这个ID如何提供给主机的?在控制面的block设备config信息里找不到相关的字段。

查阅virtio-block设备的内核驱动/drivers/block/virtio-blk.c,看到一个serial相关信息,这是在block设备下面提供的一个attribute属性文件。找到一个virtio block设备的主机环境,到/sys/block/vda/下查看确实有一个serial文件,cat文件显示

1、VIRTIO_BLK驱动的serial属性

对于attribute文件来说,驱动里的接口就是show和store,查看serial_show做了什么?

static ssize_t serial_show(struct device *dev,
			   struct device_attribute *attr, char *buf)
{
	struct gendisk *disk = dev_to_disk(dev);
	int err;

	/* sysfs gives us a PAGE_SIZE buffer */
	BUILD_BUG_ON(PAGE_SIZE < VIRTIO_BLK_ID_BYTES);

	buf[VIRTIO_BLK_ID_BYTES] = '\0';
	err = virtblk_get_id(disk, buf);
	if (!err)
		return strlen(buf);

	if (err == -EIO) /* Unsupported? Make it empty. */
		return 0;

	return err;
}

serial_show接口调用了virtblk_get_id接口来获取真正显示出来的id。

static int virtblk_get_id(struct gendisk *disk, char *id_str)
{
	struct virtio_blk *vblk = disk->private_data;
	struct request_queue *q = vblk->disk->queue;
	struct request *req;
	int err;

	req = blk_get_request(q, REQ_OP_DRV_IN, 0);
	if (IS_ERR(req))
		return PTR_ERR(req);

	err = blk_rq_map_kern(q, req, id_str, VIRTIO_BLK_ID_BYTES, GFP_KERNEL);
	if (err)
		goto out;

	blk_execute_rq(vblk->disk, req, false);
	err = blk_status_to_errno(virtblk_result(blk_mq_rq_to_pdu(req)));
out:
	blk_put_request(req);
	return err;
}

virtblk_get_id这个接口里,调用了blk设备驱动收发数据典型的4个接口,分别是blk_get_request、blk_rq_map_kern以及blk_execute_rq和blk_put_request。这三个接口是block设备层的接口,并不是virtio特有的。一次block设备的访问:

1)首先blk_get_request是从request queue中分配了一条类型为REQ_OP_DRV_IN的请求,记录在req变量;

2)然后blk_rq_map_kern是分配一条bio请求,这个bio绑定到前面的request请求,而且bio内部记录读取数据的存储空间首地址和数据长度;

3)blk_execute_rq是真正执行了这条request请求和bio操作,完成后退出,数据存储在id_str中;

4)blk_put_request,释放这条请求;

由此可以看出,这个serial字段最终是在block的数据通道实现的,而非控制通道。负责实现的可能是SDPK或者qemu。

2、REQ_OP_DRV_IN请求

上一节描述了serial字段是通过block设备的通用接口,执行了一次完整的block设备访问操作来获得的,是block层的抽象操作;

具体的执行还是在每个底层驱动里,对于virtio设备来说,blk_execute_rq最终会执行virtio-blk.c里的virtio_queue_rq接口,进而驱动传输层的数据通信。

static blk_status_t virtio_queue_rq(struct blk_mq_hw_ctx *hctx,
			   const struct blk_mq_queue_data *bd)
{
    struct request *req = bd->rq;

	switch (req_op(req)) {
	case REQ_OP_READ:
	case REQ_OP_WRITE:
		type = 0;
		break;
    /*....*/
	case REQ_OP_DRV_IN:                //serial请求,REQ_OP_DRV_IN类型
		type = VIRTIO_BLK_T_GET_ID;    //传输层的关键字,VIRTIO_BLK_T_GET_ID
		break;
	default:
		WARN_ON_ONCE(1);
		return BLK_STS_IOERR;
	}

	vbr->out_hdr.type = cpu_to_virtio32(vblk->vdev, type);

    blk_mq_start_request(req);

    num = blk_rq_map_sg(hctx->queue, req, vbr->sg);

    err = virtblk_add_req(vblk->vqs[qid].vq, vbr, vbr->sg, num);

}

通过上述代码看到,最终REQ_OP_DRV_IN转化成了virtio传输层的type字段,type= VIRTIO_BLK_T_GET_ID;然后SPDK或者qemu中接收到这个type的消息,就会返回相应的ID。

最终确实在SPDK和QEMU代码中找到了同样的字段,对于DPU,只需要在SPDK里返回相应字段即可。

3、UDEV命名规则

还有一个关键的环节,前面理清了serial字段的前因后果,那么serial到底是不是/dev/disk/by-id下显示的ID呢,这个还不知道呢。

BLOCK设备是遵循UDEV命名规则的,UDEV是linux系统的设备管理器,它主要是负责管理 /dev 目录中的设备节点(device nodes)和 /dev/disk 子目录中的与设备 ID 相关的的符号连接文件,当新的硬件设备(hardware devices)加入系统或者从系统删除时,Linux 内核通过 netlink socket 通知 udev,然后 udev 根据存在的 udev rules 来做相应的处理,默认地,它会在 /dev 目录中创建或者删除设备节点。

udev rules文件都存储在/lib/udev/rules.d/,其中block设备的rules文件是/lib/udev/rules.d/60-persistent-storage.rules。

打开这个文件,搜索VIRTIO字段,可以看到virtio-blk设备的命名规则,其中SYMLINK就是/dev/disk/by-id/下的设备名称,命名格式为virtio-$env{ID_SERIAL},而ID_SERIAL是从vd*设备的serial ATTR中获得的。也就是说/dev/disk/by-id下的设备名称确实是通过serial属性文件,然后从SDPK中获取的。

理解 QEMU/KVM 和 Ceph(3):存储卷挂接和设备名称 - SammyLiu - 博客园

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值