Sun Microsystems, Inc.
spacerspacer
spacer www.sun.com docs.sun.com |
spacer
black dot
 
 
8.  STREAMS Kernel-Level Mechanisms General ioctl Processing Transparent ioctl Examples M_COPYIN Example  Previous   Contents   Next 
   
 

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).

The if else statement checks whether the size count is equal to TRANSPARENT. If it is equal, the message was not generated from an I_STR ioctl(2) and the else clause of the if else executes.

	if (iocbp->ioc_count != TRANSPARENT) {
			/* do non-transparent processing here (not shown here) */
	} else {

The mblk is reused and mapped into a copyreq(9S) structure. 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. The b_cont of the copy request mblk is not needed, so it is freed and then filled with NULL.

	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;

Caution - The layout of the iocblk, copyreq, and copyresp structures is different between 32-bit and 64-bit kernels. Be careful not to overload any data structure in the cp_private or the cq_filler fields because alignment has changed.



Example 8-9 M_COPYIN: Copy the Buffer Address

	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 GETADDR fail */
					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;

On receipt of the M_IOCDATA message for the SET_ADDR command, xxioc() checks cp_rval. If an error occurred during the copyin operation, cp_rval is set. The mblk is freed 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.

              if (csp->cp_rval){ /* GETSTRUCT or GETADDR fail */
					freemsg(mp);
					return;

If no error occurred during the copyin operation, the switch statement determines whether to process the user structure, GETSTRUCT, or user address, GETADDR.

             switch ((int)csp->cp_private){  /*determine state*/

The cp_private field set to GETSTRUCT 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, 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.

				  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;

The final M_IOCDATA message arrives from the stream head. cp_private contains GETADDR. 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 an M_IOCACK message. If xx_set_addr() fails, the message is rejected with an M_IOCNAK message]. xx_set_addr() 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 set ioc_error to zero, otherwise an error code could be passed to the user application. Set ioc_rval and ioc_count to zero to reflect that a return value of 0 and no data is to be passed upstream. 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. It is more efficient to use the stream head handle to free the linked M_DATA block.

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.

                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;
 
 
 
  Previous   Contents   Next