xref: /freebsd/sys/kern/kern_physio.c (revision e627b39baccd1ec9129690167cf5e6d860509655)
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.16 1995/12/13 15:12:59 julian 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 
31 static void	physwakeup __P((struct buf *bp));
32 
33 int
34 physio(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 				tsleep((caddr_t)bp, PRIBIO, "physstr", 0);
116 			splx(spl);
117 
118 			/* release mapping into kernel space */
119 			if (uio->uio_segflg == UIO_USERSPACE)
120 				vunmapbuf(bp);
121 
122 			/*
123 			 * update the uio data
124 			 */
125 			{
126 				int iolen = bp->b_bcount - bp->b_resid;
127 
128 				if (iolen == 0 && !(bp->b_flags & B_ERROR))
129 					goto doerror;	/* EOF */
130 				uio->uio_iov[i].iov_len -= iolen;
131 				uio->uio_iov[i].iov_base += iolen;
132 				uio->uio_resid -= iolen;
133 				uio->uio_offset += iolen;
134 			}
135 
136 			/*
137 			 * check for an error
138 			 */
139 			if( bp->b_flags & B_ERROR) {
140 				error = bp->b_error;
141 				goto doerror;
142 			}
143 		}
144 	}
145 
146 
147 doerror:
148 	relpbuf(bpa);
149 	if (!bp_alloc) {
150 		bp->b_flags &= ~(B_BUSY|B_PHYS);
151 		if( bp->b_flags & B_WANTED) {
152 			bp->b_flags &= ~B_WANTED;
153 			wakeup((caddr_t)bp);
154 		}
155 	}
156 /*
157  * allow the process to be swapped
158  */
159 	curproc->p_flag &= ~P_PHYSIO;
160 
161 	return (error);
162 }
163 
164 u_int
165 minphys(struct buf *bp)
166 {
167 	u_int maxphys = MAXPHYS;
168 
169 	if( ((vm_offset_t) bp->b_data) & PAGE_MASK) {
170 		maxphys = MAXPHYS - PAGE_SIZE;
171 	}
172 
173 	if( bp->b_bcount > maxphys) {
174 		bp->b_bcount = maxphys;
175 	}
176 	return bp->b_bcount;
177 }
178 
179 int
180 rawread(dev_t dev, struct uio *uio, int ioflag)
181 {
182 	return (physio(cdevsw[major(dev)]->d_strategy, (struct buf *)NULL,
183 	    dev, 1, minphys, uio));
184 }
185 
186 int
187 rawwrite(dev_t dev, struct uio *uio, int ioflag)
188 {
189 	return (physio(cdevsw[major(dev)]->d_strategy, (struct buf *)NULL,
190 	    dev, 0, minphys, uio));
191 }
192 
193 static void
194 physwakeup(bp)
195 	struct buf *bp;
196 {
197 	wakeup((caddr_t) bp);
198 	bp->b_flags &= ~B_CALL;
199 }
200