The transparent STREAMS ioctl(2) mechanism is needed because user context does not exist in modules and drivers when an ioctl(2) is processed. This prevents them from using the kernel ddi_copyin/ddi_copyout functions.
Transparent ioctl(2)s also let an application be written using conventional ioctl(2) semantics instead of using the I_STR ioctl(2) and an strioctl structure. The difference between transparent and nontransparent ioctl(2)
ioctl(2) processing in a Streams driver and module is in the way data is transferred from user to kernel space.
The transparent ioctl(2) mechanism allow backward compatibility for older programs. This transparency only works for modules and drivers that support transparent ioctl(2)s. Trying to use transparent ioctl(2)s on a Stream that doesn't support them makes the driver send an error message upstream, causing the ioctl to fail.
The following example illustrates the semantic difference between a nontransparent and transparent ioctl(2). A module that allows arbitrary character translations. is pushed on the Stream The ioctl(2) specifies the translation to do, and in this case all uppercase vowels are changed to lowercase. A transparent ioctl(2) uses XCASE, instead of using I_STR to inform the module directly what should be done.
Assume that fd points to a Streams device and that the conversion module has been pushed on to it. Use a nontransparent I_STR command to inform the module to change the case of AEIOU. The semantics of this command are:
strioctl.ic_cmd = XCASE; strioctl.ic_timout = 0; strioctl.ic_dp = "AEIOU" strioctl.ic_len = strlen(strioctl.ic_dp); ioctl(fd,I_STR, & strioctl);
When the Stream head receives the I_STR ioctl(2), it creates an M_IOCTL message with the ioc_cmd set to XCASE and the data specified by ic_dp "AEIOU" is copied into the first mblk following the M_IOCTL mblk.
ioctl(fd, XCASE, "AEIOU");
The Stream head creates an M_IOCTL message with the ioc_cmd set to XCASE, but the data is not copied in. Instead, ioc_count is set to TRANSPARENT and the address of the user data is placed in the first mblk following the M_IOCTL mblk. The module then requests the Stream head to copy in the data ("AEIOU") from user space.
Unlike the nontransparent ioctl(2) which can specify a timeout parameter, transparent ioctl(2)s block until processing is complete.
Incorrectly written drivers can cause applications using transparent ioctl(2)s to block indefinitely.
Notice that even though this process is simpler in the application, transparent ioctls add considerable complexity to modules and drivers, and additional overhead to the time required to process the request.
The form of the M_IOCTL message generated by the Stream head for a transparent ioctl(2) is a single M_IOCTL message block followed by one M_DATA block. The form of the iocblk(9S) structure in the M_IOCTL block is the same as described under General ioctl(2) processing. However, ioc_cmd is set to the value of the command argument in ioctl(2) and ioc_count is set to the special value of TRANSPARENT. The value TRANSPARENT distinguishes when an I_STR ioctl(2) can specify a value of ioc_cmd that is equivalent to the command argument of a transparent ioctl(2). The b_cont block of the message contains the value of the arg parameter in the call.
If a module processes a specific ioc_cmd and does not validate the ioc_count field of the M_IOCTL message, it breaks when transparent ioctl(2)s are performed with the same command.
Write modules and drivers to support both transparent and I_STR ioctl(2)s.
All M_IOCTL message types (M_COPYIN, M_COPYOUT, M_IOCDATA,M_IOCACK and M_IOCNACK) have some similar data structures and sizes. Reuse these structures instead of reallocating them. Note the similarities in the command type, credentials, and id.
The iocblk(9S) structure is contained in M_IOCTL, M_IOCACK and M_IOCNAK message types. For the transparent case, M_IOCTL has one M_DATA message linked to it. This message contains a copy of the argument passed to ioctl(2). Transparent processing of M_IOCACK and M_IONAK does not allow any messages linked to them.
The copyreq(9S) structure is contained in M_COPYIN and M_COPYOUT message types. The M_COPYIN message type must not have any other message linked to it (that is, b_cont == NULL). The M_COPYOUT message type must have one or more M_DATA messages linked to it. These messages contain the data to be copied into user space.
The copyresp(9S) structure is contained in M_IOCDATA response message types. These message are generated by the Stream head in response to an M_COPYIN or M_COPYOUT request. If the message is in response to an M_COPYOUT request, the message has no messages attached to it (b_cont is NULL). If the response is to an M_COPYIN, then zero or more M_DATA message types are attached to the M_IOCDATA message. These attached messages contain a copy of the user data requested by the M_COPYIN message.
The iocblk(9S), copyreq(9S), and copyresp(9S) structures each contain a field indicating the type of ioctl(2) command, a pointer to the user's credentials, and a unique identifier for this ioctl(2). These fields must be preserved.
The structure member cq_private is reserved for use by the module. M_COPYIN and M_COPYOUT request messages contain a cq_private field that can be set to contain state information for ioctl(2) processing (this identifies what the subsequent M_IOCDATA response message contains). This state is returned in cp_private in the M_IOCDATA message. This state information determines the next step in processing the message. Keeping the state in the message makes the message self-describing and simplifies the ioctl(2) processing.
For each piece of data the module copies from user space an M_COPYIN message is sent to the Stream head. The M_COPYIN message specifies the user address (cq_addr) and number of bytes (cq_size) to copy from user space. The Stream head responds to the M_COPYIN request with a M_IOCDATA message. The b_cont field of the M_IOCDATA mblk contains the contents pointed to by the M_COPYIN request. Likewise, for each piece of data the module copies to user space, an M_COPYOUT message is sent to the Stream head. Specify the user address (cq_addr) and number of bytes to copy (cq_size). The data to be copied is linked to the M_COPYOUT message as one or more M_DATA messages. The Stream head responds to M_COPYOUT requests with an M_IOCDATA message, but b_cont is null.
After the module has completed processing the ioctl (that is, all M_COPYIN and M_COPYOUT requests have been processed), the ioctl(2) must be acknowledged with an M_IOCACK to indicate successful completion of the command or an M_IOCNAK to indicate failure.
If an error occurs when attempting to copy data to or from user address space, the Stream head will set cp_rval in the M_IOCDATA message to the error number. In the event of such an error, the M_IOCDATA message should be freed by the module or driver. No acknowledgement of the ioctl(2) is sent in this case.