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