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.33 1999/05/07 07:03:39 phk 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 32 static void physwakeup __P((struct buf *bp)); 33 static struct buf * phygetvpbuf(dev_t dev, int resid); 34 35 int 36 physread(dev_t dev, struct uio *uio, int ioflag) 37 { 38 return(physio(devsw(dev)->d_strategy, NULL, dev, 1, minphys, uio)); 39 } 40 41 int 42 physwrite(dev_t dev, struct uio *uio, int ioflag) 43 { 44 return(physio(devsw(dev)->d_strategy, NULL, dev, 0, minphys, uio)); 45 } 46 47 int 48 physio(strategy, bp, dev, rw, minp, uio) 49 d_strategy_t *strategy; 50 struct buf *bp; 51 dev_t dev; 52 int rw; 53 u_int (*minp) __P((struct buf *bp)); 54 struct uio *uio; 55 { 56 int i; 57 int bufflags = rw?B_READ:0; 58 int error; 59 int spl; 60 caddr_t sa; 61 int bp_alloc = (bp == 0); 62 struct buf *bpa; 63 64 /* 65 * Keep the process UPAGES from being swapped. (XXX for performance?) 66 */ 67 PHOLD(curproc); 68 69 /* create and build a buffer header for a transfer */ 70 bpa = (struct buf *)phygetvpbuf(dev, uio->uio_resid); 71 if (!bp_alloc) { 72 spl = splbio(); 73 while (bp->b_flags & B_BUSY) { 74 bp->b_flags |= B_WANTED; 75 tsleep((caddr_t)bp, PRIBIO, "physbw", 0); 76 } 77 bp->b_flags |= B_BUSY; 78 splx(spl); 79 } else { 80 bp = bpa; 81 } 82 83 /* 84 * get a copy of the kva from the physical buffer 85 */ 86 sa = bpa->b_data; 87 error = bp->b_error = 0; 88 89 for(i=0;i<uio->uio_iovcnt;i++) { 90 while( uio->uio_iov[i].iov_len) { 91 92 bp->b_dev = dev; 93 bp->b_bcount = uio->uio_iov[i].iov_len; 94 bp->b_flags = B_BUSY | B_PHYS | B_CALL | bufflags; 95 bp->b_iodone = physwakeup; 96 bp->b_data = uio->uio_iov[i].iov_base; 97 bp->b_bcount = minp( bp); 98 if( minp != minphys) 99 bp->b_bcount = minphys( bp); 100 bp->b_bufsize = bp->b_bcount; 101 /* 102 * pass in the kva from the physical buffer 103 * for the temporary kernel mapping. 104 */ 105 bp->b_saveaddr = sa; 106 bp->b_blkno = btodb(uio->uio_offset); 107 bp->b_offset = uio->uio_offset; 108 109 if (uio->uio_segflg == UIO_USERSPACE) { 110 if (rw && !useracc(bp->b_data, bp->b_bufsize, B_WRITE)) { 111 error = EFAULT; 112 goto doerror; 113 } 114 if (!rw && !useracc(bp->b_data, bp->b_bufsize, B_READ)) { 115 error = EFAULT; 116 goto doerror; 117 } 118 119 /* bring buffer into kernel space */ 120 vmapbuf(bp); 121 } 122 123 /* perform transfer */ 124 (*strategy)(bp); 125 126 spl = splbio(); 127 while ((bp->b_flags & B_DONE) == 0) 128 tsleep((caddr_t)bp, PRIBIO, "physstr", 0); 129 splx(spl); 130 131 /* release mapping into kernel space */ 132 if (uio->uio_segflg == UIO_USERSPACE) 133 vunmapbuf(bp); 134 135 /* 136 * update the uio data 137 */ 138 { 139 int iolen = bp->b_bcount - bp->b_resid; 140 141 if (iolen == 0 && !(bp->b_flags & B_ERROR)) 142 goto doerror; /* EOF */ 143 uio->uio_iov[i].iov_len -= iolen; 144 uio->uio_iov[i].iov_base += iolen; 145 uio->uio_resid -= iolen; 146 uio->uio_offset += iolen; 147 } 148 149 /* 150 * check for an error 151 */ 152 if( bp->b_flags & B_ERROR) { 153 error = bp->b_error; 154 goto doerror; 155 } 156 } 157 } 158 159 160 doerror: 161 relpbuf(bpa, NULL); 162 if (!bp_alloc) { 163 bp->b_flags &= ~(B_BUSY|B_PHYS); 164 if( bp->b_flags & B_WANTED) { 165 bp->b_flags &= ~B_WANTED; 166 wakeup((caddr_t)bp); 167 } 168 } 169 /* 170 * Allow the process UPAGES to be swapped again. 171 */ 172 PRELE(curproc); 173 174 return (error); 175 } 176 177 u_int 178 minphys(bp) 179 struct buf *bp; 180 { 181 u_int maxphys = DFLTPHYS; 182 struct cdevsw *bdsw; 183 184 bdsw = devsw(bp->b_dev); 185 186 if (bdsw && bdsw->d_maxio) { 187 maxphys = bdsw->d_maxio; 188 } 189 if (bp->b_kvasize && (bp->b_kvasize < maxphys)) 190 maxphys = bp->b_kvasize; 191 192 if(((vm_offset_t) bp->b_data) & PAGE_MASK) { 193 maxphys -= PAGE_SIZE; 194 } 195 196 if( bp->b_bcount > maxphys) { 197 bp->b_bcount = maxphys; 198 } 199 200 return bp->b_bcount; 201 } 202 203 struct buf * 204 phygetvpbuf(dev_t dev, int resid) 205 { 206 struct cdevsw *bdsw; 207 int maxio; 208 209 bdsw = devsw(dev); 210 if ((bdsw == NULL) || (bdsw->d_bmaj == -1)) 211 return getpbuf(NULL); 212 213 maxio = bdsw->d_maxio; 214 if (resid > maxio) 215 resid = maxio; 216 217 return getpbuf(NULL); 218 } 219 220 static void 221 physwakeup(bp) 222 struct buf *bp; 223 { 224 wakeup((caddr_t) bp); 225 bp->b_flags &= ~B_CALL; 226 } 227