#include <sys/types.h>
#include <sys/sysmacros.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/uio.h>
#include <sys/errno.h>
#include <sys/vmsystm.h>
#include <sys/cmn_err.h>
#include <vm/as.h>
#include <vm/page.h>
#include <sys/dcopy.h>
int64_t uioa_maxpoll = -1;
#define UIO_DCOPY_CHANNEL 0
#define UIO_DCOPY_CMD 1
int
uiomove(void *p, size_t n, enum uio_rw rw, struct uio *uio)
{
struct iovec *iov;
ulong_t cnt;
int error;
while (n && uio->uio_resid) {
iov = uio->uio_iov;
cnt = MIN(iov->iov_len, n);
if (cnt == 0l) {
uio->uio_iov++;
uio->uio_iovcnt--;
continue;
}
switch (uio->uio_segflg) {
case UIO_USERSPACE:
case UIO_USERISPACE:
if (rw == UIO_READ) {
error = xcopyout_nta(p, iov->iov_base, cnt,
(uio->uio_extflg & UIO_COPY_CACHED));
} else {
error = xcopyin_nta(iov->iov_base, p, cnt,
(uio->uio_extflg & UIO_COPY_CACHED));
}
if (error)
return (error);
break;
case UIO_SYSSPACE:
if (rw == UIO_READ)
error = kcopy_nta(p, iov->iov_base, cnt,
(uio->uio_extflg & UIO_COPY_CACHED));
else
error = kcopy_nta(iov->iov_base, p, cnt,
(uio->uio_extflg & UIO_COPY_CACHED));
if (error)
return (error);
break;
}
iov->iov_base += cnt;
iov->iov_len -= cnt;
uio->uio_resid -= cnt;
uio->uio_loffset += cnt;
p = (caddr_t)p + cnt;
n -= cnt;
}
return (0);
}
void
uio_prefaultpages(ssize_t n, struct uio *uio)
{
struct iovec *iov;
ulong_t cnt, incr;
caddr_t p;
uint8_t tmp;
int iovcnt;
iov = uio->uio_iov;
iovcnt = uio->uio_iovcnt;
while ((n > 0) && (iovcnt > 0)) {
cnt = MIN(iov->iov_len, n);
if (cnt == 0) {
iov++;
iovcnt--;
continue;
}
n -= cnt;
p = iov->iov_base;
while (cnt) {
switch (uio->uio_segflg) {
case UIO_USERSPACE:
case UIO_USERISPACE:
if (fuword8(p, &tmp))
return;
break;
case UIO_SYSSPACE:
if (kcopy(p, &tmp, 1))
return;
break;
}
incr = MIN(cnt, PAGESIZE);
p += incr;
cnt -= incr;
}
p--;
switch (uio->uio_segflg) {
case UIO_USERSPACE:
case UIO_USERISPACE:
if (fuword8(p, &tmp))
return;
break;
case UIO_SYSSPACE:
if (kcopy(p, &tmp, 1))
return;
break;
}
iov++;
iovcnt--;
}
}
int
uiocopy(void *p, size_t n, enum uio_rw rw, struct uio *uio, size_t *cbytes)
{
struct iovec *iov;
ulong_t cnt;
int error;
int iovcnt;
iovcnt = uio->uio_iovcnt;
*cbytes = 0;
for (iov = uio->uio_iov; n && iovcnt; iov++, iovcnt--) {
cnt = MIN(iov->iov_len, n);
if (cnt == 0)
continue;
switch (uio->uio_segflg) {
case UIO_USERSPACE:
case UIO_USERISPACE:
if (rw == UIO_READ) {
error = xcopyout_nta(p, iov->iov_base, cnt,
(uio->uio_extflg & UIO_COPY_CACHED));
} else {
error = xcopyin_nta(iov->iov_base, p, cnt,
(uio->uio_extflg & UIO_COPY_CACHED));
}
if (error)
return (error);
break;
case UIO_SYSSPACE:
if (rw == UIO_READ)
error = kcopy_nta(p, iov->iov_base, cnt,
(uio->uio_extflg & UIO_COPY_CACHED));
else
error = kcopy_nta(iov->iov_base, p, cnt,
(uio->uio_extflg & UIO_COPY_CACHED));
if (error)
return (error);
break;
}
p = (caddr_t)p + cnt;
n -= cnt;
*cbytes += cnt;
}
return (0);
}
int
ureadc(int val, struct uio *uiop)
{
struct iovec *iovp;
unsigned char c;
if (!(uiop && uiop->uio_resid > 0))
return (EFAULT);
while (uiop->uio_iovcnt > 0) {
iovp = uiop->uio_iov;
if (iovp->iov_len <= 0) {
uiop->uio_iovcnt--;
uiop->uio_iov++;
} else
break;
}
if (uiop->uio_iovcnt <= 0)
return (EFAULT);
c = (unsigned char) (val & 0xFF);
switch (uiop->uio_segflg) {
case UIO_USERISPACE:
case UIO_USERSPACE:
if (copyout(&c, iovp->iov_base, sizeof (unsigned char)))
return (EFAULT);
break;
case UIO_SYSSPACE:
*iovp->iov_base = c;
break;
default:
return (EFAULT);
}
iovp->iov_base++;
iovp->iov_len--;
uiop->uio_resid--;
uiop->uio_loffset++;
return (0);
}
int
uwritec(struct uio *uiop)
{
struct iovec *iovp;
unsigned char c;
if (!(uiop && uiop->uio_resid > 0))
return (-1);
while (uiop->uio_iovcnt > 0) {
iovp = uiop->uio_iov;
if (iovp->iov_len <= 0) {
uiop->uio_iovcnt--;
uiop->uio_iov++;
} else
break;
}
if (uiop->uio_iovcnt <= 0)
return (-1);
switch (uiop->uio_segflg) {
case UIO_USERISPACE:
case UIO_USERSPACE:
if (copyin(iovp->iov_base, &c, sizeof (unsigned char)))
return (-1);
break;
case UIO_SYSSPACE:
c = *iovp->iov_base;
break;
default:
return (-1);
}
iovp->iov_base++;
iovp->iov_len--;
uiop->uio_resid--;
uiop->uio_loffset++;
return ((int)c & 0xFF);
}
void
uioskip(uio_t *uiop, size_t n)
{
if (n > uiop->uio_resid)
return;
while (n != 0) {
register iovec_t *iovp = uiop->uio_iov;
register size_t niovb = MIN(iovp->iov_len, n);
if (niovb == 0) {
uiop->uio_iov++;
uiop->uio_iovcnt--;
continue;
}
iovp->iov_base += niovb;
uiop->uio_loffset += niovb;
iovp->iov_len -= niovb;
uiop->uio_resid -= niovb;
n -= niovb;
}
}
int
uiodup(uio_t *suio, uio_t *duio, iovec_t *diov, int diov_cnt)
{
int ix;
iovec_t *siov = suio->uio_iov;
*duio = *suio;
for (ix = 0; ix < suio->uio_iovcnt; ix++) {
diov[ix] = siov[ix];
if (ix >= diov_cnt)
return (1);
}
duio->uio_iov = diov;
return (0);
}
uioasync_t uioasync = {B_FALSE, 1024};
void
uioa_dcopy_enable()
{
uioasync.enabled = B_TRUE;
}
void
uioa_dcopy_disable()
{
uioasync.enabled = B_FALSE;
}
int
uioamove(void *p, size_t n, enum uio_rw rw, uioa_t *uioa)
{
int soff, doff;
uint64_t pa;
int cnt;
iovec_t *iov;
dcopy_handle_t channel;
dcopy_cmd_t cmd;
int ret = 0;
int dcopy_flags;
if (!(uioa->uioa_state & UIOA_ENABLED)) {
return (ENXIO);
}
if (uioa->uio_segflg != UIO_USERSPACE || rw != UIO_READ) {
return (ENOTSUP);
}
channel = uioa->uioa_hwst[UIO_DCOPY_CHANNEL];
cmd = uioa->uioa_hwst[UIO_DCOPY_CMD];
dcopy_flags = DCOPY_NOSLEEP;
while (n > 0 && uioa->uio_resid > 0) {
iov = uioa->uio_iov;
if (iov->iov_len == 0l) {
uioa->uio_iov++;
uioa->uio_iovcnt--;
uioa->uioa_lcur++;
uioa->uioa_lppp = uioa->uioa_lcur->uioa_ppp;
continue;
}
while (n > 0) {
soff = (uintptr_t)p & PAGEOFFSET;
doff = (uintptr_t)iov->iov_base & PAGEOFFSET;
cnt = MIN(n, iov->iov_len);
cnt = MIN(cnt, PAGESIZE - soff);
cnt = MIN(cnt, PAGESIZE - doff);
if (cmd != NULL) {
dcopy_flags |= DCOPY_ALLOC_LINK;
}
ret = dcopy_cmd_alloc(channel, dcopy_flags, &cmd);
if (ret != DCOPY_SUCCESS) {
return (EIO);
}
uioa->uioa_hwst[UIO_DCOPY_CMD] = cmd;
ASSERT(cmd->dp_version == DCOPY_CMD_V0);
if (uioa_maxpoll >= 0) {
cmd->dp_flags = DCOPY_CMD_INTR;
} else {
cmd->dp_flags = DCOPY_CMD_NOFLAGS;
}
cmd->dp_cmd = DCOPY_CMD_COPY;
pa = ptob((uint64_t)hat_getpfnum(kas.a_hat, p));
cmd->dp.copy.cc_source = pa + soff;
if (uioa->uioa_lcur->uioa_pfncnt == 0) {
pa = ptob((uint64_t)(
*(page_t **)uioa->uioa_lppp)->p_pagenum);
} else {
pa = ptob((uint64_t)(
*(pfn_t *)uioa->uioa_lppp));
}
cmd->dp.copy.cc_dest = pa + doff;
cmd->dp.copy.cc_size = cnt;
ret = dcopy_cmd_post(cmd);
if (ret != DCOPY_SUCCESS) {
return (EIO);
}
ret = 0;
if (!(uioa->uioa_state & UIOA_POLL))
uioa->uioa_state |= UIOA_POLL;
iov->iov_base += cnt;
iov->iov_len -= cnt;
uioa->uio_resid -= cnt;
uioa->uioa_mbytes += cnt;
uioa->uio_loffset += cnt;
p = (caddr_t)p + cnt;
n -= cnt;
if (iov->iov_len == 0) {
break;
}
if (doff + cnt == PAGESIZE) {
uioa->uioa_lppp++;
}
}
}
return (ret);
}
int
uioainit(uio_t *uiop, uioa_t *uioap)
{
caddr_t addr;
page_t **pages;
int off;
int len;
proc_t *procp = ttoproc(curthread);
struct as *as = procp->p_as;
iovec_t *iov = uiop->uio_iov;
int32_t iovcnt = uiop->uio_iovcnt;
uioa_page_t *locked = uioap->uioa_locked;
dcopy_handle_t channel;
int error;
if (! (uioap->uioa_state & UIOA_ALLOC)) {
return (EINVAL);
}
error = dcopy_alloc(DCOPY_NOSLEEP, &channel);
if (error == DCOPY_NORESOURCES) {
uioasync.enabled = B_FALSE;
return (ENODEV);
}
if (error != DCOPY_SUCCESS) {
return (EIO);
}
uioap->uioa_hwst[UIO_DCOPY_CHANNEL] = channel;
uioap->uioa_hwst[UIO_DCOPY_CMD] = NULL;
uioap->uioa_state = UIOA_INIT;
uioap->uioa_mbytes = 0;
*((uio_t *)uioap) = *uiop;
if (iovcnt > UIOA_IOV_MAX) {
return (E2BIG);
}
uioap->uio_iov = iov;
uioap->uio_iovcnt = iovcnt;
uioap->uio_extflg |= UIO_ASYNC;
iov = uiop->uio_iov;
iovcnt = uiop->uio_iovcnt;
while (iovcnt > 0) {
addr = iov->iov_base;
off = (uintptr_t)addr & PAGEOFFSET;
addr = (caddr_t)((uintptr_t)addr & (uintptr_t)PAGEMASK);
len = iov->iov_len + off;
if ((error = as_pagelock(as, &pages,
iov->iov_base, iov->iov_len, S_WRITE)) != 0) {
goto cleanup;
}
if (pages == NULL) {
pfn_t *pfnp;
int pcnt = len >> PAGESHIFT;
if (off)
pcnt++;
if ((pfnp = kmem_alloc(pcnt * sizeof (pfnp),
KM_NOSLEEP)) == NULL) {
error = ENOMEM;
goto cleanup;
}
locked->uioa_ppp = (void **)pfnp;
locked->uioa_pfncnt = pcnt;
AS_LOCK_ENTER(as, RW_READER);
while (pcnt-- > 0) {
*pfnp++ = hat_getpfnum(as->a_hat, addr);
addr += PAGESIZE;
}
AS_LOCK_EXIT(as);
} else {
locked->uioa_ppp = (void **)pages;
locked->uioa_pfncnt = 0;
}
locked->uioa_base = iov->iov_base;
locked->uioa_len = iov->iov_len;
locked++;
iov++;
iovcnt--;
}
uioap->uioa_lcur = uioap->uioa_locked;
uioap->uioa_lppp = uioap->uioa_lcur->uioa_ppp;
return (0);
cleanup:
while (locked > uioap->uioa_locked) {
locked--;
as_pageunlock(as, (page_t **)locked->uioa_ppp,
locked->uioa_base, locked->uioa_len, S_WRITE);
}
uioap->uioa_state = UIOA_ALLOC;
uioap->uioa_mbytes = 0;
return (error);
}
int
uioafini(uio_t *uiop, uioa_t *uioap)
{
int32_t iovcnt = uiop->uio_iovcnt;
uioa_page_t *locked = uioap->uioa_locked;
struct as *as = ttoproc(curthread)->p_as;
dcopy_handle_t channel;
dcopy_cmd_t cmd;
int ret = 0;
ASSERT(uioap->uio_extflg & UIO_ASYNC);
if (!(uioap->uioa_state & (UIOA_ENABLED|UIOA_FINI))) {
return (EINVAL);
}
channel = uioap->uioa_hwst[UIO_DCOPY_CHANNEL];
cmd = uioap->uioa_hwst[UIO_DCOPY_CMD];
if (cmd != NULL) {
if (uioap->uioa_state & UIOA_POLL) {
int64_t poll = 1;
int poll_flag = DCOPY_POLL_NOFLAGS;
do {
if (uioa_maxpoll == 0 ||
(uioa_maxpoll > 0 &&
poll >= uioa_maxpoll)) {
poll_flag = DCOPY_POLL_BLOCK;
} else {
poll++;
}
ret = dcopy_cmd_poll(cmd, poll_flag);
} while (ret == DCOPY_PENDING);
if (ret == DCOPY_COMPLETED) {
ret = 0;
} else {
ret = EIO;
}
}
dcopy_cmd_free(&cmd);
}
dcopy_free(&channel);
while (iovcnt-- > 0) {
page_t **pages;
if (locked->uioa_pfncnt == 0) {
pages = (page_t **)locked->uioa_ppp;
} else {
pages = NULL;
kmem_free(locked->uioa_ppp, locked->uioa_pfncnt *
sizeof (pfn_t *));
}
as_pageunlock(as, pages, locked->uioa_base, locked->uioa_len,
S_WRITE);
locked++;
}
*uiop = *((uio_t *)uioap);
uioap->uioa_state = UIOA_ALLOC;
uioap->uioa_mbytes = 0;
uioap->uioa_hwst[UIO_DCOPY_CMD] = NULL;
uioap->uioa_hwst[UIO_DCOPY_CHANNEL] = NULL;
return (ret);
}