xref: /illumos-gate/usr/src/uts/common/os/fbio.c (revision 7c478bd9)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
23 /*	  All Rights Reserved  	*/
24 
25 
26 /*
27  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
28  * Use is subject to license terms.
29  */
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 
33 #include <sys/types.h>
34 #include <sys/buf.h>
35 #include <sys/cmn_err.h>
36 #include <sys/conf.h>
37 #include <sys/debug.h>
38 #include <sys/errno.h>
39 #include <sys/fbuf.h>
40 #include <sys/kmem.h>
41 #include <sys/param.h>
42 #include <sys/sysmacros.h>
43 #include <sys/vfs.h>
44 #include <sys/cred.h>
45 #include <sys/vnode.h>
46 #include <vm/hat.h>
47 #include <vm/as.h>
48 #include <vm/seg.h>
49 #include <vm/seg_kmem.h>
50 #include <vm/seg_map.h>
51 #include <vm/seg_kpm.h>
52 
53 
54 /*
55  * Pseudo-bio routines which use a segmap mapping to address file data.
56  */
57 
58 /*
59  * Return a pointer to locked kernel virtual address for
60  * the given <vp, off> for len bytes.  It is not allowed to
61  * have the offset cross a MAXBSIZE boundary over len bytes.
62  */
63 int
fbread(vnode_t * vp,offset_t off,uint_t len,enum seg_rw rw,struct fbuf ** fbpp)64 fbread(vnode_t *vp, offset_t off, uint_t len, enum seg_rw rw,
65 	struct fbuf **fbpp)
66 {
67 	caddr_t addr;
68 	ulong_t o;
69 	struct fbuf *fbp;
70 	faultcode_t err;
71 	caddr_t	raddr;
72 	uint_t	rsize;
73 	uintptr_t pgoff = PAGEOFFSET;
74 
75 	o = (ulong_t)(off & (offset_t)MAXBOFFSET);
76 	if (o + len > MAXBSIZE)
77 		cmn_err(CE_PANIC, "fbread");
78 
79 	if (segmap_kpm) {
80 		addr = segmap_getmapflt(segkmap, vp, off & (offset_t)MAXBMASK,
81 					MAXBSIZE, SM_LOCKPROTO, rw);
82 	} else {
83 		addr = segmap_getmapflt(segkmap, vp,
84 				off & (offset_t)MAXBMASK, MAXBSIZE, 0, rw);
85 	}
86 
87 	raddr = (caddr_t)((uintptr_t)(addr + o) & ~pgoff);
88 	rsize = (((uintptr_t)(addr + o) + len + pgoff) & ~pgoff) -
89 	    (uintptr_t)raddr;
90 
91 	err = segmap_fault(kas.a_hat, segkmap, raddr, rsize, F_SOFTLOCK, rw);
92 	if (err) {
93 		(void) segmap_release(segkmap, addr, 0);
94 		if (FC_CODE(err) == FC_OBJERR)
95 			return (FC_ERRNO(err));
96 		else
97 			return (EIO);
98 	}
99 
100 	*fbpp = fbp = kmem_alloc(sizeof (struct fbuf), KM_SLEEP);
101 	fbp->fb_addr = addr + o;
102 	fbp->fb_count = len;
103 	return (0);
104 }
105 
106 /*
107  * Similar to fbread() but we call segmap_pagecreate instead of using
108  * segmap_fault for SOFTLOCK to create the pages without using VOP_GETPAGE
109  * and then we zero up to the length rounded to a page boundary.
110  * XXX - this won't work right when bsize < PAGESIZE!!!
111  */
112 void
fbzero(vnode_t * vp,offset_t off,uint_t len,struct fbuf ** fbpp)113 fbzero(vnode_t *vp, offset_t off, uint_t len, struct fbuf **fbpp)
114 {
115 	caddr_t addr;
116 	ulong_t o, zlen;
117 	struct fbuf *fbp;
118 
119 	o = (ulong_t)(off & MAXBOFFSET);
120 	if (o + len > MAXBSIZE)
121 		cmn_err(CE_PANIC, "fbzero: Bad offset/length");
122 
123 	if (segmap_kpm) {
124 		addr = segmap_getmapflt(segkmap, vp, off & (offset_t)MAXBMASK,
125 				MAXBSIZE, SM_PAGECREATE, S_WRITE) + o;
126 	} else {
127 		addr = segmap_getmap(segkmap, vp, off & (offset_t)MAXBMASK) + o;
128 	}
129 
130 	*fbpp = fbp = kmem_alloc(sizeof (struct fbuf), KM_SLEEP);
131 	fbp->fb_addr = addr;
132 	fbp->fb_count = len;
133 
134 	(void) segmap_pagecreate(segkmap, addr, len, 1);
135 
136 	/*
137 	 * Now we zero all the memory in the mapping we are interested in.
138 	 */
139 	zlen = (caddr_t)ptob(btopr((uintptr_t)(len + addr))) - addr;
140 	if (zlen < len || (o + zlen > MAXBSIZE))
141 		cmn_err(CE_PANIC, "fbzero: Bad zlen");
142 	bzero(addr, zlen);
143 }
144 
145 /*
146  * FBCOMMON() is the common code for fbrelse, fbwrite and variants thereof:
147  *
148  * fbrelse()	release fbp
149  * fbwrite()	direct write
150  * fbdwrite()	delayed write
151  */
152 #define	FBCOMMON(fbp, rw, flags, howtoreturn) \
153 { \
154 	caddr_t addr; \
155 	size_t size; \
156 	uintptr_t pgoff = PAGEOFFSET; \
157 	addr = (caddr_t)((uintptr_t)fbp->fb_addr & ~pgoff); \
158 	size = ((fbp->fb_addr - addr) + fbp->fb_count + pgoff) & ~pgoff; \
159 	(void) segmap_fault(kas.a_hat, segkmap, addr, size, F_SOFTUNLOCK, rw); \
160 	addr = (caddr_t)((uintptr_t)fbp->fb_addr & MAXBMASK); \
161 	kmem_free(fbp, sizeof (struct fbuf)); \
162 	howtoreturn(segmap_release(segkmap, addr, flags)); \
163 }
164 
165 void
fbrelse(struct fbuf * fbp,enum seg_rw rw)166 fbrelse(struct fbuf *fbp, enum seg_rw rw)
167 {
168 	FBCOMMON(fbp, rw, 0, (void))
169 }
170 
171 int
fbwrite(struct fbuf * fbp)172 fbwrite(struct fbuf *fbp)
173 {
174 	FBCOMMON(fbp, S_WRITE, SM_WRITE, return)
175 }
176 
177 int
fbdwrite(struct fbuf * fbp)178 fbdwrite(struct fbuf *fbp)
179 {
180 	FBCOMMON(fbp, S_WRITE, 0, return)
181 }
182 
183 /*
184  * Perform a synchronous indirect write of the given block number
185  * on the given device, using the given fbuf.  Upon return the fbp
186  * is invalid.
187  */
188 int
fbiwrite(struct fbuf * fbp,vnode_t * devvp,daddr_t bn,int bsize)189 fbiwrite(struct fbuf *fbp, vnode_t *devvp, daddr_t bn, int bsize)
190 {
191 	struct buf *bp;
192 	int error, fberror;
193 
194 	/*
195 	 * Allocate a temp bp using pageio_setup, but then use it
196 	 * for physio to the area mapped by fbuf which is currently
197 	 * all locked down in place.
198 	 *
199 	 * XXX - need to have a generalized bp header facility
200 	 * which we build up pageio_setup on top of.  Other places
201 	 * (like here and in device drivers for the raw I/O case)
202 	 * could then use these new facilities in a more straight
203 	 * forward fashion instead of playing all these games.
204 	 */
205 	bp = pageio_setup((struct page *)NULL, fbp->fb_count, devvp, B_WRITE);
206 	bp->b_flags &= ~B_PAGEIO;		/* XXX */
207 	bp->b_un.b_addr = fbp->fb_addr;
208 
209 	bp->b_blkno = bn * btod(bsize);
210 	bp->b_dev = cmpdev(devvp->v_rdev);	/* store in old dev format */
211 	bp->b_edev = devvp->v_rdev;
212 	bp->b_proc = NULL;			/* i.e. the kernel */
213 
214 	(void) bdev_strategy(bp);
215 	error = biowait(bp);
216 	pageio_done(bp);
217 
218 	/*CSTYLED*/
219 	FBCOMMON(fbp, S_OTHER, 0, fberror = )
220 
221 	return (error ? error : fberror);
222 }
223