SR-IOV 驱动程序 Ioctl
SR-IOV 驱动程序 ioctl 用于标识可由管理员配置的特定于设备的参数,并在应用特定配置之前验证此特定配置。以下各节介绍了 ioctl 数据结构和接口。
数据结构
以下各节列出了 PF 驱动程序在实现 ioctl 之前应该定义并初始化的数据结构:
iov_param_ver_info 结构
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;
其中:version
版本信息
num_params
参数个数
iov_param_validate 结构
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;
其中:pv_reason
ioctl 调用失败时用于解释失败原因的一个 ASCII 字符串
pv_buflen
缓冲区 pv_buf 的长度
pv_buf
包含参数的缓冲区
iov_param_desc 结构
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;
其中:pd_name
在 ldm(1M) 命令或 pci.conf 文件中使用,用于为参数指定值。
pd_desc
参数的简短说明。
pd_flag
指示参数是仅适用于 PF,还是仅适用于 VF,亦或是同时适用于 PF 和 VF。
pd_default_value
未在 ldm() 命令或 pci.conf 文件中指定参数时由驱动程序指定的值。
pd_min64
指定整数参数值的最小范围。
pd_max64
指定整数参数值的最大范围。
pd_default_string
指定将使用的缺省字符串(如果参数是字符串)。
IOV_GET_VER_INFO Ioctl
实现了 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_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 Ioctl
对调用 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)
typedef int(*ddi_cb_func_t)
(dev_info_t *dip, ddi_cb_action_t action,
void *cbarg, void *arg1, void *arg2)
其中:dip
是指向 dev_info 结构的指针。
cbarg
是指向 pciv_config_vf_t 结构的指针。
action
设置为 DDI_CB_PCIV_CONFIG_VF 以接收有关对 VF 配置所做更改的通知。
arg1
在每次执行自己的 cbfunc() 例程期间发送给自己的专用参数。
arg2
在每次执行自己的 cbfunc() 例程期间发送给自己的专用参数。
有关更多信息,请参见注册回调处理程序函数。
注 -具有 SR-IOV 功能的所有 PF 驱动程序必须使用 ddi_cb_flags_t DDI_CB_FLAG_SRIOV 来通知 Oracle Solaris IOV 框架 PF
驱动程序具有 SR-IOV 功能。
驱动程序 Ioctl 的样例代码
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);