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