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