kern_physio.c revision 254802
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 20#include <sys/cdefs.h> 21__FBSDID("$FreeBSD: head/sys/kern/kern_physio.c 254802 2013-08-24 19:02:36Z ken $"); 22 23#include <sys/param.h> 24#include <sys/systm.h> 25#include <sys/bio.h> 26#include <sys/buf.h> 27#include <sys/conf.h> 28#include <sys/proc.h> 29#include <sys/uio.h> 30 31#include <vm/vm.h> 32#include <vm/vm_extern.h> 33 34int 35physio(struct cdev *dev, struct uio *uio, int ioflag) 36{ 37 struct buf *bp; 38 struct cdevsw *csw; 39 caddr_t sa; 40 u_int iolen; 41 int error, i, mapped; 42 43 /* Keep the process UPAGES from being swapped. XXX: why ? */ 44 PHOLD(curproc); 45 46 bp = getpbuf(NULL); 47 sa = bp->b_data; 48 error = 0; 49 50 /* XXX: sanity check */ 51 if(dev->si_iosize_max < PAGE_SIZE) { 52 printf("WARNING: %s si_iosize_max=%d, using DFLTPHYS.\n", 53 devtoname(dev), dev->si_iosize_max); 54 dev->si_iosize_max = DFLTPHYS; 55 } 56 57 /* 58 * If the driver does not want I/O to be split, that means that we 59 * need to reject any requests that will not fit into one buffer. 60 */ 61 if ((dev->si_flags & SI_NOSPLIT) && 62 ((uio->uio_resid > dev->si_iosize_max) || 63 (uio->uio_resid > MAXPHYS) || 64 (uio->uio_iovcnt > 1))) { 65 /* 66 * Tell the user why his I/O was rejected. 67 */ 68 if (uio->uio_resid > dev->si_iosize_max) 69 printf("%s: request size %zd > si_iosize_max=%d, " 70 "cannot split request\n", devtoname(dev), 71 uio->uio_resid, dev->si_iosize_max); 72 73 if (uio->uio_resid > MAXPHYS) 74 printf("%s: request size %zd > MAXPHYS=%d, " 75 "cannot split request\n", devtoname(dev), 76 uio->uio_resid, MAXPHYS); 77 78 if (uio->uio_iovcnt > 1) 79 printf("%s: request vectors=%d > 1, " 80 "cannot split request\n", devtoname(dev), 81 uio->uio_iovcnt); 82 83 error = EFBIG; 84 goto doerror; 85 } 86 87 for (i = 0; i < uio->uio_iovcnt; i++) { 88 while (uio->uio_iov[i].iov_len) { 89 bp->b_flags = 0; 90 if (uio->uio_rw == UIO_READ) { 91 bp->b_iocmd = BIO_READ; 92 curthread->td_ru.ru_inblock++; 93 } else { 94 bp->b_iocmd = BIO_WRITE; 95 curthread->td_ru.ru_oublock++; 96 } 97 bp->b_iodone = bdone; 98 bp->b_data = uio->uio_iov[i].iov_base; 99 bp->b_bcount = uio->uio_iov[i].iov_len; 100 bp->b_offset = uio->uio_offset; 101 bp->b_iooffset = uio->uio_offset; 102 bp->b_saveaddr = sa; 103 104 /* Don't exceed drivers iosize limit */ 105 if (bp->b_bcount > dev->si_iosize_max) 106 bp->b_bcount = dev->si_iosize_max; 107 108 /* 109 * Make sure the pbuf can map the request 110 * XXX: The pbuf has kvasize = MAXPHYS so a request 111 * XXX: larger than MAXPHYS - PAGE_SIZE must be 112 * XXX: page aligned or it will be fragmented. 113 */ 114 iolen = ((vm_offset_t) bp->b_data) & PAGE_MASK; 115 if ((bp->b_bcount + iolen) > bp->b_kvasize) { 116 /* 117 * This device does not want I/O to be split. 118 */ 119 if (dev->si_flags & SI_NOSPLIT) { 120 printf("%s: request ptr %p is not " 121 "on a page boundary, cannot split " 122 "request\n", devtoname(dev), 123 bp->b_data); 124 error = EFBIG; 125 goto doerror; 126 } 127 bp->b_bcount = bp->b_kvasize; 128 if (iolen != 0) 129 bp->b_bcount -= PAGE_SIZE; 130 } 131 bp->b_bufsize = bp->b_bcount; 132 133 bp->b_blkno = btodb(bp->b_offset); 134 135 csw = dev->si_devsw; 136 if (uio->uio_segflg == UIO_USERSPACE) { 137 if (dev->si_flags & SI_UNMAPPED) 138 mapped = 0; 139 else 140 mapped = 1; 141 if (vmapbuf(bp, mapped) < 0) { 142 error = EFAULT; 143 goto doerror; 144 } 145 } 146 147 dev_strategy_csw(dev, csw, bp); 148 if (uio->uio_rw == UIO_READ) 149 bwait(bp, PRIBIO, "physrd"); 150 else 151 bwait(bp, PRIBIO, "physwr"); 152 153 if (uio->uio_segflg == UIO_USERSPACE) 154 vunmapbuf(bp); 155 iolen = bp->b_bcount - bp->b_resid; 156 if (iolen == 0 && !(bp->b_ioflags & BIO_ERROR)) 157 goto doerror; /* EOF */ 158 uio->uio_iov[i].iov_len -= iolen; 159 uio->uio_iov[i].iov_base = 160 (char *)uio->uio_iov[i].iov_base + iolen; 161 uio->uio_resid -= iolen; 162 uio->uio_offset += iolen; 163 if( bp->b_ioflags & BIO_ERROR) { 164 error = bp->b_error; 165 goto doerror; 166 } 167 } 168 } 169doerror: 170 relpbuf(bp, NULL); 171 PRELE(curproc); 172 return (error); 173} 174