第 1 部分针对 Oracle Solaris 平台设计设备驱动程序
9. 直接内存访问 (Direct Memory Access, DMA)
14. 分层驱动程序接口 (Layered Driver Interface, LDI)
物理功能 (Physical Function, PF) 驱动程序
虚拟功能 (Virtual Function, VF) 驱动程序
SR-IOV 驱动程序 ioctl 用于标识可由管理员配置的特定于设备的参数,并在应用特定配置之前验证此特定配置。以下各节介绍了 ioctl 数据结构和接口。
以下各节列出了 PF 驱动程序在实现 ioctl 之前应该定义并初始化的数据结构:
iov_param_ver_info 结构定义如下:
#define IOV_IOCTL (('I' << 24) | ('O' << 16) | ('V' << 8))
#define IOV_GET_VER_INFO (IOV_IOCTL | 0)
#define IOV_GET_PARAM_INFO (IOV_IOCTL | 1)
#define IOV_VALIDATE_PARAM (IOV_IOCTL | 2)
#define IOV_PARAM_DESC_VERSION 1
iov_param_ver_info 结构包含以下字段:
typedef struct iov_param_ver_info {
uint32_t version;
uint32_t num_params;
} iov_param_ver_info_t;
其中:
版本信息
参数个数
iov_param_validate 结构定义如下:
#define IOV_IOCTL (('I' << 24) | ('O' << 16) | ('V' << 8))
#define IOV_GET_VER_INFO (IOV_IOCTL | 0)
#define IOV_GET_PARAM_INFO (IOV_IOCTL | 1)
#define IOV_VALIDATE_PARAM (IOV_IOCTL | 2)
#define IOV_PARAM_DESC_VERSION 1
iov_param_validate 包含以下字段:
typedef struct iov_param_validate {
char pv_reason[MAX_REASON_LEN + 1];
int32_t pv_buflen;
/* encoded buffer containing params */
char pv_buf[1]; /* size of buf is pv_buflen */
} iov_param_validate_t;
其中:
ioctl 调用失败时用于解释失败原因的一个 ASCII 字符串
缓冲区 pv_buf 的长度
包含参数的缓冲区
iov_param_desc 结构定义如下:
#define IOV_IOCTL (('I' << 24) | ('O' << 16) | ('V' << 8))
#define IOV_GET_VER_INFO (IOV_IOCTL | 0)
#define IOV_GET_PARAM_INFO (IOV_IOCTL | 1)
#define IOV_VALIDATE_PARAM (IOV_IOCTL | 2)
#define IOV_PARAM_DESC_VERSION 1
iov_param_desc 结构包含以下字段:
typedef struct iov_param_desc {
char pd_name[MAX_PARAM_NAME_SIZE];
char pd_desc[MAX_PARAM_DESC_SIZE];
int32_t pd_flag; /* applicable for PF or VF or both */
int32_t pd_data_type; /* integer, string, plist */
/* Following 3 are applicable for integer data types */
uint64_t pd_default_value;
uint64_t pd_min64;
uint64_t pd_max64;
char pd_default_string [MAX_PARAM_DEFAULT_STRING_SIZE];
} iov_param_desc_t;
其中:
在 ldm(1M) 命令或 pci.conf 文件中使用,用于为参数指定值。
参数的简短说明。
指示参数是仅适用于 PF,还是仅适用于 VF,亦或是同时适用于 PF 和 VF。
未在 ldm() 命令或 pci.conf 文件中指定参数时由驱动程序指定的值。
指定整数参数值的最小范围。
指定整数参数值的最大范围。
指定将使用的缺省字符串(如果参数是字符串)。
实现了 IOV_GET_VER_INFO IOCTL() ioctl 的 SR-IOV 设备驱动程序应在 iov_param_ver_info 结构中设置 version 和 num_params 字段,并将值返回到调用函数。调用函数随后将使用 version 和 num_params 参数确定使用 IOV_GET_PARAM_INFO() ioctl 调用获取参数描述所需的缓冲区大小。
调用 IOV_GET_PARAM_INFO() ioctl 的驱动程序的一般控制流如下所述:
保留 iov_param_desc_t 结构的数组,这些结构包含各自支持的每个可配置参数的说明。有关结构说明的信息,请参见 iov_param_desc 结构。
将 iov_param_desc_t 结构的数组复制到 arg 参数。iov_param_desc_t 结构中的字段是静态字段,可在编译时定义。
数组中元素的数量是 IOV_GET_VER_INFO() ioctl 调用返回的 num_params 值。缓冲区的大小为 sizeof (iov_param_desc_t) * num_params。
对调用 IOV_VALIDATE_PARAM() 的驱动程序的一般控制流程如下所述:
将 arg 参数发送到 pci_param_get_ioctl() 接口并获取指向 pci_param_t 结构的指针。
当 param 验证失败时,向 pv_reason 数组写入一个解释性的字符串。
依次调用 pci_get_plist() 接口和 pci_plist_lookup() 接口来获取设备参数。
在 PF plist 中查找 vfs 名称-值对以获取要针对此配置的验证而配置的 VF 的数量。驱动程序应使用长度至少为 16 位的整数数据类型查找 vfs 名称-值对。使用 pciv_plist_getvf() 接口获取 VF 设备的 plist 参数。
在不实际将参数应用于设备的情况下对参数进行验证。
在找到有效配置时返回 0。
![]() | 注意 - 上述过程中验证的参数与设备的当前配置毫不相关。它们需要单独进行验证(假定它们可以进一步配置)。如果不单独进行验证,驱动程序应返回 DDI_EINVAL 来指示配置不正确。当发现了无效配置时,驱动程序还应在 iov_param_validate 结构的 pv_reason 字段中提供一个解释性字符串。此字符串会将配置失败的原因告知管理员。 |
DDI 接口 ddi_cb_register() 和 ddi_cb_unregister() 用来注册回调。回调用于事件通知和传入数据通信。回调在传输期间充当每个事件的事件处理程序。
SR-IOV 驱动程序应实现其他回调以在配置或取消配置 VF 前后通知 PF 驱动程序。
驱动程序可以使用以下 DDI 回调注册机制接口实现回调:
int ddi_cb_register(dev_info_t *dip, ddi_cb_flags_t flags, ddi_cb_func_t cbfunc, void *arg1, void *arg2,ddi_cb_handle_t *ret_hdlp)
有关详细信息,请参见 ddi_cb_register(9F) 手册页。
typedef int(*ddi_cb_func_t) (dev_info_t *dip, ddi_cb_action_t action, void *cbarg, void *arg1, void *arg2)
其中:
是指向 dev_info 结构的指针。
是指向 pciv_config_vf_t 结构的指针。
设置为 DDI_CB_PCIV_CONFIG_VF 以接收有关对 VF 配置所做更改的通知。
在每次执行自己的 cbfunc() 例程期间发送给自己的专用参数。
在每次执行自己的 cbfunc() 例程期间发送给自己的专用参数。
有关更多信息,请参见注册回调处理程序函数。
注 - 具有 SR-IOV 功能的所有 PF 驱动程序必须使用 ddi_cb_flags_t DDI_CB_FLAG_SRIOV 来通知 Oracle Solaris IOV 框架 PF 驱动程序具有 SR-IOV 功能。
enum ioc_reply
igb_ioctl(igb_t *igb, struct iocblk *iocp, mblk_t *mp)
{
int rval = 0;
iov_param_ver_info_t *iov_param_ver;
iov_param_validate_t pvalidate;
pci_param_t my_params;
char reason[81];
if (mp->b_cont == NULL)
return (IOC_INVAL);
if ((int)iocp->ioc_count < 0)
return (IOC_INVAL);
switch (iocp->ioc_cmd) {
case IOV_GET_PARAM_VER_INFO:
if (iocp->ioc_count < sizeof (iov_param_ver_info_t))
return (IOC_INVAL);
iov_param_ver = (iov_param_ver_info_t *)(mp->b_cont->b_rptr);
iov_param_ver->version = IOV_PARAM_DESC_VERSION;
iov_param_ver->num_params = NUM_OF_PARAMS;
return (IOC_REPLY);
case IOV_GET_PARAM_INFO:
if (iocp->ioc_count < sizeof (pci_list))
return (IOC_INVAL);
memcpy((caddr_t)(mp->b_cont->b_rptr), &pci_list,sizeof (pci_list));
return (IOC_REPLY);
case IOV_VALIDATE_PARAM:
if (iocp->ioc_count <= 0)
return (IOC_INVAL);
strcpy(reason, "Failed to read params sent\n");
rval = pci_param_get_ioctl(igb->dip,(uintptr_t)(mp->b_cont->b_rptr),
iocp->ioc_flag | FKIOCTL,&my_params );
if (rval == 0) {
rval = validate_params(igb->dip, my_params, reason);
pci_param_free(my_params);
}
if (rval) {
memcpy(mp->b_cont->b_rptr, reason, sizeof (reason));
iocp->ioc_count = sizeof (reason);
return (IOC_REPLY);
}
iocp->ioc_count = 0;
return (IOC_REPLY);
iov_param_ver_info_t iov_param_ver;
iov_param_validate_t pvalidate;
pci_param_t my_params;
switch (cmd) {
case IOV_GET_PARAM_VER_INFO:
iov_param_ver.version = IOV_PARAM_DESC_VERSION;
iov_param_ver.num_params = NUM_OF_PARAMS;
if (ddi_copyout(&iov_param_ver, (caddr_t)arg,
sizeof (iov_param_ver_info_t), mode) != DDI_SUCCESS)
return (DEFAULT);
return (0);
case IOV_GET_PARAM_INFO:
if (ddi_copyout(&pci_list, (caddr_t)arg,param_list_size, mode) != DDI_SUCCESS)
return (DEFAULT);
return (0);
case IOV_VALIDATE_PARAM:
strcpy(reason, "Failed to read params sent\n");
rv = pci_param_get_ioctl(state->dip, arg, mode, &my_params);
if (rv == 0)
rv = validate_params(state->dip, my_params, reason);
else
return (rv);
pci_param_free(my_params);
if (rv) {
if (ddi_copyout(reason,iov_param_validate_t *)arg)->pv_reason,
sizeof (reason), mode) != DDI_SUCCESS)
return (DEFAULT);
return (rv);
}
return (0);