/* * Copyright (c) 1994 John S. Dyson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice immediately at the beginning of the file, without modification, * this list of conditions, and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Absolutely no warranty of function or purpose is made by the author * John S. Dyson. * 4. Modifications may be freely made to this file if the above conditions * are met. */ #include #include #include #include #include #include static void physwakeup(); int physio(strategy, bp, dev, rw, minp, uio) int (*strategy)(); struct buf *bp; dev_t dev; int rw; u_int (*minp)(); struct uio *uio; { int i; int bp_alloc = (bp == 0); int bufflags = rw?B_READ:0; int error; int spl; /* * keep the process from being swapped */ curproc->p_flag |= P_PHYSIO; /* create and build a buffer header for a transfer */ if (bp_alloc) { bp = (struct buf *)getpbuf(); } else { spl = splbio(); while (bp->b_flags & B_BUSY) { bp->b_flags |= B_WANTED; tsleep((caddr_t)bp, PRIBIO, "physbw", 0); } bp->b_flags |= B_BUSY; splx(spl); } bp->b_proc = curproc; bp->b_dev = dev; error = bp->b_error = 0; for(i=0;iuio_iovcnt;i++) { while( uio->uio_iov[i].iov_len) { vm_offset_t v, lastv, pa; caddr_t adr; bp->b_bcount = uio->uio_iov[i].iov_len; bp->b_bufsize = bp->b_bcount; bp->b_flags = B_BUSY | B_PHYS | B_CALL | bufflags; bp->b_iodone = physwakeup; bp->b_data = uio->uio_iov[i].iov_base; bp->b_blkno = btodb(uio->uio_offset); if (rw && !useracc(bp->b_data, bp->b_bufsize, B_WRITE)) { error = EFAULT; goto doerror; } if (!rw && !useracc(bp->b_data, bp->b_bufsize, B_READ)) { error = EFAULT; goto doerror; } vmapbuf(bp); /* perform transfer */ (*strategy)(bp); spl = splbio(); while ((bp->b_flags & B_DONE) == 0) tsleep((caddr_t)bp, PRIBIO, "physstr", 0); splx(spl); vunmapbuf(bp); /* * update the uio data */ { int iolen = bp->b_bcount - bp->b_resid; uio->uio_iov[i].iov_len -= iolen; uio->uio_iov[i].iov_base += iolen; uio->uio_resid -= iolen; uio->uio_offset += iolen; } /* * check for an error */ if( bp->b_flags & B_ERROR) { error = bp->b_error; goto doerror; } } } doerror: if (bp_alloc) { relpbuf(bp); } else { bp->b_flags &= ~(B_BUSY|B_PHYS); if( bp->b_flags & B_WANTED) { bp->b_flags &= ~B_WANTED; wakeup((caddr_t)bp); } } /* * allow the process to be swapped */ curproc->p_flag &= ~P_PHYSIO; return (error); } u_int minphys(struct buf *bp) { if( bp->b_bcount > MAXBSIZE) { bp->b_bcount = MAXBSIZE; } return bp->b_bcount; } int rawread(dev_t dev, struct uio *uio) { return (physio(cdevsw[major(dev)].d_strategy, (struct buf *)NULL, dev, 1, minphys, uio)); } int rawwrite(dev_t dev, struct uio *uio) { return (physio(cdevsw[major(dev)].d_strategy, (struct buf *)NULL, dev, 0, minphys, uio)); } static void physwakeup(bp) struct buf *bp; { wakeup((caddr_t) bp); bp->b_flags &= ~B_CALL; }