kern_physio.c revision 49771
1/* 2 * Copyright (c) 1994 John S. Dyson 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice immediately at the beginning of the file, without modification, 10 * this list of conditions, and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. Absolutely no warranty of function or purpose is made by the author 15 * John S. Dyson. 16 * 4. Modifications may be freely made to this file if the above conditions 17 * are met. 18 * 19 * $Id: kern_physio.c,v 1.35 1999/06/26 02:46:02 mckusick Exp $ 20 */ 21 22#include <sys/param.h> 23#include <sys/systm.h> 24#include <sys/buf.h> 25#include <sys/conf.h> 26#include <sys/proc.h> 27#include <sys/uio.h> 28 29#include <vm/vm.h> 30#include <vm/vm_extern.h> 31 32static void physwakeup __P((struct buf *bp)); 33static struct buf * phygetvpbuf(dev_t dev, int resid); 34 35int 36physread(dev_t dev, struct uio *uio, int ioflag) 37{ 38 return(physio(NULL, dev, 1, minphys, uio)); 39} 40 41int 42physwrite(dev_t dev, struct uio *uio, int ioflag) 43{ 44 return(physio(NULL, dev, 0, minphys, uio)); 45} 46 47int 48physio(bp, dev, rw, minp, uio) 49 struct buf *bp; 50 dev_t dev; 51 int rw; 52 u_int (*minp) __P((struct buf *bp)); 53 struct uio *uio; 54{ 55 int i; 56 int bufflags = rw?B_READ:0; 57 int error; 58 int spl; 59 caddr_t sa; 60 int bp_alloc = (bp == 0); 61 struct buf *bpa; 62 63 /* 64 * Keep the process UPAGES from being swapped. (XXX for performance?) 65 */ 66 PHOLD(curproc); 67 68 /* create and build a buffer header for a transfer */ 69 bpa = (struct buf *)phygetvpbuf(dev, uio->uio_resid); 70 if (!bp_alloc) 71 BUF_LOCK(bp, LK_EXCLUSIVE); 72 else 73 bp = bpa; 74 75 /* 76 * get a copy of the kva from the physical buffer 77 */ 78 sa = bpa->b_data; 79 error = bp->b_error = 0; 80 81 for (i = 0; i < uio->uio_iovcnt; i++) { 82 while (uio->uio_iov[i].iov_len) { 83 84 bp->b_dev = dev; 85 bp->b_bcount = uio->uio_iov[i].iov_len; 86 bp->b_flags = B_PHYS | B_CALL | bufflags; 87 bp->b_iodone = physwakeup; 88 bp->b_data = uio->uio_iov[i].iov_base; 89 bp->b_bcount = minp( bp); 90 if( minp != minphys) 91 bp->b_bcount = minphys( bp); 92 bp->b_bufsize = bp->b_bcount; 93 /* 94 * pass in the kva from the physical buffer 95 * for the temporary kernel mapping. 96 */ 97 bp->b_saveaddr = sa; 98 bp->b_blkno = btodb(uio->uio_offset); 99 bp->b_offset = uio->uio_offset; 100 101 if (uio->uio_segflg == UIO_USERSPACE) { 102 if (rw && !useracc(bp->b_data, bp->b_bufsize, B_WRITE)) { 103 error = EFAULT; 104 goto doerror; 105 } 106 if (!rw && !useracc(bp->b_data, bp->b_bufsize, B_READ)) { 107 error = EFAULT; 108 goto doerror; 109 } 110 111 /* bring buffer into kernel space */ 112 vmapbuf(bp); 113 } 114 115 /* perform transfer */ 116 BUF_STRATEGY(bp, 0); 117 118 spl = splbio(); 119 while ((bp->b_flags & B_DONE) == 0) 120 tsleep((caddr_t)bp, PRIBIO, "physstr", 0); 121 splx(spl); 122 123 /* release mapping into kernel space */ 124 if (uio->uio_segflg == UIO_USERSPACE) 125 vunmapbuf(bp); 126 127 /* 128 * update the uio data 129 */ 130 { 131 int iolen = bp->b_bcount - bp->b_resid; 132 133 if (iolen == 0 && !(bp->b_flags & B_ERROR)) 134 goto doerror; /* EOF */ 135 uio->uio_iov[i].iov_len -= iolen; 136 uio->uio_iov[i].iov_base += iolen; 137 uio->uio_resid -= iolen; 138 uio->uio_offset += iolen; 139 } 140 141 /* 142 * check for an error 143 */ 144 if( bp->b_flags & B_ERROR) { 145 error = bp->b_error; 146 goto doerror; 147 } 148 } 149 } 150 151 152doerror: 153 relpbuf(bpa, NULL); 154 if (!bp_alloc) { 155 bp->b_flags &= ~B_PHYS; 156 BUF_UNLOCK(bp); 157 } 158 /* 159 * Allow the process UPAGES to be swapped again. 160 */ 161 PRELE(curproc); 162 163 return (error); 164} 165 166u_int 167minphys(bp) 168 struct buf *bp; 169{ 170 u_int maxphys = DFLTPHYS; 171 struct cdevsw *bdsw; 172 173 bdsw = devsw(bp->b_dev); 174 175 if (bdsw && bdsw->d_maxio) { 176 maxphys = bdsw->d_maxio; 177 } 178 if (bp->b_kvasize && (bp->b_kvasize < maxphys)) 179 maxphys = bp->b_kvasize; 180 181 if(((vm_offset_t) bp->b_data) & PAGE_MASK) { 182 maxphys -= PAGE_SIZE; 183 } 184 185 if( bp->b_bcount > maxphys) { 186 bp->b_bcount = maxphys; 187 } 188 189 return bp->b_bcount; 190} 191 192struct buf * 193phygetvpbuf(dev_t dev, int resid) 194{ 195 struct cdevsw *bdsw; 196 int maxio; 197 198 bdsw = devsw(dev); 199 if ((bdsw == NULL) || (bdsw->d_bmaj == -1)) 200 return getpbuf(NULL); 201 202 maxio = bdsw->d_maxio; 203 if (resid > maxio) 204 resid = maxio; 205 206 return getpbuf(NULL); 207} 208 209static void 210physwakeup(bp) 211 struct buf *bp; 212{ 213 wakeup((caddr_t) bp); 214 bp->b_flags &= ~B_CALL; 215} 216