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