kern_physio.c revision 50108
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.36 1999/08/14 11:40:42 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 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 off_t blockno; 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 BUF_LOCK(bp, LK_EXCLUSIVE); 73 else 74 bp = bpa; 75 76 /* 77 * get a copy of the kva from the physical buffer 78 */ 79 sa = bpa->b_data; 80 error = bp->b_error = 0; 81 82 for (i = 0; i < uio->uio_iovcnt; i++) { 83 while (uio->uio_iov[i].iov_len) { 84 85 bp->b_dev = dev; 86 bp->b_bcount = uio->uio_iov[i].iov_len; 87 bp->b_flags = B_PHYS | B_CALL | bufflags; 88 bp->b_iodone = physwakeup; 89 bp->b_data = uio->uio_iov[i].iov_base; 90 bp->b_bcount = minp( bp); 91 if( minp != minphys) 92 bp->b_bcount = minphys( bp); 93 bp->b_bufsize = bp->b_bcount; 94 /* 95 * pass in the kva from the physical buffer 96 * for the temporary kernel mapping. 97 */ 98 bp->b_saveaddr = sa; 99 blockno = uio->uio_offset >> DEV_BSHIFT; 100 bp->b_blkno = blockno; 101 if (bp->b_blkno != blockno) { 102 error = EINVAL; 103 goto doerror; 104 } 105 bp->b_offset = uio->uio_offset; 106 107 if (uio->uio_segflg == UIO_USERSPACE) { 108 if (rw && !useracc(bp->b_data, bp->b_bufsize, B_WRITE)) { 109 error = EFAULT; 110 goto doerror; 111 } 112 if (!rw && !useracc(bp->b_data, bp->b_bufsize, B_READ)) { 113 error = EFAULT; 114 goto doerror; 115 } 116 117 /* bring buffer into kernel space */ 118 vmapbuf(bp); 119 } 120 121 /* perform transfer */ 122 BUF_STRATEGY(bp, 0); 123 124 spl = splbio(); 125 while ((bp->b_flags & B_DONE) == 0) 126 tsleep((caddr_t)bp, PRIBIO, "physstr", 0); 127 splx(spl); 128 129 /* release mapping into kernel space */ 130 if (uio->uio_segflg == UIO_USERSPACE) 131 vunmapbuf(bp); 132 133 /* 134 * update the uio data 135 */ 136 { 137 int iolen = bp->b_bcount - bp->b_resid; 138 139 if (iolen == 0 && !(bp->b_flags & B_ERROR)) 140 goto doerror; /* EOF */ 141 uio->uio_iov[i].iov_len -= iolen; 142 uio->uio_iov[i].iov_base += iolen; 143 uio->uio_resid -= iolen; 144 uio->uio_offset += iolen; 145 } 146 147 /* 148 * check for an error 149 */ 150 if( bp->b_flags & B_ERROR) { 151 error = bp->b_error; 152 goto doerror; 153 } 154 } 155 } 156 157 158doerror: 159 relpbuf(bpa, NULL); 160 if (!bp_alloc) { 161 bp->b_flags &= ~B_PHYS; 162 BUF_UNLOCK(bp); 163 } 164 /* 165 * Allow the process UPAGES to be swapped again. 166 */ 167 PRELE(curproc); 168 169 return (error); 170} 171 172u_int 173minphys(bp) 174 struct buf *bp; 175{ 176 u_int maxphys = DFLTPHYS; 177 struct cdevsw *bdsw; 178 179 bdsw = devsw(bp->b_dev); 180 181 if (bdsw && bdsw->d_maxio) { 182 maxphys = bdsw->d_maxio; 183 } 184 if (bp->b_kvasize && (bp->b_kvasize < maxphys)) 185 maxphys = bp->b_kvasize; 186 187 if(((vm_offset_t) bp->b_data) & PAGE_MASK) { 188 maxphys -= PAGE_SIZE; 189 } 190 191 if( bp->b_bcount > maxphys) { 192 bp->b_bcount = maxphys; 193 } 194 195 return bp->b_bcount; 196} 197 198struct buf * 199phygetvpbuf(dev_t dev, int resid) 200{ 201 struct cdevsw *bdsw; 202 int maxio; 203 204 bdsw = devsw(dev); 205 if ((bdsw == NULL) || (bdsw->d_bmaj == -1)) 206 return getpbuf(NULL); 207 208 maxio = bdsw->d_maxio; 209 if (resid > maxio) 210 resid = maxio; 211 212 return getpbuf(NULL); 213} 214 215static void 216physwakeup(bp) 217 struct buf *bp; 218{ 219 wakeup((caddr_t) bp); 220 bp->b_flags &= ~B_CALL; 221} 222