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