FreeBSD/sys/fs/nfsclient/nfs_clbio.c
/*
 * Initiate asynchronous I/O. Return an error if no nfsiods are available.
 * This is mainly to avoid queueing async I/O requests when the nfsiods
 * are all hung on a dead server.
 *
 * Note: ncl_asyncio() does not clear (BIO_ERROR|B_INVAL) but when the bp
 * is eventually dequeued by the async daemon, ncl_doio() *will*.
 */
Initiate asynchronous I/O. Return an error if no nfsiods are available.
This is mainly to avoid queueing async I/O requests when the nfsiods
are all hung on a dead server.

ncl_asyncio() does not clear (BIO_ERROR|B_INVAL) but when the bp
is eventually dequeued by the async daemon, ncl_doio() *will*.
Initiate asynchronous I / O . Return an error if no nfsiods are available . This is mainly to avoid queueing async I / O requests when the nfsiods are all hung on a dead server . ncl_asyncio () does not clear ( BIO_ERROR|B_INVAL ) but when the bp is eventually dequeued by the async daemon ncl_doio () * will * .
int
ncl_asyncio(struct nfsmount *nmp, struct buf *bp, struct ucred *cred, struct thread *td)
{
	int iod;
	int gotiod;
	int slpflag = 0;
	int slptimeo = 0;
	int error, error2;
	mtx_lock(&ncl_iod_mutex);
	if ((bp->b_iocmd == BIO_WRITE && (bp->b_flags & B_NEEDCOMMIT) &&
	     (nmp->nm_bufqiods > ncl_numasync / 2)) ||
	    (bp->b_vp->v_type == VDIR && (nmp->nm_flag & NFSMNT_RDIRPLUS))) {
		mtx_unlock(&ncl_iod_mutex);
		return(EIO);
	}
again:
	if (nmp->nm_flag & NFSMNT_INT)
		slpflag = PCATCH;
	gotiod = FALSE;
	for (iod = 0; iod < ncl_numasync; iod++)
		if (ncl_iodwant[iod] == NFSIOD_AVAILABLE) {
			gotiod = TRUE;
			break;
		}
	if (!gotiod)
		ncl_nfsiodnew();
	else {
		NFS_DPF(ASYNCIO, ("ncl_asyncio: waking iod %d for mount %p\n",
		    iod, nmp));
		ncl_iodwant[iod] = NFSIOD_NOT_AVAILABLE;
		ncl_iodmount[iod] = nmp;
		nmp->nm_bufqiods++;
		wakeup(&ncl_iodwant[iod]);
	}
	if (!gotiod) {
		if (nmp->nm_bufqiods > 0) {
			NFS_DPF(ASYNCIO,
				("ncl_asyncio: %d iods are already processing mount %p\n",
				 nmp->nm_bufqiods, nmp));
			gotiod = TRUE;
		}
	}
	if (gotiod) {
		while (nmp->nm_bufqlen >= 2*ncl_numasync) {
			NFS_DPF(ASYNCIO,
				("ncl_asyncio: waiting for mount %p queue to drain\n", nmp));
			nmp->nm_bufqwant = TRUE;
			error = newnfs_msleep(td, &nmp->nm_bufq,
			    &ncl_iod_mutex, slpflag | PRIBIO, "nfsaio",
			   slptimeo);
			if (error) {
				error2 = newnfs_sigintr(nmp, td);
				if (error2) {
					mtx_unlock(&ncl_iod_mutex);
					return (error2);
				}
				if (slpflag == PCATCH) {
					slpflag = 0;
					slptimeo = 2 * hz;
				}
			}
			goto again;
		}
		if (nmp->nm_bufqiods == 0) {
			NFS_DPF(ASYNCIO,
				("ncl_asyncio: no iods after mount %p queue was drained, looping\n", nmp));
			goto again;
		}
		if (bp->b_iocmd == BIO_READ) {
			if (bp->b_rcred == NOCRED && cred != NOCRED)
				bp->b_rcred = crhold(cred);
		} else {
			if (bp->b_wcred == NOCRED && cred != NOCRED)
				bp->b_wcred = crhold(cred);
		}
		if (bp->b_flags & B_REMFREE)
			bremfreef(bp);
		BUF_KERNPROC(bp);
		TAILQ_INSERT_TAIL(&nmp->nm_bufq, bp, b_freelist);
		nmp->nm_bufqlen++;
		if ((bp->b_flags & B_DIRECT) && bp->b_iocmd == BIO_WRITE) {
			mtx_lock(&(VTONFS(bp->b_vp))->n_mtx);
			VTONFS(bp->b_vp)->n_flag |= NMODIFIED;
			VTONFS(bp->b_vp)->n_directio_asyncwr++;
			mtx_unlock(&(VTONFS(bp->b_vp))->n_mtx);
		}
		mtx_unlock(&ncl_iod_mutex);
		return (0);
	}
	mtx_unlock(&ncl_iod_mutex);
	NFS_DPF(ASYNCIO, ("ncl_asyncio: no iods available, i/o is synchronous\n"));
	return (EIO);
}
int ncl_asyncio ( struct nfsmount * nmp , struct buf * bp , struct ucred * cred , struct thread * td ) { int iod ; int gotiod ; int slpflag = 0 ; int slptimeo = 0 ; int error , error2 ; mtx_lock ( & ncl_iod_mutex ) ; if ( ( bp -> b_iocmd == BIO_WRITE && ( bp -> b_flags & B_NEEDCOMMIT ) && ( nmp -> nm_bufqiods > ncl_numasync / 2 ) ) || ( bp -> b_vp -> v_type == VDIR && ( nmp -> nm_flag & NFSMNT_RDIRPLUS ) ) ) { mtx_unlock ( & ncl_iod_mutex ) ; return ( EIO ) ; } again : if ( nmp -> nm_flag & NFSMNT_INT ) slpflag = PCATCH ; gotiod = FALSE ; for ( iod = 0 ; iod < ncl_numasync ; iod ++ ) if ( ncl_iodwant [ iod ] == NFSIOD_AVAILABLE ) { gotiod = TRUE ; break ; } if ( ! gotiod ) ncl_nfsiodnew ( ) ; else { NFS_DPF ( ASYNCIO , ( " \n " , iod , nmp ) ) ; ncl_iodwant [ iod ] = NFSIOD_NOT_AVAILABLE ; ncl_iodmount [ iod ] = nmp ; nmp -> nm_bufqiods ++ ; wakeup ( & ncl_iodwant [ iod ] ) ; } if ( ! gotiod ) { if ( nmp -> nm_bufqiods > 0 ) { NFS_DPF ( ASYNCIO , ( " \n " , nmp -> nm_bufqiods , nmp ) ) ; gotiod = TRUE ; } } if ( gotiod ) { while ( nmp -> nm_bufqlen >= 2 * ncl_numasync ) { NFS_DPF ( ASYNCIO , ( " \n " , nmp ) ) ; nmp -> nm_bufqwant = TRUE ; error = newnfs_msleep ( td , & nmp -> nm_bufq , & ncl_iod_mutex , slpflag | PRIBIO , " " , slptimeo ) ; if ( error ) { error2 = newnfs_sigintr ( nmp , td ) ; if ( error2 ) { mtx_unlock ( & ncl_iod_mutex ) ; return ( error2 ) ; } if ( slpflag == PCATCH ) { slpflag = 0 ; slptimeo = 2 * hz ; } } goto again ; } if ( nmp -> nm_bufqiods == 0 ) { NFS_DPF ( ASYNCIO , ( " \n " , nmp ) ) ; goto again ; } if ( bp -> b_iocmd == BIO_READ ) { if ( bp -> b_rcred == NOCRED && cred != NOCRED ) bp -> b_rcred = crhold ( cred ) ; } else { if ( bp -> b_wcred == NOCRED && cred != NOCRED ) bp -> b_wcred = crhold ( cred ) ; } if ( bp -> b_flags & B_REMFREE ) bremfreef ( bp ) ; BUF_KERNPROC ( bp ) ; TAILQ_INSERT_TAIL ( & nmp -> nm_bufq , bp , b_freelist ) ; nmp -> nm_bufqlen ++ ; if ( ( bp -> b_flags & B_DIRECT ) && bp -> b_iocmd == BIO_WRITE ) { mtx_lock ( & ( VTONFS ( bp -> b_vp ) ) -> n_mtx ) ; VTONFS ( bp -> b_vp ) -> n_flag |= NMODIFIED ; VTONFS ( bp -> b_vp ) -> n_directio_asyncwr ++ ; mtx_unlock ( & ( VTONFS ( bp -> b_vp ) ) -> n_mtx ) ; } mtx_unlock ( & ncl_iod_mutex ) ; return ( 0 ) ; } mtx_unlock ( & ncl_iod_mutex ) ; NFS_DPF ( ASYNCIO , ( " \n " ) ) ; return ( EIO ) ; }
Initiate asynchronous I/O.
Initiate asynchronous I / O .
/*
	 * Commits are usually short and sweet so lets save some cpu and
	 * leave the async daemons for more important rpc's (such as reads
	 * and writes).
	 *
	 * Readdirplus RPCs do vget()s to acquire the vnodes for entries
	 * in the directory in order to update attributes. This can deadlock
	 * with another thread that is waiting for async I/O to be done by
	 * an nfsiod thread while holding a lock on one of these vnodes.
	 * To avoid this deadlock, don't allow the async nfsiod threads to
	 * perform Readdirplus RPCs.
	 */ /*
	 * Find a free iod to process this request.
	 */ /*
	 * Try to create one if none are free.
	 */ /*
		 * Found one, so wake it up and tell it which
		 * mount to process.
		 */ /*
	 * If none are free, we may already have an iod working on this mount
	 * point.  If so, it will process our request.
	 */ /*
	 * If we have an iod which can process the request, then queue
	 * the buffer.
	 */ /*
		 * Ensure that the queue never grows too large.  We still want
		 * to asynchronize so we block rather then return EIO.
		 */ /*
			 * We might have lost our iod while sleeping,
			 * so check and loop if necessary.
			 */ /* We might have lost our nfsiod */ /*
	 * All the iods are busy on other mounts, so return EIO to
	 * force the caller to process the i/o synchronously.
	 */