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