kern_physio.c revision 1817
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$
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();
30
31int
32physio(strategy, bp, dev, rw, minp, uio)
33	int (*strategy)();
34	struct buf *bp;
35	dev_t dev;
36	int rw;
37	u_int (*minp)();
38	struct uio *uio;
39{
40	int i;
41	int bp_alloc = (bp == 0);
42	int bufflags = rw?B_READ:0;
43	int error;
44	int spl;
45
46/*
47 * keep the process from being swapped
48 */
49	curproc->p_flag |= P_PHYSIO;
50
51	/* create and build a buffer header for a transfer */
52
53	if (bp_alloc) {
54		bp = (struct buf *)getpbuf();
55	} else {
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	}
64
65	bp->b_proc = curproc;
66	bp->b_dev = dev;
67	error = bp->b_error = 0;
68
69	for(i=0;i<uio->uio_iovcnt;i++) {
70		while( uio->uio_iov[i].iov_len) {
71			vm_offset_t v, lastv, pa;
72			caddr_t adr;
73
74			bp->b_bcount = uio->uio_iov[i].iov_len;
75			bp->b_bufsize = bp->b_bcount;
76			bp->b_flags = B_BUSY | B_PHYS | B_CALL | bufflags;
77			bp->b_iodone = physwakeup;
78			bp->b_data = uio->uio_iov[i].iov_base;
79			bp->b_blkno = btodb(uio->uio_offset);
80
81
82			if (rw && !useracc(bp->b_data, bp->b_bufsize, B_WRITE)) {
83				error = EFAULT;
84				goto doerror;
85			}
86			if (!rw && !useracc(bp->b_data, bp->b_bufsize, B_READ)) {
87				error = EFAULT;
88				goto doerror;
89			}
90
91			vmapbuf(bp);
92
93			/* perform transfer */
94			(*strategy)(bp);
95
96			spl = splbio();
97			while ((bp->b_flags & B_DONE) == 0)
98				tsleep((caddr_t)bp, PRIBIO, "physstr", 0);
99			splx(spl);
100
101			vunmapbuf(bp);
102
103			/*
104			 * update the uio data
105			 */
106			{
107				int iolen = bp->b_bcount - bp->b_resid;
108				uio->uio_iov[i].iov_len -= iolen;
109				uio->uio_iov[i].iov_base += iolen;
110				uio->uio_resid -= iolen;
111				uio->uio_offset += iolen;
112			}
113
114			/*
115			 * check for an error
116			 */
117			if( bp->b_flags & B_ERROR) {
118				error = bp->b_error;
119				goto doerror;
120			}
121		}
122	}
123
124
125doerror:
126	if (bp_alloc) {
127		relpbuf(bp);
128	} else {
129		bp->b_flags &= ~(B_BUSY|B_PHYS);
130		if( bp->b_flags & B_WANTED) {
131			bp->b_flags &= ~B_WANTED;
132			wakeup((caddr_t)bp);
133		}
134	}
135/*
136 * allow the process to be swapped
137 */
138	curproc->p_flag &= ~P_PHYSIO;
139
140	return (error);
141}
142
143u_int
144minphys(struct buf *bp)
145{
146
147	if( bp->b_bcount > MAXBSIZE) {
148		bp->b_bcount = MAXBSIZE;
149	}
150	return bp->b_bcount;
151}
152
153int
154rawread(dev_t dev, struct uio *uio)
155{
156	return (physio(cdevsw[major(dev)].d_strategy, (struct buf *)NULL,
157	    dev, 1, minphys, uio));
158}
159
160int
161rawwrite(dev_t dev, struct uio *uio)
162{
163	return (physio(cdevsw[major(dev)].d_strategy, (struct buf *)NULL,
164	    dev, 0, minphys, uio));
165}
166
167static void
168physwakeup(bp)
169	struct buf *bp;
170{
171	wakeup((caddr_t) bp);
172	bp->b_flags &= ~B_CALL;
173}
174