xref: /netbsd-src/libexec/lfs_cleanerd/fdfs.c (revision b1c86f5f087524e68db12794ee9c3e3da1ab17a0)
1 /* $NetBSD: fdfs.c,v 1.7 2009/08/06 00:51:55 pooka Exp $	 */
2 
3 /*-
4  * Copyright (c) 2005 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Konrad E. Schroder <perseant@hhhh.org>.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*
33  * Buffer cache routines for a file-descriptor backed filesystem.
34  * This is part of lfs_cleanerd so there is also a "segment pointer" that
35  * we can make buffers out of without duplicating memory or reading the data
36  * again.
37  */
38 
39 #include <err.h>
40 #include <fcntl.h>
41 #include <time.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46 
47 #include <sys/syslog.h>
48 #include <sys/param.h>
49 #include <sys/mount.h>
50 #include <sys/stat.h>
51 
52 #include "vnode.h"
53 #include "bufcache.h"
54 #include "fdfs.h"
55 #include "kernelops.h"
56 
57 /*
58  * Return a "vnode" interface to a given file descriptor.
59  */
60 struct uvnode *
61 fd_vget(int fd, int bsize, int segsize, int nseg)
62 {
63 	struct fdfs *fs;
64 	struct uvnode *vp;
65 	int i;
66 
67 	fs = (struct fdfs *)malloc(sizeof(*fs));
68 	if (fs == NULL)
69 		return NULL;
70 	if (segsize > 0) {
71 		fs->fd_bufp = (struct fd_buf *)malloc(nseg *
72 						      sizeof(struct fd_buf));
73 		if (fs->fd_bufp == NULL) {
74 			free(fs);
75 			return NULL;
76 		}
77 		for (i = 0; i < nseg; i++) {
78 			fs->fd_bufp[i].start = 0x0;
79 			fs->fd_bufp[i].end = 0x0;
80 			fs->fd_bufp[i].buf = (char *)malloc(segsize);
81 			if (fs->fd_bufp[i].buf == NULL) {
82 				while (--i >= 0)
83 					free(fs->fd_bufp[i].buf);
84 				free(fs->fd_bufp);
85 				free(fs);
86 				return NULL;
87 			}
88 		}
89 	} else
90 		fs->fd_bufp = NULL;
91 
92 	fs->fd_fd = fd;
93 	fs->fd_bufc = nseg;
94 	fs->fd_bufi = 0;
95 	fs->fd_bsize = bsize;
96 	fs->fd_ssize = segsize;
97 
98 	vp = (struct uvnode *) malloc(sizeof(*vp));
99 	if (vp == NULL) {
100 		if (fs->fd_bufp) {
101 			for (i = nseg - 1; i >= 0; i--)
102 				free(fs->fd_bufp[i].buf);
103 			free(fs->fd_bufp);
104 		}
105 		free(fs);
106 		return NULL;
107 	}
108 	memset(vp, 0, sizeof(*vp));
109 	vp->v_fd = fd;
110 	vp->v_fs = fs;
111 	vp->v_usecount = 0;
112 	vp->v_strategy_op = fd_vop_strategy;
113 	vp->v_bwrite_op = fd_vop_bwrite;
114 	vp->v_bmap_op = fd_vop_bmap;
115 	LIST_INIT(&vp->v_cleanblkhd);
116 	LIST_INIT(&vp->v_dirtyblkhd);
117 	vp->v_data = NULL;
118 
119 	return vp;
120 }
121 
122 /*
123  * Deallocate a vnode.
124  */
125 void
126 fd_reclaim(struct uvnode *vp)
127 {
128 	int i;
129 	struct ubuf *bp;
130 	struct fdfs *fs;
131 
132 	while ((bp = LIST_FIRST(&vp->v_dirtyblkhd)) != NULL) {
133 		bremfree(bp);
134 		buf_destroy(bp);
135 	}
136 	while ((bp = LIST_FIRST(&vp->v_cleanblkhd)) != NULL) {
137 		bremfree(bp);
138 		buf_destroy(bp);
139 	}
140 
141 	fs = (struct fdfs *)vp->v_fs;
142 	for (i = 0; i < fs->fd_bufc; i++)
143 		free(fs->fd_bufp[i].buf);
144 	free(fs->fd_bufp);
145 	free(fs);
146 	memset(vp, 0, sizeof(vp));
147 }
148 
149 /*
150  * We won't be using that last segment after all.
151  */
152 void
153 fd_release(struct uvnode *vp)
154 {
155 	--((struct fdfs *)vp->v_fs)->fd_bufi;
156 }
157 
158 /*
159  * Reset buffer pointer to first buffer.
160  */
161 void
162 fd_release_all(struct uvnode *vp)
163 {
164 	((struct fdfs *)vp->v_fs)->fd_bufi = 0;
165 }
166 
167 /*
168  * Prepare a segment buffer which we will expect to read from.
169  * We never increment fd_bufi unless we have succeeded to allocate the space,
170  * if necessary, and have read the segment.
171  */
172 int
173 fd_preload(struct uvnode *vp, daddr_t start)
174 {
175 	struct fdfs *fs = (struct fdfs *)vp->v_fs;
176 	struct fd_buf *t;
177 	int r;
178 
179 	/* We might need to allocate more buffers. */
180 	if (fs->fd_bufi == fs->fd_bufc) {
181 		++fs->fd_bufc;
182 		syslog(LOG_DEBUG, "increasing number of segment buffers to %d",
183 			fs->fd_bufc);
184 		t = realloc(fs->fd_bufp, fs->fd_bufc * sizeof(struct fd_buf));
185 		if (t == NULL) {
186 			syslog(LOG_NOTICE, "failed resizing table to %d\n",
187 				fs->fd_bufc);
188 			return -1;
189 		}
190 		fs->fd_bufp = t;
191 		fs->fd_bufp[fs->fd_bufi].start = 0x0;
192 		fs->fd_bufp[fs->fd_bufi].end = 0x0;
193 		fs->fd_bufp[fs->fd_bufi].buf = (char *)malloc(fs->fd_ssize);
194 		if (fs->fd_bufp[fs->fd_bufi].buf == NULL) {
195 			syslog(LOG_NOTICE, "failed to allocate buffer #%d\n",
196 				fs->fd_bufc);
197 			--fs->fd_bufc;
198 			return -1;
199 		}
200 	}
201 
202 	/* Read the current buffer. */
203 	fs->fd_bufp[fs->fd_bufi].start = start;
204 	fs->fd_bufp[fs->fd_bufi].end =	 start + fs->fd_ssize / fs->fd_bsize;
205 
206 	if ((r = kops.ko_pread(fs->fd_fd, fs->fd_bufp[fs->fd_bufi].buf,
207 		       (size_t)fs->fd_ssize, start * fs->fd_bsize)) < 0) {
208 		syslog(LOG_ERR, "preload to segment buffer %d", fs->fd_bufi);
209 		return r;
210 	}
211 
212 	fs->fd_bufi = fs->fd_bufi + 1;
213 	return 0;
214 }
215 
216 /*
217  * Get a pointer to a block contained in one of the segment buffers,
218  * as if from bread() but avoiding the buffer cache.
219  */
220 char *
221 fd_ptrget(struct uvnode *vp, daddr_t bn)
222 {
223 	int i;
224 	struct fdfs *fs;
225 
226 	fs = (struct fdfs *)vp->v_fs;
227 	for (i = 0; i < fs->fd_bufc; i++) {
228 		if (bn >= fs->fd_bufp[i].start && bn < fs->fd_bufp[i].end) {
229 			return fs->fd_bufp[i].buf +
230 				(bn - fs->fd_bufp[i].start) * fs->fd_bsize;
231 		}
232 	}
233 	return NULL;
234 }
235 
236 /*
237  * Strategy routine.  We can read from the segment buffer if requested.
238  */
239 int
240 fd_vop_strategy(struct ubuf * bp)
241 {
242 	struct fdfs *fs;
243 	char *cp;
244 	int count;
245 
246 	fs = (struct fdfs *)bp->b_vp->v_fs;
247 	if (bp->b_flags & B_READ) {
248 		if ((cp = fd_ptrget(bp->b_vp, bp->b_blkno)) != NULL) {
249 			free(bp->b_data);
250 			bp->b_data = cp;
251 			bp->b_flags |= (B_DONTFREE | B_DONE);
252 			return 0;
253 		}
254 		count = kops.ko_pread(bp->b_vp->v_fd, bp->b_data, bp->b_bcount,
255 			      bp->b_blkno * fs->fd_bsize);
256 		if (count == bp->b_bcount)
257 			bp->b_flags |= B_DONE;
258 	} else {
259 		count = kops.ko_pwrite(bp->b_vp->v_fd, bp->b_data, bp->b_bcount,
260 			       bp->b_blkno * fs->fd_bsize);
261 		if (count == 0) {
262 			perror("pwrite");
263 			return -1;
264 		}
265 		bp->b_flags &= ~B_DELWRI;
266 		reassignbuf(bp, bp->b_vp);
267 	}
268 	return 0;
269 }
270 
271 /*
272  * Delayed write.
273  */
274 int
275 fd_vop_bwrite(struct ubuf * bp)
276 {
277 	bp->b_flags |= B_DELWRI;
278 	reassignbuf(bp, bp->b_vp);
279 	brelse(bp, 0);
280 	return 0;
281 }
282 
283 /*
284  * Map lbn to disk address.  Since we are using the file
285  * descriptor as the "disk", the disk address is meaningless
286  * and we just return the block address.
287  */
288 int
289 fd_vop_bmap(struct uvnode * vp, daddr_t lbn, daddr_t * daddrp)
290 {
291 	*daddrp = lbn;
292 	return 0;
293 }
294