第 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);