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