xref: /freebsd/sys/kern/kern_physio.c (revision afe61c15161c324a7af299a9b8457aba5afc92db)
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 
27 static void physwakeup();
28 
29 int
30 physio(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 
123 doerror:
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 
141 u_int
142 minphys(struct buf *bp)
143 {
144 
145 	if( bp->b_bcount > MAXBSIZE) {
146 		bp->b_bcount = MAXBSIZE;
147 	}
148 	return bp->b_bcount;
149 }
150 
151 int
152 rawread(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 
158 int
159 rawwrite(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 
165 static void
166 physwakeup(bp)
167 	struct buf *bp;
168 {
169 	wakeup((caddr_t) bp);
170 	bp->b_flags &= ~B_CALL;
171 }
172