#include <sys/types.h>
#include <sys/proc.h>
#include <sys/file.h>
#include <sys/errno.h>
#include <sys/param.h>
#include <sys/sysmacros.h>
#include <sys/cmn_err.h>
#include <sys/systm.h>
#include <vm/as.h>
#include <vm/page.h>
#include <sys/uio.h>
#include <sys/kmem.h>
#include <sys/debug.h>
#include <sys/aio_impl.h>
#include <sys/epm.h>
#include <sys/fs/snode.h>
#include <sys/siginfo.h>
#include <sys/cpuvar.h>
#include <sys/conf.h>
#include <sys/sdt.h>
int aphysio(int (*)(), int (*)(), dev_t, int, void (*)(), struct aio_req *);
int aio_done(struct buf *);
void aphysio_unlock(aio_req_t *);
void aio_cleanup(int);
void aio_cleanup_exit(void);
static void aio_sigev_send(proc_t *, sigqueue_t *);
static void aio_hash_delete(aio_t *, aio_req_t *);
static void aio_lio_free(aio_t *, aio_lio_t *);
static int aio_cleanup_cleanupq(aio_t *, aio_req_t *, int);
static int aio_cleanup_notifyq(aio_t *, aio_req_t *, int);
static void aio_cleanup_pollq(aio_t *, aio_req_t *, int);
static void aio_cleanup_portq(aio_t *, aio_req_t *, int);
int
aphysio(
int (*strategy)(struct buf *),
int (*cancel)(struct buf *),
dev_t dev,
int rw,
void (*mincnt)(struct buf *),
struct aio_req *aio)
{
struct uio *uio = aio->aio_uio;
aio_req_t *reqp = (aio_req_t *)aio->aio_private;
struct buf *bp = &reqp->aio_req_buf;
struct iovec *iov;
struct as *as;
char *a;
int error;
size_t c;
struct page **pplist;
struct dev_ops *ops = devopsp[getmajor(dev)];
if (uio->uio_loffset < 0)
return (EINVAL);
#ifdef _ILP32
if (uio->uio_loffset > SPEC_MAXOFFSET_T)
return (EINVAL);
#endif
if (rw == B_READ) {
CPU_STATS_ADD_K(sys, phread, 1);
} else {
CPU_STATS_ADD_K(sys, phwrite, 1);
}
iov = uio->uio_iov;
sema_init(&bp->b_sem, 0, NULL, SEMA_DEFAULT, NULL);
sema_init(&bp->b_io, 0, NULL, SEMA_DEFAULT, NULL);
bp->b_error = 0;
bp->b_flags = B_BUSY | B_PHYS | B_ASYNC | rw;
bp->b_edev = dev;
bp->b_dev = cmpdev(dev);
bp->b_lblkno = btodt(uio->uio_loffset);
bp->b_offset = uio->uio_loffset;
(void) ops->devo_getinfo(NULL, DDI_INFO_DEVT2DEVINFO,
(void *)bp->b_edev, (void **)&bp->b_dip);
if (bp->b_iodone == NULL) {
bp->b_iodone = aio_done;
bp->b_forw = (struct buf *)reqp;
bp->b_proc = curproc;
}
a = bp->b_un.b_addr = iov->iov_base;
c = bp->b_bcount = iov->iov_len;
(*mincnt)(bp);
if (bp->b_bcount != iov->iov_len)
return (ENOTSUP);
as = bp->b_proc->p_as;
error = as_pagelock(as, &pplist, a,
c, rw == B_READ? S_WRITE : S_READ);
if (error != 0) {
bp->b_flags |= B_ERROR;
bp->b_error = error;
bp->b_flags &= ~(B_BUSY|B_WANTED|B_PHYS|B_SHADOW);
return (error);
}
reqp->aio_req_flags |= AIO_PAGELOCKDONE;
bp->b_shadow = pplist;
if (pplist != NULL) {
bp->b_flags |= B_SHADOW;
}
if (cancel != anocancel)
cmn_err(CE_PANIC,
"aphysio: cancellation not supported, use anocancel");
reqp->aio_req_cancel = cancel;
DTRACE_IO1(start, struct buf *, bp);
return ((*strategy)(bp));
}
int
anocancel(struct buf *bp)
{
return (ENXIO);
}
int
aio_done(struct buf *bp)
{
proc_t *p;
struct as *as;
aio_req_t *reqp;
aio_lio_t *head = NULL;
aio_t *aiop;
sigqueue_t *sigev = NULL;
sigqueue_t *lio_sigev = NULL;
port_kevent_t *pkevp = NULL;
port_kevent_t *lio_pkevp = NULL;
int fd;
int cleanupqflag;
int pollqflag;
int portevpend;
void (*func)();
int use_port = 0;
int reqp_flags = 0;
int send_signal = 0;
p = bp->b_proc;
as = p->p_as;
reqp = (aio_req_t *)bp->b_forw;
fd = reqp->aio_req_fd;
if (bp->b_flags & B_REMAPPED)
bp_mapout(bp);
areleasef(fd, P_FINFO(p));
aiop = p->p_aio;
ASSERT(aiop != NULL);
mutex_enter(&aiop->aio_portq_mutex);
mutex_enter(&aiop->aio_mutex);
ASSERT(aiop->aio_pending > 0);
ASSERT(reqp->aio_req_flags & AIO_PENDING);
aiop->aio_pending--;
reqp->aio_req_flags &= ~AIO_PENDING;
reqp_flags = reqp->aio_req_flags;
if ((pkevp = reqp->aio_req_portkev) != NULL) {
if (reqp->aio_req_flags & AIO_CLOSE_PORT) {
portevpend = --aiop->aio_portpendcnt;
aio_deq(&aiop->aio_portpending, reqp);
aio_enq(&aiop->aio_portq, reqp, 0);
mutex_exit(&aiop->aio_mutex);
mutex_exit(&aiop->aio_portq_mutex);
port_send_event(pkevp);
if (portevpend == 0)
cv_broadcast(&aiop->aio_portcv);
return (0);
}
if (aiop->aio_flags & AIO_CLEANUP) {
mutex_enter(&as->a_contents);
aio_deq(&aiop->aio_portpending, reqp);
aio_enq(&aiop->aio_portcleanupq, reqp, 0);
cv_signal(&aiop->aio_cleanupcv);
mutex_exit(&as->a_contents);
mutex_exit(&aiop->aio_mutex);
mutex_exit(&aiop->aio_portq_mutex);
return (0);
}
aio_deq(&aiop->aio_portpending, reqp);
aio_enq(&aiop->aio_portq, reqp, 0);
use_port = 1;
} else {
cleanupqflag = (aiop->aio_flags & AIO_CLEANUP);
pollqflag = (reqp->aio_req_flags & AIO_POLL);
if (cleanupqflag | pollqflag) {
if (cleanupqflag)
mutex_enter(&as->a_contents);
if (pollqflag)
aio_enq(&aiop->aio_pollq, reqp, AIO_POLLQ);
else if (reqp->aio_req_sigqp || reqp->aio_req_lio)
aio_enq(&aiop->aio_notifyq, reqp, AIO_NOTIFYQ);
else
aio_enq(&aiop->aio_cleanupq, reqp,
AIO_CLEANUPQ);
if (cleanupqflag) {
cv_signal(&aiop->aio_cleanupcv);
mutex_exit(&as->a_contents);
mutex_exit(&aiop->aio_mutex);
mutex_exit(&aiop->aio_portq_mutex);
} else {
ASSERT(pollqflag);
aiop->aio_done_cnt++;
mutex_exit(&aiop->aio_mutex);
mutex_exit(&aiop->aio_portq_mutex);
mutex_enter(&p->p_lock);
set_proc_ast(p);
mutex_exit(&p->p_lock);
mutex_enter(&aiop->aio_mutex);
cv_broadcast(&aiop->aio_waitcv);
ASSERT3U(aiop->aio_done_cnt, >, 0);
aiop->aio_done_cnt--;
if ((aiop->aio_flags & AIO_CLEANUP) &&
aiop->aio_done_cnt == 0) {
cv_signal(&aiop->aio_cleanupcv);
}
mutex_exit(&aiop->aio_mutex);
}
return (0);
}
sigev = reqp->aio_req_sigqp;
reqp->aio_req_sigqp = NULL;
aio_enq(&aiop->aio_doneq, reqp, AIO_DONEQ);
}
if ((head = reqp->aio_req_lio) != NULL) {
ASSERT(head->lio_refcnt > 0);
if (--head->lio_refcnt == 0) {
lio_sigev = head->lio_sigqp;
head->lio_sigqp = NULL;
cv_signal(&head->lio_notify);
if (head->lio_port >= 0 &&
(lio_pkevp = head->lio_portkev) != NULL)
head->lio_port = -1;
}
}
if (aiop->aio_flags & AIO_WAITN) {
if (aiop->aio_waitncnt > 0)
aiop->aio_waitncnt--;
if (aiop->aio_pending == 0 ||
aiop->aio_waitncnt == 0)
cv_broadcast(&aiop->aio_waitcv);
} else {
cv_broadcast(&aiop->aio_waitcv);
}
if (!sigev && !use_port && head == NULL &&
(reqp->aio_req_flags & AIO_SOLARIS) &&
(func = PTOU(p)->u_signal[SIGIO - 1]) != SIG_DFL &&
(func != SIG_IGN)) {
send_signal = 1;
reqp->aio_req_flags |= AIO_SIGNALLED;
}
mutex_exit(&aiop->aio_mutex);
mutex_exit(&aiop->aio_portq_mutex);
mutex_enter(&as->a_contents);
if ((reqp_flags & AIO_PAGELOCKDONE) && AS_ISUNMAPWAIT(as))
cv_broadcast(&as->a_cv);
mutex_exit(&as->a_contents);
if (sigev)
aio_sigev_send(p, sigev);
else if (send_signal)
psignal(p, SIGIO);
if (pkevp)
port_send_event(pkevp);
if (lio_sigev)
aio_sigev_send(p, lio_sigev);
if (lio_pkevp)
port_send_event(lio_pkevp);
return (0);
}
static void
aio_sigev_send(proc_t *p, sigqueue_t *sigev)
{
ASSERT(sigev != NULL);
mutex_enter(&p->p_lock);
sigaddqa(p, NULL, sigev);
mutex_exit(&p->p_lock);
}
void
aio_zerolen(aio_req_t *reqp)
{
struct buf *bp = &reqp->aio_req_buf;
reqp->aio_req_flags |= AIO_ZEROLEN;
bp->b_forw = (struct buf *)reqp;
bp->b_proc = curproc;
bp->b_resid = 0;
bp->b_flags = 0;
aio_done(bp);
}
void
aphysio_unlock(aio_req_t *reqp)
{
struct buf *bp;
struct iovec *iov;
int flags;
if (reqp->aio_req_flags & AIO_PHYSIODONE)
return;
reqp->aio_req_flags |= AIO_PHYSIODONE;
if (reqp->aio_req_flags & AIO_ZEROLEN)
return;
bp = &reqp->aio_req_buf;
iov = reqp->aio_req_uio.uio_iov;
flags = (((bp->b_flags & B_READ) == B_READ) ? S_WRITE : S_READ);
if (reqp->aio_req_flags & AIO_PAGELOCKDONE) {
as_pageunlock(bp->b_proc->p_as,
bp->b_flags & B_SHADOW ? bp->b_shadow : NULL,
iov->iov_base, iov->iov_len, flags);
reqp->aio_req_flags &= ~AIO_PAGELOCKDONE;
}
bp->b_flags &= ~(B_BUSY|B_WANTED|B_PHYS|B_SHADOW);
bp->b_flags |= B_DONE;
}
static void
aio_hash_delete(aio_t *aiop, struct aio_req_t *reqp)
{
long index;
aio_result_t *resultp = reqp->aio_req_resultp;
aio_req_t *current;
aio_req_t **nextp;
index = AIO_HASH(resultp);
nextp = (aiop->aio_hash + index);
while ((current = *nextp) != NULL) {
if (current->aio_req_resultp == resultp) {
*nextp = current->aio_hash_next;
return;
}
nextp = ¤t->aio_hash_next;
}
}
static void
aio_lio_free(aio_t *aiop, aio_lio_t *head)
{
ASSERT(MUTEX_HELD(&aiop->aio_mutex));
if (head->lio_sigqp != NULL)
kmem_free(head->lio_sigqp, sizeof (sigqueue_t));
head->lio_next = aiop->aio_lio_free;
aiop->aio_lio_free = head;
}
void
aio_req_free(aio_t *aiop, aio_req_t *reqp)
{
aio_lio_t *liop;
ASSERT(MUTEX_HELD(&aiop->aio_mutex));
if (reqp->aio_req_portkev) {
port_free_event(reqp->aio_req_portkev);
reqp->aio_req_portkev = NULL;
}
if ((liop = reqp->aio_req_lio) != NULL) {
if (--liop->lio_nent == 0)
aio_lio_free(aiop, liop);
reqp->aio_req_lio = NULL;
}
if (reqp->aio_req_sigqp != NULL) {
kmem_free(reqp->aio_req_sigqp, sizeof (sigqueue_t));
reqp->aio_req_sigqp = NULL;
}
reqp->aio_req_next = aiop->aio_free;
reqp->aio_req_prev = NULL;
aiop->aio_free = reqp;
aiop->aio_outstanding--;
if (aiop->aio_outstanding == 0)
cv_broadcast(&aiop->aio_waitcv);
aio_hash_delete(aiop, reqp);
}
void
aio_req_free_port(aio_t *aiop, aio_req_t *reqp)
{
ASSERT(MUTEX_HELD(&aiop->aio_mutex));
reqp->aio_req_next = aiop->aio_free;
reqp->aio_req_prev = NULL;
aiop->aio_free = reqp;
aiop->aio_outstanding--;
aio_hash_delete(aiop, reqp);
}
#if defined(DEBUG)
static void
aio_verify_queue(aio_req_t *head, aio_req_t *entry_present,
aio_req_t *entry_missing)
{
aio_req_t *reqp;
int found = 0;
int present = 0;
if ((reqp = head) != NULL) {
do {
ASSERT(reqp->aio_req_prev->aio_req_next == reqp);
ASSERT(reqp->aio_req_next->aio_req_prev == reqp);
if (entry_present == reqp)
found++;
if (entry_missing == reqp)
present++;
} while ((reqp = reqp->aio_req_next) != head);
}
ASSERT(entry_present == NULL || found == 1);
ASSERT(entry_missing == NULL || present == 0);
}
#else
#define aio_verify_queue(x, y, z)
#endif
void
aio_enq(aio_req_t **qhead, aio_req_t *reqp, int qflg_new)
{
aio_req_t *head;
aio_req_t *prev;
aio_verify_queue(*qhead, NULL, reqp);
if ((head = *qhead) == NULL) {
reqp->aio_req_next = reqp;
reqp->aio_req_prev = reqp;
*qhead = reqp;
} else {
reqp->aio_req_next = head;
reqp->aio_req_prev = prev = head->aio_req_prev;
prev->aio_req_next = reqp;
head->aio_req_prev = reqp;
}
reqp->aio_req_flags |= qflg_new;
}
void
aio_deq(aio_req_t **qhead, aio_req_t *reqp)
{
aio_verify_queue(*qhead, reqp, NULL);
if (reqp->aio_req_next == reqp) {
*qhead = NULL;
} else {
reqp->aio_req_prev->aio_req_next = reqp->aio_req_next;
reqp->aio_req_next->aio_req_prev = reqp->aio_req_prev;
if (*qhead == reqp)
*qhead = reqp->aio_req_next;
}
reqp->aio_req_next = NULL;
reqp->aio_req_prev = NULL;
}
void
aio_cleanupq_concat(aio_t *aiop, aio_req_t *q2, int qflg)
{
aio_req_t *cleanupqhead, *q2tail;
aio_req_t *reqp = q2;
do {
ASSERT(reqp->aio_req_flags & qflg);
reqp->aio_req_flags &= ~qflg;
reqp->aio_req_flags |= AIO_CLEANUPQ;
} while ((reqp = reqp->aio_req_next) != q2);
cleanupqhead = aiop->aio_cleanupq;
if (cleanupqhead == NULL)
aiop->aio_cleanupq = q2;
else {
cleanupqhead->aio_req_prev->aio_req_next = q2;
q2tail = q2->aio_req_prev;
q2tail->aio_req_next = cleanupqhead;
q2->aio_req_prev = cleanupqhead->aio_req_prev;
cleanupqhead->aio_req_prev = q2tail;
}
}
void
aio_cleanup(int flag)
{
aio_t *aiop = curproc->p_aio;
aio_req_t *pollqhead, *cleanupqhead, *notifyqhead;
aio_req_t *cleanupport;
aio_req_t *portq = NULL;
void (*func)();
int signalled = 0;
int qflag = 0;
int exitflg;
ASSERT(aiop != NULL);
if (flag == AIO_CLEANUP_EXIT)
exitflg = AIO_CLEANUP_EXIT;
else
exitflg = 0;
mutex_enter(&aiop->aio_cleanupq_mutex);
mutex_enter(&aiop->aio_mutex);
if ((cleanupqhead = aiop->aio_cleanupq) != NULL) {
aiop->aio_cleanupq = NULL;
qflag++;
}
if ((notifyqhead = aiop->aio_notifyq) != NULL) {
aiop->aio_notifyq = NULL;
qflag++;
}
if ((pollqhead = aiop->aio_pollq) != NULL) {
aiop->aio_pollq = NULL;
qflag++;
}
if (flag) {
if ((portq = aiop->aio_portq) != NULL)
qflag++;
if ((cleanupport = aiop->aio_portcleanupq) != NULL) {
aiop->aio_portcleanupq = NULL;
qflag++;
}
}
mutex_exit(&aiop->aio_mutex);
if (!qflag) {
mutex_exit(&aiop->aio_cleanupq_mutex);
return;
}
if (cleanupqhead)
signalled = aio_cleanup_cleanupq(aiop, cleanupqhead, exitflg);
mutex_exit(&aiop->aio_cleanupq_mutex);
if (notifyqhead)
signalled = aio_cleanup_notifyq(aiop, notifyqhead, exitflg);
if (pollqhead)
aio_cleanup_pollq(aiop, pollqhead, exitflg);
if (flag && (cleanupport || portq))
aio_cleanup_portq(aiop, cleanupport, exitflg);
if (exitflg)
return;
mutex_enter(&aiop->aio_mutex);
if (!(aiop->aio_flags & AIO_SOLARIS_REQ))
signalled = 1;
cv_broadcast(&aiop->aio_waitcv);
mutex_exit(&aiop->aio_mutex);
if (!signalled &&
(func = PTOU(curproc)->u_signal[SIGIO - 1]) != SIG_DFL &&
func != SIG_IGN)
psignal(curproc, SIGIO);
}
static void
aio_cleanup_portq(aio_t *aiop, aio_req_t *cleanupq, int exitflag)
{
aio_req_t *reqp;
aio_req_t *next;
aio_req_t *headp;
aio_lio_t *liop;
if (exitflag || ((aiop->aio_flags & AIO_CLEANUP_PORT) == 0)) {
mutex_enter(&aiop->aio_mutex);
if (aiop->aio_flags & AIO_CLEANUP)
aiop->aio_flags |= AIO_CLEANUP_PORT;
mutex_exit(&aiop->aio_mutex);
mutex_enter(&aiop->aio_portq_mutex);
headp = aiop->aio_portq;
aiop->aio_portq = NULL;
mutex_exit(&aiop->aio_portq_mutex);
if ((reqp = headp) != NULL) {
do {
next = reqp->aio_req_next;
aphysio_unlock(reqp);
if (exitflag) {
mutex_enter(&aiop->aio_mutex);
aio_req_free(aiop, reqp);
mutex_exit(&aiop->aio_mutex);
}
} while ((reqp = next) != headp);
}
if (headp != NULL && exitflag == 0) {
aio_req_t *newq;
mutex_enter(&aiop->aio_portq_mutex);
if ((newq = aiop->aio_portq) != NULL) {
aio_req_t *headprev = headp->aio_req_prev;
aio_req_t *newqprev = newq->aio_req_prev;
headp->aio_req_prev = newqprev;
newq->aio_req_prev = headprev;
headprev->aio_req_next = newq;
newqprev->aio_req_next = headp;
}
aiop->aio_portq = headp;
cv_broadcast(&aiop->aio_portcv);
mutex_exit(&aiop->aio_portq_mutex);
}
}
if ((reqp = cleanupq) == NULL)
return;
do {
next = reqp->aio_req_next;
aphysio_unlock(reqp);
if (exitflag) {
mutex_enter(&aiop->aio_mutex);
aio_req_free(aiop, reqp);
mutex_exit(&aiop->aio_mutex);
} else {
mutex_enter(&aiop->aio_portq_mutex);
aio_enq(&aiop->aio_portq, reqp, 0);
mutex_exit(&aiop->aio_portq_mutex);
port_send_event(reqp->aio_req_portkev);
if ((liop = reqp->aio_req_lio) != NULL) {
int send_event = 0;
mutex_enter(&aiop->aio_mutex);
ASSERT(liop->lio_refcnt > 0);
if (--liop->lio_refcnt == 0) {
if (liop->lio_port >= 0 &&
liop->lio_portkev) {
liop->lio_port = -1;
send_event = 1;
}
}
mutex_exit(&aiop->aio_mutex);
if (send_event)
port_send_event(liop->lio_portkev);
}
}
} while ((reqp = next) != cleanupq);
}
static int
aio_cleanup_cleanupq(aio_t *aiop, aio_req_t *qhead, int exitflg)
{
aio_req_t *reqp, *next;
int signalled = 0;
ASSERT(MUTEX_HELD(&aiop->aio_cleanupq_mutex));
if ((reqp = qhead) == NULL)
return (0);
do {
ASSERT(reqp->aio_req_flags & AIO_CLEANUPQ);
ASSERT(reqp->aio_req_portkev == NULL);
next = reqp->aio_req_next;
aphysio_unlock(reqp);
mutex_enter(&aiop->aio_mutex);
if (exitflg)
aio_req_free(aiop, reqp);
else
aio_enq(&aiop->aio_doneq, reqp, AIO_DONEQ);
if (!exitflg) {
if (reqp->aio_req_flags & AIO_SIGNALLED)
signalled++;
else
reqp->aio_req_flags |= AIO_SIGNALLED;
}
mutex_exit(&aiop->aio_mutex);
} while ((reqp = next) != qhead);
return (signalled);
}
static int
aio_cleanup_notifyq(aio_t *aiop, aio_req_t *qhead, int exitflg)
{
aio_req_t *reqp, *next;
aio_lio_t *liohead;
sigqueue_t *sigev, *lio_sigev = NULL;
int signalled = 0;
if ((reqp = qhead) == NULL)
return (0);
do {
ASSERT(reqp->aio_req_flags & AIO_NOTIFYQ);
next = reqp->aio_req_next;
aphysio_unlock(reqp);
if (exitflg) {
mutex_enter(&aiop->aio_mutex);
aio_req_free(aiop, reqp);
mutex_exit(&aiop->aio_mutex);
} else {
mutex_enter(&aiop->aio_mutex);
aio_enq(&aiop->aio_doneq, reqp, AIO_DONEQ);
sigev = reqp->aio_req_sigqp;
reqp->aio_req_sigqp = NULL;
if ((liohead = reqp->aio_req_lio) != NULL) {
ASSERT(liohead->lio_refcnt > 0);
if (--liohead->lio_refcnt == 0) {
cv_signal(&liohead->lio_notify);
lio_sigev = liohead->lio_sigqp;
liohead->lio_sigqp = NULL;
}
}
mutex_exit(&aiop->aio_mutex);
if (sigev) {
signalled++;
aio_sigev_send(reqp->aio_req_buf.b_proc,
sigev);
}
if (lio_sigev) {
signalled++;
aio_sigev_send(reqp->aio_req_buf.b_proc,
lio_sigev);
}
}
} while ((reqp = next) != qhead);
return (signalled);
}
static void
aio_cleanup_pollq(aio_t *aiop, aio_req_t *qhead, int exitflg)
{
aio_req_t *reqp, *next;
if ((reqp = qhead) == NULL)
return;
do {
ASSERT(reqp->aio_req_flags & AIO_POLLQ);
next = reqp->aio_req_next;
aphysio_unlock(reqp);
if (exitflg) {
mutex_enter(&aiop->aio_mutex);
aio_req_free(aiop, reqp);
mutex_exit(&aiop->aio_mutex);
} else {
aio_copyout_result(reqp);
mutex_enter(&aiop->aio_mutex);
aio_enq(&aiop->aio_doneq, reqp, AIO_DONEQ);
mutex_exit(&aiop->aio_mutex);
}
} while ((reqp = next) != qhead);
}
void
aio_cleanup_exit(void)
{
proc_t *p = curproc;
aio_t *aiop = p->p_aio;
aio_req_t *reqp, *next, *head;
aio_lio_t *nxtlio, *liop;
mutex_enter(&aiop->aio_mutex);
aiop->aio_flags |= AIO_CLEANUP;
while (aiop->aio_pending != 0 || aiop->aio_done_cnt > 0)
cv_wait(&aiop->aio_cleanupcv, &aiop->aio_mutex);
mutex_exit(&aiop->aio_mutex);
aio_cleanup(AIO_CLEANUP_EXIT);
mutex_enter(&p->p_lock);
if ((head = aiop->aio_doneq) != NULL) {
aiop->aio_doneq = NULL;
reqp = head;
do {
next = reqp->aio_req_next;
aphysio_unlock(reqp);
kmem_free(reqp, sizeof (struct aio_req_t));
} while ((reqp = next) != head);
}
for (reqp = aiop->aio_free; reqp != NULL; reqp = next) {
next = reqp->aio_req_next;
kmem_free(reqp, sizeof (struct aio_req_t));
}
for (liop = aiop->aio_lio_free; liop != NULL; liop = nxtlio) {
nxtlio = liop->lio_next;
kmem_free(liop, sizeof (aio_lio_t));
}
if (aiop->aio_iocb)
kmem_free(aiop->aio_iocb, aiop->aio_iocbsz);
mutex_destroy(&aiop->aio_mutex);
mutex_destroy(&aiop->aio_portq_mutex);
mutex_destroy(&aiop->aio_cleanupq_mutex);
p->p_aio = NULL;
mutex_exit(&p->p_lock);
kmem_free(aiop, sizeof (struct aio));
}
void
aio_copyout_result(aio_req_t *reqp)
{
struct buf *bp;
struct iovec *iov;
void *resultp;
int error;
size_t retval;
if (reqp->aio_req_flags & AIO_COPYOUTDONE)
return;
reqp->aio_req_flags |= AIO_COPYOUTDONE;
iov = reqp->aio_req_uio.uio_iov;
bp = &reqp->aio_req_buf;
resultp = (void *)reqp->aio_req_resultp;
if (bp->b_flags & B_ERROR) {
if (bp->b_error)
error = bp->b_error;
else
error = EIO;
retval = (size_t)-1;
} else {
error = 0;
retval = iov->iov_len - bp->b_resid;
}
#ifdef _SYSCALL32_IMPL
if (get_udatamodel() == DATAMODEL_NATIVE) {
(void) sulword(&((aio_result_t *)resultp)->aio_return, retval);
(void) suword32(&((aio_result_t *)resultp)->aio_errno, error);
} else {
(void) suword32(&((aio_result32_t *)resultp)->aio_return,
(int)retval);
(void) suword32(&((aio_result32_t *)resultp)->aio_errno, error);
}
#else
(void) suword32(&((aio_result_t *)resultp)->aio_return, retval);
(void) suword32(&((aio_result_t *)resultp)->aio_errno, error);
#endif
}
void
aio_copyout_result_port(struct iovec *iov, struct buf *bp, void *resultp)
{
int errno;
size_t retval;
if (bp->b_flags & B_ERROR) {
if (bp->b_error)
errno = bp->b_error;
else
errno = EIO;
retval = (size_t)-1;
} else {
errno = 0;
retval = iov->iov_len - bp->b_resid;
}
#ifdef _SYSCALL32_IMPL
if (get_udatamodel() == DATAMODEL_NATIVE) {
(void) sulword(&((aio_result_t *)resultp)->aio_return, retval);
(void) suword32(&((aio_result_t *)resultp)->aio_errno, errno);
} else {
(void) suword32(&((aio_result32_t *)resultp)->aio_return,
(int)retval);
(void) suword32(&((aio_result32_t *)resultp)->aio_errno, errno);
}
#else
(void) suword32(&((aio_result_t *)resultp)->aio_return, retval);
(void) suword32(&((aio_result_t *)resultp)->aio_errno, errno);
#endif
}
void
aio_req_remove_portq(aio_t *aiop, aio_req_t *reqp)
{
ASSERT(MUTEX_HELD(&aiop->aio_portq_mutex));
while (aiop->aio_portq == NULL) {
cv_wait(&aiop->aio_portcv, &aiop->aio_portq_mutex);
}
aio_deq(&aiop->aio_portq, reqp);
}
void
aio_close_port(void *arg, int port, pid_t pid, int lastclose)
{
aio_t *aiop;
aio_req_t *reqp;
aio_req_t *next;
aio_req_t *headp;
int counter;
if (arg == NULL)
aiop = curproc->p_aio;
else
aiop = (aio_t *)arg;
if (aiop == NULL)
return;
mutex_enter(&aiop->aio_portq_mutex);
mutex_enter(&aiop->aio_mutex);
counter = 0;
if ((headp = aiop->aio_portpending) != NULL) {
reqp = headp;
do {
if (reqp->aio_req_portkev &&
reqp->aio_req_port == port) {
reqp->aio_req_flags |= AIO_CLOSE_PORT;
counter++;
}
} while ((reqp = reqp->aio_req_next) != headp);
}
if (counter == 0) {
mutex_exit(&aiop->aio_mutex);
mutex_exit(&aiop->aio_portq_mutex);
return;
}
aiop->aio_portpendcnt += counter;
mutex_exit(&aiop->aio_mutex);
while (aiop->aio_portpendcnt)
cv_wait(&aiop->aio_portcv, &aiop->aio_portq_mutex);
headp = NULL;
if ((reqp = aiop->aio_portq) != NULL) {
do {
next = reqp->aio_req_next;
if (reqp->aio_req_port == port) {
aio_req_remove_portq(aiop, reqp);
port_free_event(reqp->aio_req_portkev);
reqp->aio_req_next = headp;
headp = reqp;
}
} while ((reqp = next) != aiop->aio_portq);
}
mutex_exit(&aiop->aio_portq_mutex);
for (reqp = headp; reqp != NULL; reqp = next) {
next = reqp->aio_req_next;
aphysio_unlock(reqp);
mutex_enter(&aiop->aio_mutex);
aio_req_free_port(aiop, reqp);
mutex_exit(&aiop->aio_mutex);
}
if (aiop->aio_flags & AIO_CLEANUP)
cv_broadcast(&aiop->aio_waitcv);
}
int
aio_cleanup_dr_delete_memory(proc_t *procp)
{
struct aio *aiop = procp->p_aio;
struct as *as = procp->p_as;
int ret = 0;
ASSERT(MUTEX_HELD(&procp->p_lock));
mutex_enter(&as->a_contents);
if (aiop != NULL) {
aiop->aio_rqclnup = 1;
cv_broadcast(&as->a_cv);
ret = 1;
}
mutex_exit(&as->a_contents);
return (ret);
}