STREAMS Programming Guide

M_COPYIN Example


Note -

Please see copyin() section in the Writing Device Driversfor information on the 64-bit data structure macros.


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) 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)[line 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 [lines 51-52].


Caution - Caution -

The layout of the iocblk, copyreq, and copyresp structures is different between 32-bit and 64-bit kernels. Be cautious of any data structure overloading in the cp_private or the cq_filler fields since alignment has changed.


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 an 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;