kern_physio.c revision 51878
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 * $FreeBSD: head/sys/kern/kern_physio.c 51878 1999-10-02 20:21:49Z sos $ 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)); 33 34int 35physread(dev_t dev, struct uio *uio, int ioflag) 36{ 37 return(physio(NULL, dev, 1, minphys, uio)); 38} 39 40int 41physwrite(dev_t dev, struct uio *uio, int ioflag) 42{ 43 return(physio(NULL, dev, 0, minphys, uio)); 44} 45 46int 47physio(bp, dev, rw, minp, uio) 48 struct buf *bp; 49 dev_t dev; 50 int rw; 51 u_int (*minp) __P((struct buf *bp)); 52 struct uio *uio; 53{ 54 int i; 55 int bufflags = rw?B_READ:0; 56 int error; 57 int spl; 58 caddr_t sa; 59 off_t blockno; 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 = getpbuf(NULL); 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 blockno = uio->uio_offset >> DEV_BSHIFT; 99 if ((daddr_t)blockno != blockno) { 100 error = EINVAL; 101 goto doerror; 102 } 103 bp->b_blkno = blockno; 104 bp->b_offset = uio->uio_offset; 105 106 if (uio->uio_segflg == UIO_USERSPACE) { 107 if (rw && !useracc(bp->b_data, bp->b_bufsize, B_WRITE)) { 108 error = EFAULT; 109 goto doerror; 110 } 111 if (!rw && !useracc(bp->b_data, bp->b_bufsize, B_READ)) { 112 error = EFAULT; 113 goto doerror; 114 } 115 116 /* bring buffer into kernel space */ 117 vmapbuf(bp); 118 } 119 120 /* perform transfer */ 121 BUF_STRATEGY(bp, 0); 122 123 spl = splbio(); 124 while ((bp->b_flags & B_DONE) == 0) 125 tsleep((caddr_t)bp, PRIBIO, "physstr", 0); 126 splx(spl); 127 128 /* release mapping into kernel space */ 129 if (uio->uio_segflg == UIO_USERSPACE) 130 vunmapbuf(bp); 131 132 /* 133 * update the uio data 134 */ 135 { 136 int iolen = bp->b_bcount - bp->b_resid; 137 138 if (iolen == 0 && !(bp->b_flags & B_ERROR)) 139 goto doerror; /* EOF */ 140 uio->uio_iov[i].iov_len -= iolen; 141 uio->uio_iov[i].iov_base += iolen; 142 uio->uio_resid -= iolen; 143 uio->uio_offset += iolen; 144 } 145 146 /* 147 * check for an error 148 */ 149 if( bp->b_flags & B_ERROR) { 150 error = bp->b_error; 151 goto doerror; 152 } 153 } 154 } 155 156 157doerror: 158 relpbuf(bpa, NULL); 159 if (!bp_alloc) { 160 bp->b_flags &= ~B_PHYS; 161 BUF_UNLOCK(bp); 162 } 163 /* 164 * Allow the process UPAGES to be swapped again. 165 */ 166 PRELE(curproc); 167 168 return (error); 169} 170 171u_int 172minphys(bp) 173 struct buf *bp; 174{ 175 u_int maxphys; 176 177 maxphys = bp->b_dev->si_iosize_max; 178 if(!maxphys) { 179 printf("WARNING: %s maxphys = 0 ??", devtoname(bp->b_dev)); 180 maxphys = DFLTPHYS; 181 bp->b_dev->si_iosize_max = DFLTPHYS; 182 } 183 if (bp->b_kvasize && (bp->b_kvasize < maxphys)) 184 maxphys = bp->b_kvasize; 185 186 if(((vm_offset_t) bp->b_data) & PAGE_MASK) { 187 maxphys -= PAGE_SIZE; 188 } 189 190 if( bp->b_bcount > maxphys) { 191 bp->b_bcount = maxphys; 192 } 193 194 return bp->b_bcount; 195} 196 197static void 198physwakeup(bp) 199 struct buf *bp; 200{ 201 wakeup((caddr_t) bp); 202 bp->b_flags &= ~B_CALL; 203} 204