Home | History | Annotate | Line # | Download | only in lfs_cleanerd
      1 /* $NetBSD: fdfs.c,v 1.10 2013/06/18 18:18:57 christos 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 (at) 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 = malloc(sizeof(*fs));
     68 	if (fs == NULL)
     69 		return NULL;
     70 	if (segsize > 0) {
     71 		fs->fd_bufp = malloc(nseg * sizeof(struct fd_buf));
     72 		if (fs->fd_bufp == NULL) {
     73 			free(fs);
     74 			return NULL;
     75 		}
     76 		for (i = 0; i < nseg; i++) {
     77 			fs->fd_bufp[i].start = 0x0;
     78 			fs->fd_bufp[i].end = 0x0;
     79 			fs->fd_bufp[i].buf = malloc(segsize);
     80 			if (fs->fd_bufp[i].buf == NULL) {
     81 				while (--i >= 0)
     82 					free(fs->fd_bufp[i].buf);
     83 				free(fs->fd_bufp);
     84 				free(fs);
     85 				return NULL;
     86 			}
     87 		}
     88 	} else
     89 		fs->fd_bufp = NULL;
     90 
     91 	fs->fd_fd = fd;
     92 	fs->fd_bufc = nseg;
     93 	fs->fd_bufi = 0;
     94 	fs->fd_bsize = bsize;
     95 	fs->fd_ssize = segsize;
     96 
     97 	vp = malloc(sizeof(*vp));
     98 	if (vp == NULL) {
     99 		if (fs->fd_bufp) {
    100 			for (i = 0; i < nseg; i++)
    101 				free(fs->fd_bufp[i].buf);
    102 			free(fs->fd_bufp);
    103 		}
    104 		free(fs);
    105 		return NULL;
    106 	}
    107 	memset(vp, 0, sizeof(*vp));
    108 	vp->v_fd = fd;
    109 	vp->v_fs = fs;
    110 	vp->v_usecount = 0;
    111 	vp->v_strategy_op = fd_vop_strategy;
    112 	vp->v_bwrite_op = fd_vop_bwrite;
    113 	vp->v_bmap_op = fd_vop_bmap;
    114 	LIST_INIT(&vp->v_cleanblkhd);
    115 	LIST_INIT(&vp->v_dirtyblkhd);
    116 	vp->v_data = NULL;
    117 
    118 	return vp;
    119 }
    120 
    121 /*
    122  * Deallocate a vnode.
    123  */
    124 void
    125 fd_reclaim(struct uvnode *vp)
    126 {
    127 	int i;
    128 	struct ubuf *bp;
    129 	struct fdfs *fs;
    130 
    131 	while ((bp = LIST_FIRST(&vp->v_dirtyblkhd)) != NULL) {
    132 		bremfree(bp);
    133 		buf_destroy(bp);
    134 	}
    135 	while ((bp = LIST_FIRST(&vp->v_cleanblkhd)) != NULL) {
    136 		bremfree(bp);
    137 		buf_destroy(bp);
    138 	}
    139 
    140 	fs = (struct fdfs *)vp->v_fs;
    141 	for (i = 0; i < fs->fd_bufc; i++)
    142 		free(fs->fd_bufp[i].buf);
    143 	free(fs->fd_bufp);
    144 	free(fs);
    145 	memset(vp, 0, sizeof(*vp));
    146 }
    147 
    148 /*
    149  * We won't be using that last segment after all.
    150  */
    151 void
    152 fd_release(struct uvnode *vp)
    153 {
    154 	--((struct fdfs *)vp->v_fs)->fd_bufi;
    155 }
    156 
    157 /*
    158  * Reset buffer pointer to first buffer.
    159  */
    160 void
    161 fd_release_all(struct uvnode *vp)
    162 {
    163 	((struct fdfs *)vp->v_fs)->fd_bufi = 0;
    164 }
    165 
    166 /*
    167  * Prepare a segment buffer which we will expect to read from.
    168  * We never increment fd_bufi unless we have succeeded to allocate the space,
    169  * if necessary, and have read the segment.
    170  */
    171 int
    172 fd_preload(struct uvnode *vp, daddr_t start)
    173 {
    174 	struct fdfs *fs = (struct fdfs *)vp->v_fs;
    175 	struct fd_buf *t;
    176 	int r;
    177 
    178 	/* We might need to allocate more buffers. */
    179 	if (fs->fd_bufi == fs->fd_bufc) {
    180 		++fs->fd_bufc;
    181 		syslog(LOG_DEBUG, "increasing number of segment buffers to %d",
    182 			fs->fd_bufc);
    183 		t = realloc(fs->fd_bufp, fs->fd_bufc * sizeof(struct fd_buf));
    184 		if (t == NULL) {
    185 			syslog(LOG_NOTICE, "failed resizing table to %d\n",
    186 				fs->fd_bufc);
    187 			return -1;
    188 		}
    189 		fs->fd_bufp = t;
    190 		fs->fd_bufp[fs->fd_bufi].start = 0x0;
    191 		fs->fd_bufp[fs->fd_bufi].end = 0x0;
    192 		fs->fd_bufp[fs->fd_bufi].buf = malloc(fs->fd_ssize);
    193 		if (fs->fd_bufp[fs->fd_bufi].buf == NULL) {
    194 			syslog(LOG_NOTICE, "failed to allocate buffer #%d\n",
    195 				fs->fd_bufc);
    196 			--fs->fd_bufc;
    197 			return -1;
    198 		}
    199 	}
    200 
    201 	/* Read the current buffer. */
    202 	fs->fd_bufp[fs->fd_bufi].start = start;
    203 	fs->fd_bufp[fs->fd_bufi].end =	 start + fs->fd_ssize / fs->fd_bsize;
    204 
    205 	if ((r = kops.ko_pread(fs->fd_fd, fs->fd_bufp[fs->fd_bufi].buf,
    206 		       (size_t)fs->fd_ssize, start * fs->fd_bsize)) < 0) {
    207 		syslog(LOG_ERR, "preload to segment buffer %d", fs->fd_bufi);
    208 		return r;
    209 	}
    210 
    211 	fs->fd_bufi = fs->fd_bufi + 1;
    212 	return 0;
    213 }
    214 
    215 /*
    216  * Get a pointer to a block contained in one of the segment buffers,
    217  * as if from bread() but avoiding the buffer cache.
    218  */
    219 char *
    220 fd_ptrget(struct uvnode *vp, daddr_t bn)
    221 {
    222 	int i;
    223 	struct fdfs *fs;
    224 
    225 	fs = (struct fdfs *)vp->v_fs;
    226 	for (i = 0; i < fs->fd_bufc; i++) {
    227 		if (bn >= fs->fd_bufp[i].start && bn < fs->fd_bufp[i].end) {
    228 			return fs->fd_bufp[i].buf +
    229 				(bn - fs->fd_bufp[i].start) * fs->fd_bsize;
    230 		}
    231 	}
    232 	return NULL;
    233 }
    234 
    235 /*
    236  * Strategy routine.  We can read from the segment buffer if requested.
    237  */
    238 int
    239 fd_vop_strategy(struct ubuf * bp)
    240 {
    241 	struct fdfs *fs;
    242 	char *cp;
    243 	int count;
    244 
    245 	fs = (struct fdfs *)bp->b_vp->v_fs;
    246 	if (bp->b_flags & B_READ) {
    247 		if ((cp = fd_ptrget(bp->b_vp, bp->b_blkno)) != NULL) {
    248 			free(bp->b_data);
    249 			bp->b_data = cp;
    250 			bp->b_flags |= (B_DONTFREE | B_DONE);
    251 			return 0;
    252 		}
    253 		count = kops.ko_pread(bp->b_vp->v_fd, bp->b_data, bp->b_bcount,
    254 			      bp->b_blkno * fs->fd_bsize);
    255 		if (count == bp->b_bcount)
    256 			bp->b_flags |= B_DONE;
    257 	} else {
    258 		count = kops.ko_pwrite(bp->b_vp->v_fd, bp->b_data, bp->b_bcount,
    259 			       bp->b_blkno * fs->fd_bsize);
    260 		if (count == 0) {
    261 			perror("pwrite");
    262 			return -1;
    263 		}
    264 		bp->b_flags &= ~B_DELWRI;
    265 		reassignbuf(bp, bp->b_vp);
    266 	}
    267 	return 0;
    268 }
    269 
    270 /*
    271  * Delayed write.
    272  */
    273 int
    274 fd_vop_bwrite(struct ubuf * bp)
    275 {
    276 	bp->b_flags |= B_DELWRI;
    277 	reassignbuf(bp, bp->b_vp);
    278 	brelse(bp, 0);
    279 	return 0;
    280 }
    281 
    282 /*
    283  * Map lbn to disk address.  Since we are using the file
    284  * descriptor as the "disk", the disk address is meaningless
    285  * and we just return the block address.
    286  */
    287 int
    288 fd_vop_bmap(struct uvnode * vp, daddr_t lbn, daddr_t * daddrp)
    289 {
    290 	*daddrp = lbn;
    291 	return 0;
    292 }
    293