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