Skip Navigation Links | |
Exit Print View | |
Writing Device Drivers Oracle Solaris 11.1 Information Library |
Part I Designing Device Drivers for the Oracle Solaris Platform
1. Overview of Oracle Solaris Device Drivers
2. Oracle Solaris Kernel and Device Tree
5. Managing Events and Queueing Tasks
7. Device Access: Programmed I/O
10. Mapping Device and Kernel Memory
13. Hardening Oracle Solaris Drivers
14. Layered Driver Interface (LDI)
Part II Designing Specific Kinds of Device Drivers
15. Drivers for Character Devices
18. SCSI Host Bus Adapter Drivers
19. Drivers for Network Devices
Overview of SR-IOV Device Driver
Device Configuration Parameters
Setting Device Configuration Parameters
SR-IOV Configuration on Sparc OVM Platform
SR-IOV Configuration on Bare Metal Platforms
pci_param_get_ioctl() Interface
Part III Building a Device Driver
22. Compiling, Loading, Packaging, and Testing Drivers
23. Debugging, Testing, and Tuning Device Drivers
24. Recommended Coding Practices
B. Summary of Oracle Solaris DDI/DKI Services
C. Making a Device Driver 64-Bit Ready
The SR-IOV driver ioctls are used to identify the device specific parameters that can be configured by the administrator and to validate a specific configuration before it is applied. The following sections describe the ioctls data structures and the interfaces.
The following sections list the data structures that the PF driver should define and initialize before implementing the ioctls:
The iov_param_ver_info structure is defined as follows:
#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
The iov_param_ver_info structure contains the following fields:
typedef struct iov_param_ver_info { uint32_t version; uint32_t num_params; } iov_param_ver_info_t;
where:
Version information
Number of parameters
The iov_param_validate structure is defined as follows:
#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
The iov_param_validate contains the following fields:
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;
where:
An ASCII string that explains the reason for failure if the ioctl call fails.
Length of buffer pv_buf
Buffer containing the parameters
The iov_param_desc structure is defined as follows:
#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
The structure iov_param_desc contains the following fields:
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;
where:
Used in the ldm(1M) command or the pci.conf file to assign a value to the parameter.
A brief description of the parameter.
Indicates if the parameter is applicable to only PF, only VF or applicable to both PF and VF.
Value assigned by the driver if parameter is not specified in the ldm() command or pci.conf file.
Specifies the minimum range of values for the integer parameter
Specifies the maximum range of values for the integer parameter.
Specifies the default string that will be used, if parameter is a string.
SR-IOV device drivers that implement the IOV_GET_VER_INFO IOCTL() ioctl should set the version and the num_params fields in the iov_param_ver_info structure and return the values to the calling function. The calling function then uses the version and num_params parameters to determine the size of buffer required to get parameter descriptions using the IOV_GET_PARAM_INFO() ioctl call.
The general flow of control for a driver that calls the IOV_GET_PARAM_INFO() ioctl is as follows:
Keep an array of iov_param_desc_t structures which contain description for each of the configurable params that are supported by the structure. See the iov_param_desc structure for the structure description.
Copy out the array of iov_param_desc_t structures to the arg parameter. The fields in the iov_param_desc_t structure are static and can be defined at compile time.
The number of elements in the array is the num_params value returned by the IOV_GET_VER_INFO() ioctl call. The size of the buffer is sizeof (iov_param_desc_t) * num_params
The general flow of control for a driver that calls the IOV_VALIDATE_PARAM() is as follows:
Send the arg parameter to the pci_param_get_ioctl() interface and obtain a pointer to the pci_param_t structure.
Write an explanatory string to the pv_reason array if param validation fails.
Call the pci_get_plist() interface followed by the pci_plist_lookup() interface to obtain the device parameters.
Lookup the vfs name-value pair in the PF plist to obtain the number of VFs to be configured for the validation of this configuration. The drivers should use integer data type of at least 16 bits to look up vfs name-value pair. Use the pciv_plist_getvf() interface to get the plist parameter for the VF devices.
Validate the parameters without actually applying them to the device.
Return 0 when a valid configuration is found.
Caution - The parameters validated in the above procedure are not related to the current configuration of the device in any way. They need to be validated separately assuming they could be a future configuration. If not, the driver should return DDI_EINVAL to indicate improper configuration. The driver should also provide an explanatory string in the pv_reason field in the iov_param_validate structure when invalid configurations are found. This string informs the administrator why the configuration failed. |
The DDI interfaces ddi_cb_register() and ddi_cb_unregister() are used to register callbacks. Callbacks are used for event notifications and incoming data traffic. Callbacks serve as an event handler for each event during transmission.
The SR-IOV driver should implement additional callbacks to inform the PF driver before and after configuring or unconfiguring the VFs.
The driver can implement the callbacks using the following DDI callback registration mechanism interfaces:
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)
See the ddi_cb_register(9F) man page for details.
typedef int(*ddi_cb_func_t) (dev_info_t *dip, ddi_cb_action_t action, void *cbarg, void *arg1, void *arg2)
where:
Is a pointer to the dev_info structure
Is a pointer to the structure pciv_config_vf_t.
Is set as DDI_CB_PCIV_CONFIG_VF to receive notifications about changes to the VF configuration.
Private argument to send to itself during each execution of its cbfunc() routine.
Private argument to send to itself during each execution of its cbfunc() routine.
See Register a Callback Handler Function for additional information.
Note - All PF drivers that are SR-IOV capable must use the ddi_cb_flags_t DDI_CB_FLAG_SRIOV to inform the Oracle Solaris IOV framework that the PF drivers are SR-IOV capable.
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);