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