kern_physio.c revision 28013
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.19 1997/02/22 09:39:08 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#include <vm/vm_param.h>
29#include <vm/vm_extern.h>
30
31static void	physwakeup __P((struct buf *bp));
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 *)getpbuf();
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	bp->b_dev = dev;
75	error = bp->b_error = 0;
76
77	for(i=0;i<uio->uio_iovcnt;i++) {
78		while( uio->uio_iov[i].iov_len) {
79
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#if defined(NO_SCHEDULE_MODS)
116				tsleep((caddr_t)bp, PRIBIO, "physstr", 0);
117#else
118				tsleep((caddr_t)bp, curproc->p_usrpri, "physstr", 0);
119#endif
120			splx(spl);
121
122			/* release mapping into kernel space */
123			if (uio->uio_segflg == UIO_USERSPACE)
124				vunmapbuf(bp);
125
126			/*
127			 * update the uio data
128			 */
129			{
130				int iolen = bp->b_bcount - bp->b_resid;
131
132				if (iolen == 0 && !(bp->b_flags & B_ERROR))
133					goto doerror;	/* EOF */
134				uio->uio_iov[i].iov_len -= iolen;
135				uio->uio_iov[i].iov_base += iolen;
136				uio->uio_resid -= iolen;
137				uio->uio_offset += iolen;
138			}
139
140			/*
141			 * check for an error
142			 */
143			if( bp->b_flags & B_ERROR) {
144				error = bp->b_error;
145				goto doerror;
146			}
147		}
148	}
149
150
151doerror:
152	relpbuf(bpa);
153	if (!bp_alloc) {
154		bp->b_flags &= ~(B_BUSY|B_PHYS);
155		if( bp->b_flags & B_WANTED) {
156			bp->b_flags &= ~B_WANTED;
157			wakeup((caddr_t)bp);
158		}
159	}
160/*
161 * allow the process to be swapped
162 */
163	curproc->p_flag &= ~P_PHYSIO;
164
165	return (error);
166}
167
168u_int
169minphys(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_t dev, struct uio *uio, int ioflag)
185{
186	return (physio(cdevsw[major(dev)]->d_strategy, (struct buf *)NULL,
187	    dev, 1, minphys, uio));
188}
189
190int
191rawwrite(dev_t dev, struct uio *uio, int ioflag)
192{
193	return (physio(cdevsw[major(dev)]->d_strategy, (struct buf *)NULL,
194	    dev, 0, minphys, uio));
195}
196
197static void
198physwakeup(bp)
199	struct buf *bp;
200{
201	wakeup((caddr_t) bp);
202	bp->b_flags &= ~B_CALL;
203}
204