kern_physio.c revision 254760
1254721Semaste/*-
2254721Semaste * Copyright (c) 1994 John S. Dyson
3254721Semaste * All rights reserved.
4254721Semaste *
5254721Semaste * Redistribution and use in source and binary forms, with or without
6254721Semaste * modification, are permitted provided that the following conditions
7254721Semaste * are met:
8254721Semaste * 1. Redistributions of source code must retain the above copyright
9254721Semaste *    notice immediately at the beginning of the file, without modification,
10254721Semaste *    this list of conditions, and the following disclaimer.
11254721Semaste * 2. Redistributions in binary form must reproduce the above copyright
12254721Semaste *    notice, this list of conditions and the following disclaimer in the
13254721Semaste *    documentation and/or other materials provided with the distribution.
14254721Semaste * 3. Absolutely no warranty of function or purpose is made by the author
15254721Semaste *    John S. Dyson.
16254721Semaste * 4. Modifications may be freely made to this file if the above conditions
17254721Semaste *    are met.
18254721Semaste */
19254721Semaste
20254721Semaste#include <sys/cdefs.h>
21254721Semaste__FBSDID("$FreeBSD: head/sys/kern/kern_physio.c 254760 2013-08-24 04:52:22Z ken $");
22254721Semaste
23254721Semaste#include <sys/param.h>
24254721Semaste#include <sys/systm.h>
25254721Semaste#include <sys/bio.h>
26254721Semaste#include <sys/buf.h>
27254721Semaste#include <sys/conf.h>
28254721Semaste#include <sys/proc.h>
29254721Semaste#include <sys/uio.h>
30254721Semaste
31254721Semaste#include <vm/vm.h>
32254721Semaste#include <vm/vm_extern.h>
33254721Semaste
34254721Semasteint
35254721Semastephysio(struct cdev *dev, struct uio *uio, int ioflag)
36254721Semaste{
37254721Semaste	struct buf *bp;
38254721Semaste	struct cdevsw *csw;
39254721Semaste	caddr_t sa;
40254721Semaste	u_int iolen;
41254721Semaste	int error, i, mapped;
42254721Semaste
43254721Semaste	/* Keep the process UPAGES from being swapped. XXX: why ? */
44254721Semaste	PHOLD(curproc);
45254721Semaste
46254721Semaste	bp = getpbuf(NULL);
47254721Semaste	sa = bp->b_data;
48254721Semaste	error = 0;
49254721Semaste
50254721Semaste	/* XXX: sanity check */
51254721Semaste	if(dev->si_iosize_max < PAGE_SIZE) {
52254721Semaste		printf("WARNING: %s si_iosize_max=%d, using DFLTPHYS.\n",
53254721Semaste		    devtoname(dev), dev->si_iosize_max);
54254721Semaste		dev->si_iosize_max = DFLTPHYS;
55254721Semaste	}
56254721Semaste
57254721Semaste	/*
58254721Semaste	 * If the driver does not want I/O to be split, that means that we
59254721Semaste	 * need to reject any requests that will not fit into one buffer.
60254721Semaste	 */
61254721Semaste	if ((dev->si_flags & SI_NOSPLIT) &&
62254721Semaste	    ((uio->uio_resid > dev->si_iosize_max) ||
63254721Semaste	     (uio->uio_resid > MAXPHYS) ||
64254721Semaste	     (uio->uio_iovcnt > 1))) {
65254721Semaste		/*
66254721Semaste		 * Tell the user why his I/O was rejected.
67254721Semaste		 */
68254721Semaste		if (uio->uio_resid > dev->si_iosize_max)
69254721Semaste			printf("%s: request size %zd > si_iosize_max=%d, "
70254721Semaste			    "cannot split request\n", devtoname(dev),
71254721Semaste			    uio->uio_resid, dev->si_iosize_max);
72254721Semaste
73254721Semaste		if (uio->uio_resid > MAXPHYS)
74254721Semaste			printf("%s: request size %zd > MAXPHYS=%d, "
75254721Semaste			    "cannot split request\n", devtoname(dev),
76254721Semaste			    uio->uio_resid, MAXPHYS);
77254721Semaste
78254721Semaste		if (uio->uio_iovcnt > 1)
79254721Semaste			printf("%s: request vectors=%d > 1, "
80254721Semaste			    "cannot split request\n", devtoname(dev),
81254721Semaste			    uio->uio_iovcnt);
82254721Semaste
83254721Semaste		error = EFBIG;
84254721Semaste		goto doerror;
85254721Semaste	}
86254721Semaste
87254721Semaste	for (i = 0; i < uio->uio_iovcnt; i++) {
88254721Semaste		while (uio->uio_iov[i].iov_len) {
89254721Semaste			bp->b_flags = 0;
90254721Semaste			if (uio->uio_rw == UIO_READ) {
91254721Semaste				bp->b_iocmd = BIO_READ;
92254721Semaste				curthread->td_ru.ru_inblock++;
93254721Semaste			} else {
94254721Semaste				bp->b_iocmd = BIO_WRITE;
95254721Semaste				curthread->td_ru.ru_oublock++;
96254721Semaste			}
97254721Semaste			bp->b_iodone = bdone;
98254721Semaste			bp->b_data = uio->uio_iov[i].iov_base;
99254721Semaste			bp->b_bcount = uio->uio_iov[i].iov_len;
100254721Semaste			bp->b_offset = uio->uio_offset;
101254721Semaste			bp->b_iooffset = uio->uio_offset;
102254721Semaste			bp->b_saveaddr = sa;
103254721Semaste
104254721Semaste			/* Don't exceed drivers iosize limit */
105254721Semaste			if (bp->b_bcount > dev->si_iosize_max)
106254721Semaste				bp->b_bcount = dev->si_iosize_max;
107254721Semaste
108254721Semaste			/*
109254721Semaste			 * Make sure the pbuf can map the request
110254721Semaste			 * XXX: The pbuf has kvasize = MAXPHYS so a request
111254721Semaste			 * XXX: larger than MAXPHYS - PAGE_SIZE must be
112254721Semaste			 * XXX: page aligned or it will be fragmented.
113254721Semaste			 */
114254721Semaste			iolen = ((vm_offset_t) bp->b_data) & PAGE_MASK;
115254721Semaste			if ((bp->b_bcount + iolen) > bp->b_kvasize) {
116254721Semaste				/*
117254721Semaste				 * This device does not want I/O to be split.
118254721Semaste				 */
119254721Semaste				if (dev->si_flags & SI_NOSPLIT) {
120254721Semaste					printf("%s: request ptr %#jx is not "
121254721Semaste					    "on a page boundary, cannot split "
122254721Semaste					    "request\n", devtoname(dev),
123254721Semaste					    (uintmax_t)bp->b_data);
124254721Semaste					error = EFBIG;
125254721Semaste					goto doerror;
126254721Semaste				}
127254721Semaste				bp->b_bcount = bp->b_kvasize;
128254721Semaste				if (iolen != 0)
129254721Semaste					bp->b_bcount -= PAGE_SIZE;
130254721Semaste			}
131254721Semaste			bp->b_bufsize = bp->b_bcount;
132254721Semaste
133254721Semaste			bp->b_blkno = btodb(bp->b_offset);
134254721Semaste
135254721Semaste			csw = dev->si_devsw;
136254721Semaste			if (uio->uio_segflg == UIO_USERSPACE) {
137254721Semaste				if (dev->si_flags & SI_UNMAPPED)
138254721Semaste					mapped = 0;
139254721Semaste				else
140254721Semaste					mapped = 1;
141254721Semaste				if (vmapbuf(bp, mapped) < 0) {
142254721Semaste					error = EFAULT;
143254721Semaste					goto doerror;
144254721Semaste				}
145254721Semaste			}
146254721Semaste
147254721Semaste			dev_strategy_csw(dev, csw, bp);
148254721Semaste			if (uio->uio_rw == UIO_READ)
149254721Semaste				bwait(bp, PRIBIO, "physrd");
150254721Semaste			else
151254721Semaste				bwait(bp, PRIBIO, "physwr");
152254721Semaste
153254721Semaste			if (uio->uio_segflg == UIO_USERSPACE)
154254721Semaste				vunmapbuf(bp);
155254721Semaste			iolen = bp->b_bcount - bp->b_resid;
156254721Semaste			if (iolen == 0 && !(bp->b_ioflags & BIO_ERROR))
157254721Semaste				goto doerror;	/* EOF */
158254721Semaste			uio->uio_iov[i].iov_len -= iolen;
159254721Semaste			uio->uio_iov[i].iov_base =
160254721Semaste			    (char *)uio->uio_iov[i].iov_base + iolen;
161254721Semaste			uio->uio_resid -= iolen;
162254721Semaste			uio->uio_offset += iolen;
163254721Semaste			if( bp->b_ioflags & BIO_ERROR) {
164254721Semaste				error = bp->b_error;
165254721Semaste				goto doerror;
166254721Semaste			}
167254721Semaste		}
168254721Semaste	}
169254721Semastedoerror:
170254721Semaste	relpbuf(bp, NULL);
171254721Semaste	PRELE(curproc);
172254721Semaste	return (error);
173254721Semaste}
174254721Semaste