STREAMS Programming Guide

M_COPYIN Example

Example 8-7 illustrates only the processing of a transparent ioctl(2) request (nontransparent request processing is not shown). In this example, the contents of a user buffer are to be transferred into the kernel as part of an ioctl call of the form

ioctl(fd, SET_ADDR, (caddr_t) &bufadd);

where bufadd is a struct address whose elements are:

struct address {	
     int					ad_len;				/* buffer length in bytes */
     caddr_t			ad_addr;				/* buffer address */
};

This requires two pairs of messages (request and response) following receipt of the M_IOCTL message. The first copyin(9F)s the structure (address) and the second copyin(9F)s the buffer (address.ad.addr). Two states are maintained and processed in this example: GETSTRUCT is for copying in the address structure, and GETADDR for copying in the ad_addr of the structure.

xxwput verifies that the SET_ADDR is TRANSPARENT to avoid confusion with an I_STR ioctl(2), which uses a value of ioc_cmd equivalent to the command argument of a transparent ioctl(2). This is done by checking if the size count is equal to TRANSPARENT [line 28). If it is equal to TRANSPARENT, then the message was generated from a transparent ioctl(2); that is not from an I_STR ioctl(2). [lines 29-32)(


Example 8-7 M_COPYIN Example

	struct address {			/* same members as in user space */
		int	ad_len;	/* length in bytes */
		caddr_t	ad_addr;	/* buffer address */
	};

	/* state values (overloaded in private field) */
	#define GETSTRUCT 0			/* address structure */
	#define GETADDR	 1		/* byte string from ad_addr */

	static void xxioc(queue_t *q, mblk_t *mp);

	static int
	xxwput(q, mp)
		queue_t *q;		/* write queue */
		mblk_t *mp;
	{
		struct iocblk *iocbp;
		struct copyreq *cqp;

		switch (mp->b_datap->db_type) {
			.
			.
			.
			case M_IOCTL:
				/* Process ioctl commands */
				iocbp = (struct iocblk *)mp->b_rptr;
				switch (iocbp->ioc_cmd) {
					case SET_ADDR;
						if (iocbp->ioc_count != TRANSPARENT) {
							/* do non-transparent processing here
							 *       (not shown here) */
						} else {
							/* ioctl command is transparent 
							 * Reuse M_IOCTL block for first M_COPYIN request 
							 * of address structure */
							cqp = (struct copyreq *)mp->b_rptr;
							/* Get user space structure address from linked 
							 * M_DATA block */
							cqp->cq_addr = *(caddr_t *) mp->b_cont->b_rptr;
							cqp->cq_size = sizeof(struct address);
							/* MUST free linked blks */
							freemsg(mp->b_cont);
							mp->b_cont = NULL;

							/* identify response */
							cqp->cq_private = (mblk_t *)GETSTRUCT;

							/* Finish describing M_COPYIN message */
							cqp->cq_flag = 0;
							mp->b_datap->db_type = M_COPYIN;
							mp->b_wptr = mp->b_rptr + sizeof(struct copyreq);
							qreply(q, mp);
						break;
					default: /* M_IOCTL not for us */
						/* if module, pass on */
						/* if driver, nak ioctl */
						break;
				} /* switch (iocbp->ioc_cmd) */
				break;
			case M_IOCDATA:
				/* all M_IOCDATA processing done here */
				xxioc(q, mp);
				break;
		}
		return (0);
	}

The transparent part of the SET_ADDR M_IOCTL message processing requires the address structure to be copied from user address space. To accomplish this, it issues an M_COPYIN request to the Stream head (lines 37-64).

The mblk is reused and mapped into a copyreq(9S) structure (line 42. The user space address of bufadd is contained in the b_cont of the M_IOCTL mblk. This address and its size are copied into the copyreq(9S) message. (lines 47-49). The b_cont of the copy request mblk is not needed, so it is freed and then NULLed (line 51-52).

cq_private of the copy request is returned in cp_private of the copy response when the M_IOCDATA message is returned. This value is set to GETSTRUCT to indicate that the address structure is contained in the b_cont of the M_IOCDATA message mblk (line 53). The copy request message is then sent back to the Stream head (line 63). xxwput then returns and is called again when the Stream head responds with an M_IOCDATA message, which is processed by the xxioc routine (lines 72-74).

On receipt of the M_IOCDATA message for the SET_ADDR command, xxioc routine checks cp_rval. If an error occurred during the copyin operation, cp_rval is set. The mblk is freed (lines 93-96) and, if necessary, xxioc cleans up from previous M_IOCTL requests, freeing memory, resetting state variables, and so on. The Stream head returns the appropriate error to the user.

The cp_private field is set to GETSTRUCT (lines 97-99). This indicates that the linked b_cont mblk contains a copy of the user's address structure. The example then copies the actual address specified in address.ad_addr.

The program issues another M_COPYIN request to the Stream head (lines 100-116), but this time cq_private contains GETADDR to indicate that the M_IOCDATA response will contain a copy of address.ad_addr. The Stream head copies the information at the requested user address and sends it downstream in another, final M_IOCDATA message.

The final M_IOCDATA message arrives from the Stream head. cp_private contains GETADDR(line 118). The ad_addr data is contained in the b_cont link of the mblk. If the address is successfully processed by xx_set_addr (not shown here), the message is acknowledged with a M_IOCACK message (lines 124-128). If xx_set_addr fails, the message is rejected with a M_IOCNAK message (lines 121-122). xx_set_addr is a routine (not shown in the example) that processes the user address from the ioctl(2).

After the final M_IOCDATA message is processed, the module acknowledges the ioctl(2), to let the Stream head know that processing is complete. This is done by sending an M_IOCACK message upstream if the request was successfully processed. Always zero ioc_error, otherwise an error code could be passed to the user application. ioc_rval and ioc_count are also zeroed to reflect that a return value of 0 and no data is to be passed up (lines 124-128).

If the request cannot be processed, either an M_IOCNAK or M_IOCACK can be sent upstream with an appropriate error number. When sending an M_IOCNAK or M_IOCACK, freeing the linked M_DATA block is not mandatory, but is more efficient, as the Stream head frees it.

If ioc_error is set in an M_IOCNAK or M_IOCNACK message, this error code will be returned to the user. If no error code is set in an M_IOCNAK message, EINVAL will be returned to the user.

	xxioc(queue_t *q, mblk_t *mp)			/* M_IOCDATA processing */
	{
		struct iocblk *iocbp;
		struct copyreq *cqp;
		struct copyresp *csp;
		struct address *ap;

		csp = (struct copyresp *)mp->b_rptr;
		iocbp = (struct iocblk *)mp->b_rptr;

		/* validate this M_IOCDATA is for this module */
		switch (csp->cp_cmd) {
			case SET_ADDR:
				if (csp->cp_rval){ /*GETSTRUCT or GETADDRfail*/
					freemsg(mp);
					return;
				}
				switch ((int)csp->cp_private){ /*determine state*/
					case GETSTRUCT:					/* user structure has arrived */
						/* reuse M_IOCDATA block */
						mp->b_datap->db_type = M_COPYIN;
						mp->b_wptr = mp->b_rptr + sizeof (struct copyreq);
						cqp = (struct copyreq *)mp->b_rptr;
						/* user structure */
						ap = (struct address *)mp->b_cont->b_rptr;
						/* buffer length */
						cqp->cq_size = ap->ad_len;
						/* user space buffer address */
						cqp->cq_addr = ap->ad_addr;
						freemsg(mp->b_cont);
						mp->b_cont = NULL;
						cqp->cq_flag = 0;
						cqp->cp_private=(mblk_t *)GETADDR;  /*nxt st*/
						qreply(q, mp);
						break;

					case GETADDR:						/* user address is here */
						/* hypothetical routine */
						if (xx_set_addr(mp->b_cont) == FAILURE) {
							mp->b_datap->db_type = M_IOCNAK;
							iocbp->ioc_error = EIO;
						} else {
							mp->b_datap->db_type=M_IOCACK;/*success*/
							/* can have been overwritten */
							iocbp->ioc_error = 0;
							iocbp->ioc_count = 0;
							iocbp->ioc_rval = 0;
						}
						mp->b_wptr=mp->b_rptr + sizeof (struct ioclk);
						freemsg(mp->b_cont);
						mp->b_cont = NULL;
						qreply(q, mp);
						break;

					default: /* invalid state: can't happen */
						freemsg(mp->b_cont);
						mp->b_cont = NULL;
						mp->b_datap->db_type = M_IOCNAK;
						mp->b_wptr = mp->rptr + sizeof(struct iocblk);
						/* can have been overwritten */
						iocbp->ioc_error = EINVAL;
						qreply(q, mp);
						break;
				}
				break;						/* switch (cp_private) */

			default: /* M_IOCDATA not for us */
				/* if module, pass message on */
				/* if driver, free message */
				break;