Home | History | Annotate | Download | only in os
      1     0     stevel /*
      2     0     stevel  * CDDL HEADER START
      3     0     stevel  *
      4     0     stevel  * The contents of this file are subject to the terms of the
      5  6707     brutus  * Common Development and Distribution License (the "License").
      6  6707     brutus  * You may not use this file except in compliance with the License.
      7     0     stevel  *
      8     0     stevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9     0     stevel  * or http://www.opensolaris.org/os/licensing.
     10     0     stevel  * See the License for the specific language governing permissions
     11     0     stevel  * and limitations under the License.
     12     0     stevel  *
     13     0     stevel  * When distributing Covered Code, include this CDDL HEADER in each
     14     0     stevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15     0     stevel  * If applicable, add the following below this CDDL HEADER, with the
     16     0     stevel  * fields enclosed by brackets "[]" replaced with your own identifying
     17     0     stevel  * information: Portions Copyright [yyyy] [name of copyright owner]
     18     0     stevel  *
     19     0     stevel  * CDDL HEADER END
     20     0     stevel  */
     21     0     stevel /*
     22  9412  Aleksandr  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23     0     stevel  * Use is subject to license terms.
     24     0     stevel  */
     25     0     stevel 
     26     0     stevel /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
     27     0     stevel /*	  All Rights Reserved  	*/
     28     0     stevel 
     29     0     stevel /*
     30     0     stevel  * University Copyright- Copyright (c) 1982, 1986, 1988
     31     0     stevel  * The Regents of the University of California
     32     0     stevel  * All Rights Reserved
     33     0     stevel  *
     34     0     stevel  * University Acknowledgment- Portions of this document are derived from
     35     0     stevel  * software developed by the University of California, Berkeley, and its
     36     0     stevel  * contributors.
     37     0     stevel  */
     38     0     stevel 
     39     0     stevel #include <sys/types.h>
     40     0     stevel #include <sys/sysmacros.h>
     41     0     stevel #include <sys/param.h>
     42     0     stevel #include <sys/systm.h>
     43     0     stevel #include <sys/uio.h>
     44     0     stevel #include <sys/errno.h>
     45  6707     brutus #include <sys/vmsystm.h>
     46  6707     brutus #include <sys/cmn_err.h>
     47  6707     brutus #include <vm/as.h>
     48  6707     brutus #include <vm/page.h>
     49  6707     brutus 
     50  6707     brutus #include <sys/dcopy.h>
     51  6707     brutus 
     52  6707     brutus int64_t uioa_maxpoll = -1;	/* <0 = noblock, 0 = block, >0 = block after */
     53  6707     brutus #define	UIO_DCOPY_CHANNEL	0
     54  6707     brutus #define	UIO_DCOPY_CMD		1
     55     0     stevel 
     56     0     stevel /*
     57     0     stevel  * Move "n" bytes at byte address "p"; "rw" indicates the direction
     58     0     stevel  * of the move, and the I/O parameters are provided in "uio", which is
     59     0     stevel  * update to reflect the data which was moved.  Returns 0 on success or
     60     0     stevel  * a non-zero errno on failure.
     61     0     stevel  */
     62     0     stevel int
     63     0     stevel uiomove(void *p, size_t n, enum uio_rw rw, struct uio *uio)
     64     0     stevel {
     65     0     stevel 	struct iovec *iov;
     66     0     stevel 	ulong_t cnt;
     67     0     stevel 	int error;
     68     0     stevel 
     69     0     stevel 	while (n && uio->uio_resid) {
     70     0     stevel 		iov = uio->uio_iov;
     71     0     stevel 		cnt = MIN(iov->iov_len, n);
     72     0     stevel 		if (cnt == 0l) {
     73     0     stevel 			uio->uio_iov++;
     74     0     stevel 			uio->uio_iovcnt--;
     75     0     stevel 			continue;
     76     0     stevel 		}
     77     0     stevel 		switch (uio->uio_segflg) {
     78     0     stevel 
     79     0     stevel 		case UIO_USERSPACE:
     80     0     stevel 		case UIO_USERISPACE:
     81     0     stevel 			if (rw == UIO_READ) {
     82     0     stevel 				error = xcopyout_nta(p, iov->iov_base, cnt,
     83     0     stevel 				    (uio->uio_extflg & UIO_COPY_CACHED));
     84     0     stevel 			} else {
     85     0     stevel 				error = xcopyin_nta(iov->iov_base, p, cnt,
     86     0     stevel 				    (uio->uio_extflg & UIO_COPY_CACHED));
     87     0     stevel 			}
     88     0     stevel 
     89     0     stevel 			if (error)
     90     0     stevel 				return (error);
     91     0     stevel 			break;
     92     0     stevel 
     93     0     stevel 		case UIO_SYSSPACE:
     94     0     stevel 			if (rw == UIO_READ)
     95     0     stevel 				error = kcopy_nta(p, iov->iov_base, cnt,
     96     0     stevel 				    (uio->uio_extflg & UIO_COPY_CACHED));
     97     0     stevel 			else
     98     0     stevel 				error = kcopy_nta(iov->iov_base, p, cnt,
     99     0     stevel 				    (uio->uio_extflg & UIO_COPY_CACHED));
    100     0     stevel 			if (error)
    101     0     stevel 				return (error);
    102     0     stevel 			break;
    103     0     stevel 		}
    104     0     stevel 		iov->iov_base += cnt;
    105     0     stevel 		iov->iov_len -= cnt;
    106     0     stevel 		uio->uio_resid -= cnt;
    107     0     stevel 		uio->uio_loffset += cnt;
    108     0     stevel 		p = (caddr_t)p + cnt;
    109     0     stevel 		n -= cnt;
    110     0     stevel 	}
    111     0     stevel 	return (0);
    112  8059    Donghai }
    113  8059    Donghai 
    114  8059    Donghai /*
    115  8059    Donghai  * Fault in the pages of the first n bytes specified by the uio structure.
    116  8059    Donghai  * 1 byte in each page is touched and the uio struct is unmodified. Any
    117  8059    Donghai  * error will terminate the process as this is only a best attempt to get
    118  8059    Donghai  * the pages resident.
    119  8059    Donghai  */
    120  8059    Donghai void
    121  8059    Donghai uio_prefaultpages(ssize_t n, struct uio *uio)
    122  8059    Donghai {
    123  8059    Donghai 	struct iovec *iov;
    124  8059    Donghai 	ulong_t cnt, incr;
    125  8059    Donghai 	caddr_t p;
    126  8059    Donghai 	uint8_t tmp;
    127  8059    Donghai 	int iovcnt;
    128  8059    Donghai 
    129  8059    Donghai 	iov = uio->uio_iov;
    130  8059    Donghai 	iovcnt = uio->uio_iovcnt;
    131  8059    Donghai 
    132  8059    Donghai 	while ((n > 0) && (iovcnt > 0)) {
    133  8059    Donghai 		cnt = MIN(iov->iov_len, n);
    134  8059    Donghai 		if (cnt == 0) {
    135  8059    Donghai 			/* empty iov entry */
    136  8059    Donghai 			iov++;
    137  8059    Donghai 			iovcnt--;
    138  8059    Donghai 			continue;
    139  8059    Donghai 		}
    140  8059    Donghai 		n -= cnt;
    141  8059    Donghai 		/*
    142  8059    Donghai 		 * touch each page in this segment.
    143  8059    Donghai 		 */
    144  8059    Donghai 		p = iov->iov_base;
    145  8059    Donghai 		while (cnt) {
    146  8059    Donghai 			switch (uio->uio_segflg) {
    147  8059    Donghai 			case UIO_USERSPACE:
    148  8059    Donghai 			case UIO_USERISPACE:
    149  8059    Donghai 				if (fuword8(p, &tmp))
    150  8059    Donghai 					return;
    151  8059    Donghai 				break;
    152  8059    Donghai 			case UIO_SYSSPACE:
    153  8059    Donghai 				if (kcopy(p, &tmp, 1))
    154  8059    Donghai 					return;
    155  8059    Donghai 				break;
    156  8059    Donghai 			}
    157  8059    Donghai 			incr = MIN(cnt, PAGESIZE);
    158  8059    Donghai 			p += incr;
    159  8059    Donghai 			cnt -= incr;
    160  8059    Donghai 		}
    161  8059    Donghai 		/*
    162  8059    Donghai 		 * touch the last byte in case it straddles a page.
    163  8059    Donghai 		 */
    164  8059    Donghai 		p--;
    165  8059    Donghai 		switch (uio->uio_segflg) {
    166  8059    Donghai 		case UIO_USERSPACE:
    167  8059    Donghai 		case UIO_USERISPACE:
    168  8059    Donghai 			if (fuword8(p, &tmp))
    169  8059    Donghai 				return;
    170  8059    Donghai 			break;
    171  8059    Donghai 		case UIO_SYSSPACE:
    172  8059    Donghai 			if (kcopy(p, &tmp, 1))
    173  8059    Donghai 				return;
    174  8059    Donghai 			break;
    175  8059    Donghai 		}
    176  8059    Donghai 		iov++;
    177  8059    Donghai 		iovcnt--;
    178  8059    Donghai 	}
    179  9412  Aleksandr }
    180  9412  Aleksandr 
    181  9412  Aleksandr /*
    182  9412  Aleksandr  * same as uiomove() but doesn't modify uio structure.
    183  9412  Aleksandr  * return in cbytes how many bytes were copied.
    184  9412  Aleksandr  */
    185  9412  Aleksandr int
    186  9412  Aleksandr uiocopy(void *p, size_t n, enum uio_rw rw, struct uio *uio, size_t *cbytes)
    187  9412  Aleksandr {
    188  9412  Aleksandr 	struct iovec *iov;
    189  9412  Aleksandr 	ulong_t cnt;
    190  9412  Aleksandr 	int error;
    191  9412  Aleksandr 	int iovcnt;
    192  9412  Aleksandr 
    193  9412  Aleksandr 	iovcnt = uio->uio_iovcnt;
    194  9412  Aleksandr 	*cbytes = 0;
    195  9412  Aleksandr 
    196  9412  Aleksandr 	for (iov = uio->uio_iov; n && iovcnt; iov++, iovcnt--) {
    197  9412  Aleksandr 		cnt = MIN(iov->iov_len, n);
    198  9412  Aleksandr 		if (cnt == 0)
    199  9412  Aleksandr 			continue;
    200  9412  Aleksandr 
    201  9412  Aleksandr 		switch (uio->uio_segflg) {
    202  9412  Aleksandr 
    203  9412  Aleksandr 		case UIO_USERSPACE:
    204  9412  Aleksandr 		case UIO_USERISPACE:
    205  9412  Aleksandr 			if (rw == UIO_READ) {
    206  9412  Aleksandr 				error = xcopyout_nta(p, iov->iov_base, cnt,
    207  9412  Aleksandr 				    (uio->uio_extflg & UIO_COPY_CACHED));
    208  9412  Aleksandr 			} else {
    209  9412  Aleksandr 				error = xcopyin_nta(iov->iov_base, p, cnt,
    210  9412  Aleksandr 				    (uio->uio_extflg & UIO_COPY_CACHED));
    211  9412  Aleksandr 			}
    212  9412  Aleksandr 
    213  9412  Aleksandr 			if (error)
    214  9412  Aleksandr 				return (error);
    215  9412  Aleksandr 			break;
    216  9412  Aleksandr 
    217  9412  Aleksandr 		case UIO_SYSSPACE:
    218  9412  Aleksandr 			if (rw == UIO_READ)
    219  9412  Aleksandr 				error = kcopy_nta(p, iov->iov_base, cnt,
    220  9412  Aleksandr 				    (uio->uio_extflg & UIO_COPY_CACHED));
    221  9412  Aleksandr 			else
    222  9412  Aleksandr 				error = kcopy_nta(iov->iov_base, p, cnt,
    223  9412  Aleksandr 				    (uio->uio_extflg & UIO_COPY_CACHED));
    224  9412  Aleksandr 			if (error)
    225  9412  Aleksandr 				return (error);
    226  9412  Aleksandr 			break;
    227  9412  Aleksandr 		}
    228  9412  Aleksandr 		p = (caddr_t)p + cnt;
    229  9412  Aleksandr 		n -= cnt;
    230  9412  Aleksandr 		*cbytes += cnt;
    231  9412  Aleksandr 	}
    232  9412  Aleksandr 	return (0);
    233     0     stevel }
    234     0     stevel 
    235     0     stevel /*
    236     0     stevel  * transfer a character value into the address space
    237     0     stevel  * delineated by a uio and update fields within the
    238     0     stevel  * uio for next character. Return 0 for success, EFAULT
    239     0     stevel  * for error.
    240     0     stevel  */
    241     0     stevel int
    242     0     stevel ureadc(int val, struct uio *uiop)
    243     0     stevel {
    244     0     stevel 	struct iovec *iovp;
    245     0     stevel 	unsigned char c;
    246     0     stevel 
    247     0     stevel 	/*
    248     0     stevel 	 * first determine if uio is valid.  uiop should be
    249     0     stevel 	 * non-NULL and the resid count > 0.
    250     0     stevel 	 */
    251     0     stevel 	if (!(uiop && uiop->uio_resid > 0))
    252     0     stevel 		return (EFAULT);
    253     0     stevel 
    254     0     stevel 	/*
    255     0     stevel 	 * scan through iovecs until one is found that is non-empty.
    256     0     stevel 	 * Return EFAULT if none found.
    257     0     stevel 	 */
    258     0     stevel 	while (uiop->uio_iovcnt > 0) {
    259     0     stevel 		iovp = uiop->uio_iov;
    260     0     stevel 		if (iovp->iov_len <= 0) {
    261     0     stevel 			uiop->uio_iovcnt--;
    262     0     stevel 			uiop->uio_iov++;
    263     0     stevel 		} else
    264     0     stevel 			break;
    265     0     stevel 	}
    266     0     stevel 
    267     0     stevel 	if (uiop->uio_iovcnt <= 0)
    268     0     stevel 		return (EFAULT);
    269     0     stevel 
    270     0     stevel 	/*
    271     0     stevel 	 * Transfer character to uio space.
    272     0     stevel 	 */
    273     0     stevel 
    274     0     stevel 	c = (unsigned char) (val & 0xFF);
    275     0     stevel 
    276     0     stevel 	switch (uiop->uio_segflg) {
    277     0     stevel 
    278     0     stevel 	case UIO_USERISPACE:
    279     0     stevel 	case UIO_USERSPACE:
    280     0     stevel 		if (copyout(&c, iovp->iov_base, sizeof (unsigned char)))
    281     0     stevel 			return (EFAULT);
    282     0     stevel 		break;
    283     0     stevel 
    284     0     stevel 	case UIO_SYSSPACE: /* can do direct copy since kernel-kernel */
    285     0     stevel 		*iovp->iov_base = c;
    286     0     stevel 		break;
    287     0     stevel 
    288     0     stevel 	default:
    289     0     stevel 		return (EFAULT); /* invalid segflg value */
    290     0     stevel 	}
    291     0     stevel 
    292     0     stevel 	/*
    293     0     stevel 	 * bump up/down iovec and uio members to reflect transfer.
    294     0     stevel 	 */
    295     0     stevel 	iovp->iov_base++;
    296     0     stevel 	iovp->iov_len--;
    297     0     stevel 	uiop->uio_resid--;
    298     0     stevel 	uiop->uio_loffset++;
    299     0     stevel 	return (0); /* success */
    300     0     stevel }
    301     0     stevel 
    302     0     stevel /*
    303     0     stevel  * return a character value from the address space
    304     0     stevel  * delineated by a uio and update fields within the
    305     0     stevel  * uio for next character. Return the character for success,
    306     0     stevel  * -1 for error.
    307     0     stevel  */
    308     0     stevel int
    309     0     stevel uwritec(struct uio *uiop)
    310     0     stevel {
    311     0     stevel 	struct iovec *iovp;
    312     0     stevel 	unsigned char c;
    313     0     stevel 
    314     0     stevel 	/*
    315     0     stevel 	 * verify we were passed a valid uio structure.
    316     0     stevel 	 * (1) non-NULL uiop, (2) positive resid count
    317     0     stevel 	 * (3) there is an iovec with positive length
    318     0     stevel 	 */
    319     0     stevel 
    320     0     stevel 	if (!(uiop && uiop->uio_resid > 0))
    321     0     stevel 		return (-1);
    322     0     stevel 
    323     0     stevel 	while (uiop->uio_iovcnt > 0) {
    324     0     stevel 		iovp = uiop->uio_iov;
    325     0     stevel 		if (iovp->iov_len <= 0) {
    326     0     stevel 			uiop->uio_iovcnt--;
    327     0     stevel 			uiop->uio_iov++;
    328     0     stevel 		} else
    329     0     stevel 			break;
    330     0     stevel 	}
    331     0     stevel 
    332     0     stevel 	if (uiop->uio_iovcnt <= 0)
    333     0     stevel 		return (-1);
    334     0     stevel 
    335     0     stevel 	/*
    336     0     stevel 	 * Get the character from the uio address space.
    337     0     stevel 	 */
    338     0     stevel 	switch (uiop->uio_segflg) {
    339     0     stevel 
    340     0     stevel 	case UIO_USERISPACE:
    341     0     stevel 	case UIO_USERSPACE:
    342     0     stevel 		if (copyin(iovp->iov_base, &c, sizeof (unsigned char)))
    343     0     stevel 			return (-1);
    344     0     stevel 		break;
    345     0     stevel 
    346     0     stevel 	case UIO_SYSSPACE:
    347     0     stevel 		c = *iovp->iov_base;
    348     0     stevel 		break;
    349     0     stevel 
    350     0     stevel 	default:
    351     0     stevel 		return (-1); /* invalid segflg */
    352     0     stevel 	}
    353     0     stevel 
    354     0     stevel 	/*
    355     0     stevel 	 * Adjust fields of iovec and uio appropriately.
    356     0     stevel 	 */
    357     0     stevel 	iovp->iov_base++;
    358     0     stevel 	iovp->iov_len--;
    359     0     stevel 	uiop->uio_resid--;
    360     0     stevel 	uiop->uio_loffset++;
    361     0     stevel 	return ((int)c & 0xFF); /* success */
    362     0     stevel }
    363     0     stevel 
    364     0     stevel /*
    365     0     stevel  * Drop the next n chars out of *uiop.
    366     0     stevel  */
    367     0     stevel void
    368     0     stevel uioskip(uio_t *uiop, size_t n)
    369     0     stevel {
    370     0     stevel 	if (n > uiop->uio_resid)
    371     0     stevel 		return;
    372     0     stevel 	while (n != 0) {
    373     0     stevel 		register iovec_t	*iovp = uiop->uio_iov;
    374     0     stevel 		register size_t		niovb = MIN(iovp->iov_len, n);
    375     0     stevel 
    376     0     stevel 		if (niovb == 0) {
    377     0     stevel 			uiop->uio_iov++;
    378     0     stevel 			uiop->uio_iovcnt--;
    379     0     stevel 			continue;
    380     0     stevel 		}
    381     0     stevel 		iovp->iov_base += niovb;
    382     0     stevel 		uiop->uio_loffset += niovb;
    383     0     stevel 		iovp->iov_len -= niovb;
    384     0     stevel 		uiop->uio_resid -= niovb;
    385     0     stevel 		n -= niovb;
    386     0     stevel 	}
    387     0     stevel }
    388     0     stevel 
    389     0     stevel /*
    390     0     stevel  * Dup the suio into the duio and diovec of size diov_cnt. If diov
    391     0     stevel  * is too small to dup suio then an error will be returned, else 0.
    392     0     stevel  */
    393     0     stevel int
    394     0     stevel uiodup(uio_t *suio, uio_t *duio, iovec_t *diov, int diov_cnt)
    395     0     stevel {
    396     0     stevel 	int ix;
    397     0     stevel 	iovec_t *siov = suio->uio_iov;
    398     0     stevel 
    399     0     stevel 	*duio = *suio;
    400     0     stevel 	for (ix = 0; ix < suio->uio_iovcnt; ix++) {
    401     0     stevel 		diov[ix] = siov[ix];
    402     0     stevel 		if (ix >= diov_cnt)
    403     0     stevel 			return (1);
    404     0     stevel 	}
    405     0     stevel 	duio->uio_iov = diov;
    406     0     stevel 	return (0);
    407     0     stevel }
    408  6707     brutus 
    409  6707     brutus /*
    410  6707     brutus  * Shadow state for checking if a platform has hardware asynchronous
    411  6707     brutus  * copy capability and minimum copy size, e.g. Intel's I/OAT dma engine,
    412  6707     brutus  *
    413  6707     brutus  * Dcopy does a call-back to uioa_dcopy_enable() when a dma device calls
    414  6707     brutus  * into dcopy to register and uioa_dcopy_disable() when the device calls
    415  6707     brutus  * into dcopy to unregister.
    416  6707     brutus  */
    417  6707     brutus uioasync_t uioasync = {B_FALSE, 1024};
    418  6707     brutus 
    419  6707     brutus void
    420  6707     brutus uioa_dcopy_enable()
    421  6707     brutus {
    422  6707     brutus 	uioasync.enabled = B_TRUE;
    423  6707     brutus }
    424  6707     brutus 
    425  6707     brutus void
    426  6707     brutus uioa_dcopy_disable()
    427  6707     brutus {
    428  6707     brutus 	uioasync.enabled = B_FALSE;
    429  6707     brutus }
    430  6707     brutus 
    431  6707     brutus /*
    432  6707     brutus  * Schedule an asynchronous move of "n" bytes at byte address "p",
    433  6707     brutus  * "rw" indicates the direction of the move, I/O parameters and
    434  6707     brutus  * async state are provided in "uioa" which is update to reflect
    435  6707     brutus  * the data which is to be moved.
    436  6707     brutus  *
    437  6707     brutus  * Returns 0 on success or a non-zero errno on failure.
    438  6707     brutus  *
    439  6707     brutus  * Note, while the uioasync APIs are general purpose in design
    440  6707     brutus  * the current implementation is Intel I/OAT specific.
    441  6707     brutus  */
    442  6707     brutus int
    443  6707     brutus uioamove(void *p, size_t n, enum uio_rw rw, uioa_t *uioa)
    444  6707     brutus {
    445  6707     brutus 	int		soff, doff;
    446  6707     brutus 	uint64_t	pa;
    447  6707     brutus 	int		cnt;
    448  6707     brutus 	iovec_t		*iov;
    449  6707     brutus 	dcopy_handle_t	channel;
    450  6707     brutus 	dcopy_cmd_t	cmd;
    451  6707     brutus 	int		ret = 0;
    452  6707     brutus 	int		dcopy_flags;
    453  6707     brutus 
    454  6707     brutus 	if (!(uioa->uioa_state & UIOA_ENABLED)) {
    455  6707     brutus 		/* The uioa_t isn't enabled */
    456  6707     brutus 		return (ENXIO);
    457  6707     brutus 	}
    458  6707     brutus 
    459  6707     brutus 	if (uioa->uio_segflg != UIO_USERSPACE || rw != UIO_READ) {
    460  6707     brutus 		/* Only support to user-land from kernel */
    461  6707     brutus 		return (ENOTSUP);
    462  6707     brutus 	}
    463  6707     brutus 
    464  6707     brutus 
    465  6707     brutus 	channel = uioa->uioa_hwst[UIO_DCOPY_CHANNEL];
    466  6707     brutus 	cmd = uioa->uioa_hwst[UIO_DCOPY_CMD];
    467  6707     brutus 	dcopy_flags = DCOPY_NOSLEEP;
    468  6707     brutus 
    469  6707     brutus 	/*
    470  6707     brutus 	 * While source bytes and destination bytes.
    471  6707     brutus 	 */
    472  6707     brutus 	while (n > 0 && uioa->uio_resid > 0) {
    473  6707     brutus 		iov = uioa->uio_iov;
    474  6707     brutus 		if (iov->iov_len == 0l) {
    475  6707     brutus 			uioa->uio_iov++;
    476  6707     brutus 			uioa->uio_iovcnt--;
    477  6707     brutus 			uioa->uioa_lcur++;
    478  6707     brutus 			uioa->uioa_lppp = uioa->uioa_lcur->uioa_ppp;
    479  6707     brutus 			continue;
    480  6707     brutus 		}
    481  6707     brutus 		/*
    482  6707     brutus 		 * While source bytes schedule an async
    483  6707     brutus 		 * dma for destination page by page.
    484  6707     brutus 		 */
    485  6707     brutus 		while (n > 0) {
    486  6707     brutus 			/* Addr offset in page src/dst */
    487  6707     brutus 			soff = (uintptr_t)p & PAGEOFFSET;
    488  6707     brutus 			doff = (uintptr_t)iov->iov_base & PAGEOFFSET;
    489  6707     brutus 			/* Min copy count src and dst and page sized */
    490  6707     brutus 			cnt = MIN(n, iov->iov_len);
    491  6707     brutus 			cnt = MIN(cnt, PAGESIZE - soff);
    492  6707     brutus 			cnt = MIN(cnt, PAGESIZE - doff);
    493  6707     brutus 			/* XXX if next page(s) contiguous could use multipage */
    494  6707     brutus 
    495  6707     brutus 			/*
    496  6707     brutus 			 * if we have an old command, we want to link all
    497  6707     brutus 			 * other commands to the next command we alloced so
    498  6707     brutus 			 * we only need to track the last command but can
    499  6707     brutus 			 * still free them all.
    500  6707     brutus 			 */
    501  6707     brutus 			if (cmd != NULL) {
    502  6707     brutus 				dcopy_flags |= DCOPY_ALLOC_LINK;
    503  6707     brutus 			}
    504  6707     brutus 			ret = dcopy_cmd_alloc(channel, dcopy_flags, &cmd);
    505  6707     brutus 			if (ret != DCOPY_SUCCESS) {
    506  6707     brutus 				/* Error of some sort */
    507  6707     brutus 				return (EIO);
    508  6707     brutus 			}
    509  6707     brutus 			uioa->uioa_hwst[UIO_DCOPY_CMD] = cmd;
    510  6707     brutus 
    511  6707     brutus 			ASSERT(cmd->dp_version == DCOPY_CMD_V0);
    512  6707     brutus 			if (uioa_maxpoll >= 0) {
    513  6707     brutus 				/* Blocking (>0 may be) used in uioafini() */
    514  6707     brutus 				cmd->dp_flags = DCOPY_CMD_INTR;
    515  6707     brutus 			} else {
    516  6707     brutus 				/* Non blocking uioafini() so no intr */
    517  6707     brutus 				cmd->dp_flags = DCOPY_CMD_NOFLAGS;
    518  6707     brutus 			}
    519  6707     brutus 			cmd->dp_cmd = DCOPY_CMD_COPY;
    520  6707     brutus 			pa = ptob((uint64_t)hat_getpfnum(kas.a_hat, p));
    521  6707     brutus 			cmd->dp.copy.cc_source = pa + soff;
    522  6707     brutus 			if (uioa->uioa_lcur->uioa_pfncnt == 0) {
    523  6707     brutus 				/* Have a (page_t **) */
    524  6707     brutus 				pa = ptob((uint64_t)(
    525  6707     brutus 				    *(page_t **)uioa->uioa_lppp)->p_pagenum);
    526  6707     brutus 			} else {
    527  6707     brutus 				/* Have a (pfn_t *) */
    528  6707     brutus 				pa = ptob((uint64_t)(
    529  6707     brutus 				    *(pfn_t *)uioa->uioa_lppp));
    530  6707     brutus 			}
    531  6707     brutus 			cmd->dp.copy.cc_dest = pa + doff;
    532  6707     brutus 			cmd->dp.copy.cc_size = cnt;
    533  6707     brutus 			ret = dcopy_cmd_post(cmd);
    534  6707     brutus 			if (ret != DCOPY_SUCCESS) {
    535  6707     brutus 				/* Error of some sort */
    536  6707     brutus 				return (EIO);
    537  6707     brutus 			}
    538  6707     brutus 			ret = 0;
    539  6707     brutus 
    540  6707     brutus 			/* If UIOA_POLL not set, set it */
    541  6707     brutus 			if (!(uioa->uioa_state & UIOA_POLL))
    542  6707     brutus 				uioa->uioa_state |= UIOA_POLL;
    543  6707     brutus 
    544  6707     brutus 			/* Update iov, uio, and local pointers/counters */
    545  6707     brutus 			iov->iov_base += cnt;
    546  6707     brutus 			iov->iov_len -= cnt;
    547  6707     brutus 			uioa->uio_resid -= cnt;
    548  7660       Eric 			uioa->uioa_mbytes += cnt;
    549  6707     brutus 			uioa->uio_loffset += cnt;
    550  6707     brutus 			p = (caddr_t)p + cnt;
    551  6707     brutus 			n -= cnt;
    552  6707     brutus 
    553  6707     brutus 			/* End of iovec? */
    554  6707     brutus 			if (iov->iov_len == 0) {
    555  6707     brutus 				/* Yup, next iovec */
    556  6707     brutus 				break;
    557  6707     brutus 			}
    558  6707     brutus 
    559  6707     brutus 			/* Next dst addr page? */
    560  6707     brutus 			if (doff + cnt == PAGESIZE) {
    561  6707     brutus 				/* Yup, next page_t */
    562  6707     brutus 				uioa->uioa_lppp++;
    563  6707     brutus 			}
    564  6707     brutus 		}
    565  6707     brutus 	}
    566  6707     brutus 
    567  6707     brutus 	return (ret);
    568  6707     brutus }
    569  6707     brutus 
    570  6707     brutus /*
    571  6707     brutus  * Initialize a uioa_t for a given uio_t for the current user context,
    572  6707     brutus  * copy the common uio_t to the uioa_t, walk the shared iovec_t and
    573  6707     brutus  * lock down the user-land page(s) containing iovec_t data, then mapin
    574  6707     brutus  * user-land pages using segkpm.
    575  6707     brutus  */
    576  6707     brutus int
    577  6707     brutus uioainit(uio_t *uiop, uioa_t *uioap)
    578  6707     brutus {
    579  6707     brutus 	caddr_t	addr;
    580  6707     brutus 	page_t		**pages;
    581  6707     brutus 	int		off;
    582  6707     brutus 	int		len;
    583  6707     brutus 	proc_t		*procp = ttoproc(curthread);
    584  6707     brutus 	struct as	*as = procp->p_as;
    585  6707     brutus 	iovec_t		*iov = uiop->uio_iov;
    586  6707     brutus 	int32_t		iovcnt = uiop->uio_iovcnt;
    587  6707     brutus 	uioa_page_t	*locked = uioap->uioa_locked;
    588  6707     brutus 	dcopy_handle_t	channel;
    589  6707     brutus 	int		error;
    590  6707     brutus 
    591  6707     brutus 	if (! (uioap->uioa_state & UIOA_ALLOC)) {
    592  6707     brutus 		/* Can only init() a freshly allocated uioa_t */
    593  6707     brutus 		return (EINVAL);
    594  6707     brutus 	}
    595  6707     brutus 
    596  6707     brutus 	error = dcopy_alloc(DCOPY_NOSLEEP, &channel);
    597  6707     brutus 	if (error == DCOPY_NORESOURCES) {
    598  6707     brutus 		/* Turn off uioa */
    599  6707     brutus 		uioasync.enabled = B_FALSE;
    600  6707     brutus 		return (ENODEV);
    601  6707     brutus 	}
    602  6707     brutus 	if (error != DCOPY_SUCCESS) {
    603  6707     brutus 		/* Alloc failed */
    604  6707     brutus 		return (EIO);
    605  6707     brutus 	}
    606  6707     brutus 
    607  6707     brutus 	uioap->uioa_hwst[UIO_DCOPY_CHANNEL] = channel;
    608  6707     brutus 	uioap->uioa_hwst[UIO_DCOPY_CMD] = NULL;
    609  6707     brutus 
    610  6707     brutus 	/* Indicate uioa_t (will be) initialized */
    611  6707     brutus 	uioap->uioa_state = UIOA_INIT;
    612  7660       Eric 
    613  7660       Eric 	uioap->uioa_mbytes = 0;
    614  7660       Eric 
    615  6707     brutus 	/* uio_t/uioa_t uio_t common struct copy */
    616  6707     brutus 	*((uio_t *)uioap) = *uiop;
    617  6707     brutus 
    618  6707     brutus 	/* initialize *uiop->uio_iov */
    619  6707     brutus 	if (iovcnt > UIOA_IOV_MAX) {
    620  6707     brutus 		/* Too big? */
    621  6707     brutus 		return (E2BIG);
    622  6707     brutus 	}
    623  6707     brutus 	uioap->uio_iov = iov;
    624  6707     brutus 	uioap->uio_iovcnt = iovcnt;
    625  6707     brutus 
    626  6707     brutus 	/* Mark the uioap as such */
    627  6707     brutus 	uioap->uio_extflg |= UIO_ASYNC;
    628  6707     brutus 
    629  6707     brutus 	/*
    630  6707     brutus 	 * For each iovec_t, lock-down the page(s) backing the iovec_t
    631  6707     brutus 	 * and save the page_t list for phys addr use in uioamove().
    632  6707     brutus 	 */
    633  6707     brutus 	iov = uiop->uio_iov;
    634  6707     brutus 	iovcnt = uiop->uio_iovcnt;
    635  6707     brutus 	while (iovcnt > 0) {
    636  6707     brutus 		addr = iov->iov_base;
    637  6707     brutus 		off = (uintptr_t)addr & PAGEOFFSET;
    638  6707     brutus 		addr = (caddr_t)((uintptr_t)addr & (uintptr_t)PAGEMASK);
    639  6707     brutus 		len = iov->iov_len + off;
    640  6707     brutus 
    641  6707     brutus 		/* Lock down page(s) for the iov span */
    642  6707     brutus 		if ((error = as_pagelock(as, &pages,
    643  6707     brutus 		    iov->iov_base, iov->iov_len, S_WRITE)) != 0) {
    644  6707     brutus 			/* Error */
    645  6707     brutus 			goto cleanup;
    646  6707     brutus 		}
    647  6707     brutus 
    648  6707     brutus 		if (pages == NULL) {
    649  6707     brutus 			/*
    650  6707     brutus 			 * Need page_t list, really only need
    651  6707     brutus 			 * a pfn list so build one.
    652  6707     brutus 			 */
    653  6707     brutus 			pfn_t   *pfnp;
    654  6707     brutus 			int	pcnt = len >> PAGESHIFT;
    655  6707     brutus 
    656  6707     brutus 			if (off)
    657  6707     brutus 				pcnt++;
    658  6707     brutus 			if ((pfnp = kmem_alloc(pcnt * sizeof (pfnp),
    659  6707     brutus 			    KM_NOSLEEP)) == NULL) {
    660  6707     brutus 				error = ENOMEM;
    661  6707     brutus 				goto cleanup;
    662  6707     brutus 			}
    663  6707     brutus 			locked->uioa_ppp = (void **)pfnp;
    664  6707     brutus 			locked->uioa_pfncnt = pcnt;
    665  6707     brutus 			AS_LOCK_ENTER(as, &as->a_lock, RW_READER);
    666  6707     brutus 			while (pcnt-- > 0) {
    667  6707     brutus 				*pfnp++ = hat_getpfnum(as->a_hat, addr);
    668  6707     brutus 				addr += PAGESIZE;
    669  6707     brutus 			}
    670  6707     brutus 			AS_LOCK_EXIT(as, &as->a_lock);
    671  6707     brutus 		} else {
    672  6707     brutus 			/* Have a page_t list, save it */
    673  6707     brutus 			locked->uioa_ppp = (void **)pages;
    674  6707     brutus 			locked->uioa_pfncnt = 0;
    675  6707     brutus 		}
    676  6707     brutus 		/* Save for as_pageunlock() in uioafini() */
    677  6707     brutus 		locked->uioa_base = iov->iov_base;
    678  6707     brutus 		locked->uioa_len = iov->iov_len;
    679  6707     brutus 		locked++;
    680  6707     brutus 
    681  6707     brutus 		/* Next iovec_t */
    682  6707     brutus 		iov++;
    683  6707     brutus 		iovcnt--;
    684  6707     brutus 	}
    685  6707     brutus 	/* Initialize curret pointer into uioa_locked[] and it's uioa_ppp */
    686  6707     brutus 	uioap->uioa_lcur = uioap->uioa_locked;
    687  6707     brutus 	uioap->uioa_lppp = uioap->uioa_lcur->uioa_ppp;
    688  6707     brutus 	return (0);
    689  6707     brutus 
    690  6707     brutus cleanup:
    691  6707     brutus 	/* Unlock any previously locked page_t(s) */
    692  6707     brutus 	while (locked > uioap->uioa_locked) {
    693  6707     brutus 		locked--;
    694  6707     brutus 		as_pageunlock(as, (page_t **)locked->uioa_ppp,
    695  6707     brutus 		    locked->uioa_base, locked->uioa_len, S_WRITE);
    696  6707     brutus 	}
    697  6707     brutus 
    698  6707     brutus 	/* Last indicate uioa_t still in alloc state */
    699  6707     brutus 	uioap->uioa_state = UIOA_ALLOC;
    700  7660       Eric 	uioap->uioa_mbytes = 0;
    701  6707     brutus 
    702  6707     brutus 	return (error);
    703  6707     brutus }
    704  6707     brutus 
    705  6707     brutus /*
    706  6707     brutus  * Finish processing of a uioa_t by cleanup any pending "uioap" actions.
    707  6707     brutus  */
    708  6707     brutus int
    709  6707     brutus uioafini(uio_t *uiop, uioa_t *uioap)
    710  6707     brutus {
    711  6707     brutus 	int32_t		iovcnt = uiop->uio_iovcnt;
    712  6707     brutus 	uioa_page_t	*locked = uioap->uioa_locked;
    713  6707     brutus 	struct as	*as = ttoproc(curthread)->p_as;
    714  6707     brutus 	dcopy_handle_t	channel;
    715  6707     brutus 	dcopy_cmd_t	cmd;
    716  6707     brutus 	int		ret = 0;
    717  6707     brutus 
    718  6707     brutus 	ASSERT(uioap->uio_extflg & UIO_ASYNC);
    719  6707     brutus 
    720  6707     brutus 	if (!(uioap->uioa_state & (UIOA_ENABLED|UIOA_FINI))) {
    721  6707     brutus 		/* Must be an active uioa_t */
    722  6707     brutus 		return (EINVAL);
    723  6707     brutus 	}
    724  6707     brutus 
    725  6707     brutus 	channel = uioap->uioa_hwst[UIO_DCOPY_CHANNEL];
    726  6707     brutus 	cmd = uioap->uioa_hwst[UIO_DCOPY_CMD];
    727  6707     brutus 
    728  6707     brutus 	/* XXX - why do we get cmd == NULL sometimes? */
    729  6707     brutus 	if (cmd != NULL) {
    730  6707     brutus 		if (uioap->uioa_state & UIOA_POLL) {
    731  6707     brutus 			/* Wait for last dcopy() to finish */
    732  6707     brutus 			int64_t poll = 1;
    733  6707     brutus 			int poll_flag = DCOPY_POLL_NOFLAGS;
    734  6707     brutus 
    735  6707     brutus 			do {
    736  6707     brutus 				if (uioa_maxpoll == 0 ||
    737  6707     brutus 				    (uioa_maxpoll > 0 &&
    738  6707     brutus 				    poll >= uioa_maxpoll)) {
    739  6707     brutus 					/* Always block or after maxpoll */
    740  6707     brutus 					poll_flag = DCOPY_POLL_BLOCK;
    741  6707     brutus 				} else {
    742  6707     brutus 					/* No block, poll */
    743  6707     brutus 					poll++;
    744  6707     brutus 				}
    745  6707     brutus 				ret = dcopy_cmd_poll(cmd, poll_flag);
    746  6707     brutus 			} while (ret == DCOPY_PENDING);
    747  6707     brutus 
    748  6707     brutus 			if (ret == DCOPY_COMPLETED) {
    749  6707     brutus 				/* Poll/block succeeded */
    750  6707     brutus 				ret = 0;
    751  6707     brutus 			} else {
    752  6707     brutus 				/* Poll/block failed */
    753  6707     brutus 				ret = EIO;
    754  6707     brutus 			}
    755  6707     brutus 		}
    756  6707     brutus 		dcopy_cmd_free(&cmd);
    757  6707     brutus 	}
    758  6707     brutus 
    759  6707     brutus 	dcopy_free(&channel);
    760  6707     brutus 
    761  6707     brutus 	/* Unlock all page(s) iovec_t by iovec_t */
    762  6707     brutus 	while (iovcnt-- > 0) {
    763  6707     brutus 		page_t **pages;
    764  6707     brutus 
    765  6707     brutus 		if (locked->uioa_pfncnt == 0) {
    766  6707     brutus 			/* A as_pagelock() returned (page_t **) */
    767  6707     brutus 			pages = (page_t **)locked->uioa_ppp;
    768  6707     brutus 		} else {
    769  6707     brutus 			/* Our pfn_t array */
    770  6707     brutus 			pages = NULL;
    771  6707     brutus 			kmem_free(locked->uioa_ppp, locked->uioa_pfncnt *
    772  6707     brutus 			    sizeof (pfn_t *));
    773  6707     brutus 		}
    774  6707     brutus 		as_pageunlock(as, pages, locked->uioa_base, locked->uioa_len,
    775  6707     brutus 		    S_WRITE);
    776  6707     brutus 
    777  6707     brutus 		locked++;
    778  6707     brutus 	}
    779  6707     brutus 	/* uioa_t->uio_t common struct copy */
    780  6707     brutus 	*uiop = *((uio_t *)uioap);
    781  6707     brutus 
    782  6707     brutus 	/*
    783  6707     brutus 	 * Last, reset uioa state to alloc.
    784  6707     brutus 	 *
    785  6707     brutus 	 * Note, we only initialize the state here, all other members
    786  6707     brutus 	 * will be initialized in a subsequent uioainit().
    787  6707     brutus 	 */
    788  6707     brutus 	uioap->uioa_state = UIOA_ALLOC;
    789  7660       Eric 	uioap->uioa_mbytes = 0;
    790  6707     brutus 
    791  6707     brutus 	uioap->uioa_hwst[UIO_DCOPY_CMD] = NULL;
    792  6707     brutus 	uioap->uioa_hwst[UIO_DCOPY_CHANNEL] = NULL;
    793  6707     brutus 
    794  6707     brutus 	return (ret);
    795  6707     brutus }
    796