1 /* $NetBSD: chfs_wbuf.c,v 1.5 2012/10/19 12:44:39 ttoth Exp $ */ 2 3 /*- 4 * Copyright (c) 2010 Department of Software Engineering, 5 * University of Szeged, Hungary 6 * Copyright (C) 2010 Tamas Toth <ttoth@inf.u-szeged.hu> 7 * Copyright (C) 2010 Adam Hoka <ahoka@NetBSD.org> 8 * All rights reserved. 9 * 10 * This code is derived from software contributed to The NetBSD Foundation 11 * by the Department of Software Engineering, University of Szeged, Hungary 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include <dev/flash/flash.h> 36 #include <sys/uio.h> 37 #include "chfs.h" 38 39 #define DBG_WBUF 1 /* XXX unused, but should be */ 40 41 #define PAD(x) (((x)+3)&~3) 42 43 #define EB_ADDRESS(x) ( rounddown((x), chmp->chm_ebh->eb_size) ) 44 45 #define PAGE_DIV(x) ( rounddown((x), chmp->chm_wbuf_pagesize) ) 46 #define PAGE_MOD(x) ( (x) % (chmp->chm_wbuf_pagesize) ) 47 48 /* writebuffer options */ 49 enum { 50 WBUF_NOPAD, 51 WBUF_SETPAD 52 }; 53 54 /* 55 * chfs_flush_wbuf - write wbuf to the flash 56 * Returns zero in case of success. 57 */ 58 static int 59 chfs_flush_wbuf(struct chfs_mount *chmp, int pad) 60 { 61 int ret; 62 size_t retlen; 63 struct chfs_node_ref *nref; 64 struct chfs_flash_padding_node* padnode; 65 66 KASSERT(mutex_owned(&chmp->chm_lock_mountfields)); 67 KASSERT(mutex_owned(&chmp->chm_lock_sizes)); 68 KASSERT(rw_write_held(&chmp->chm_lock_wbuf)); 69 KASSERT(pad == WBUF_SETPAD || pad == WBUF_NOPAD); 70 71 /* check padding option */ 72 if (pad == WBUF_SETPAD) { 73 chmp->chm_wbuf_len = PAD(chmp->chm_wbuf_len); 74 memset(chmp->chm_wbuf + chmp->chm_wbuf_len, 0, 75 chmp->chm_wbuf_pagesize - chmp->chm_wbuf_len); 76 77 /* add a padding node */ 78 padnode = (void *)(chmp->chm_wbuf + chmp->chm_wbuf_len); 79 padnode->magic = htole16(CHFS_FS_MAGIC_BITMASK); 80 padnode->type = htole16(CHFS_NODETYPE_PADDING); 81 padnode->length = htole32(chmp->chm_wbuf_pagesize 82 - chmp->chm_wbuf_len); 83 padnode->hdr_crc = htole32(crc32(0, (uint8_t *)padnode, 84 sizeof(*padnode)-4)); 85 86 nref = chfs_alloc_node_ref(chmp->chm_nextblock); 87 nref->nref_offset = chmp->chm_wbuf_ofs + chmp->chm_wbuf_len; 88 nref->nref_offset = CHFS_GET_OFS(nref->nref_offset) | 89 CHFS_OBSOLETE_NODE_MASK; 90 chmp->chm_wbuf_len = chmp->chm_wbuf_pagesize; 91 92 /* change sizes after padding node */ 93 chfs_change_size_free(chmp, chmp->chm_nextblock, 94 -padnode->length); 95 chfs_change_size_wasted(chmp, chmp->chm_nextblock, 96 padnode->length); 97 } 98 99 /* write out the buffer */ 100 ret = chfs_write_leb(chmp, chmp->chm_nextblock->lnr, chmp->chm_wbuf, 101 chmp->chm_wbuf_ofs, chmp->chm_wbuf_len, &retlen); 102 if (ret) { 103 return ret; 104 } 105 106 /* reset the buffer */ 107 memset(chmp->chm_wbuf, 0xff, chmp->chm_wbuf_pagesize); 108 chmp->chm_wbuf_ofs += chmp->chm_wbuf_pagesize; 109 chmp->chm_wbuf_len = 0; 110 111 return 0; 112 } 113 114 115 /* 116 * chfs_fill_wbuf - write data to wbuf 117 * Return the len of the buf what we didn't write to the wbuf. 118 */ 119 static size_t 120 chfs_fill_wbuf(struct chfs_mount *chmp, const u_char *buf, size_t len) 121 { 122 /* check available space */ 123 if (len && !chmp->chm_wbuf_len && (len >= chmp->chm_wbuf_pagesize)) { 124 return 0; 125 } 126 /* check buffer's length */ 127 if (len > (chmp->chm_wbuf_pagesize - chmp->chm_wbuf_len)) { 128 len = chmp->chm_wbuf_pagesize - chmp->chm_wbuf_len; 129 } 130 /* write into the wbuf */ 131 memcpy(chmp->chm_wbuf + chmp->chm_wbuf_len, buf, len); 132 133 /* update the actual length of writebuffer */ 134 chmp->chm_wbuf_len += (int) len; 135 return len; 136 } 137 138 /* 139 * chfs_write_wbuf - write to wbuf and then the flash 140 * Returns zero in case of success. 141 */ 142 int 143 chfs_write_wbuf(struct chfs_mount* chmp, const struct iovec *invecs, long count, 144 off_t to, size_t *retlen) 145 { 146 int invec, ret = 0; 147 size_t wbuf_retlen, donelen = 0; 148 int outvec_to = to; 149 150 int lnr = chmp->chm_nextblock->lnr; 151 152 KASSERT(mutex_owned(&chmp->chm_lock_mountfields)); 153 KASSERT(mutex_owned(&chmp->chm_lock_sizes)); 154 KASSERT(!rw_write_held(&chmp->chm_lock_wbuf)); 155 156 rw_enter(&chmp->chm_lock_wbuf, RW_WRITER); 157 158 if (chmp->chm_wbuf_ofs == 0xffffffff) { 159 chmp->chm_wbuf_ofs = PAGE_DIV(to); 160 chmp->chm_wbuf_len = PAGE_MOD(to); 161 memset(chmp->chm_wbuf, 0xff, chmp->chm_wbuf_pagesize); 162 } 163 164 if (EB_ADDRESS(to) != EB_ADDRESS(chmp->chm_wbuf_ofs)) { 165 if (chmp->chm_wbuf_len) { 166 ret = chfs_flush_wbuf(chmp, WBUF_SETPAD); 167 if (ret) 168 goto outerr; 169 } 170 chmp->chm_wbuf_ofs = PAGE_DIV(to); 171 chmp->chm_wbuf_len = PAGE_MOD(to); 172 } 173 174 if (to != PAD(chmp->chm_wbuf_ofs + chmp->chm_wbuf_len)) { 175 dbg("to: %llu != %zu\n", (unsigned long long)to, 176 PAD(chmp->chm_wbuf_ofs + chmp->chm_wbuf_len)); 177 dbg("Non-contiguous write\n"); 178 panic("BUG\n"); 179 } 180 181 /* adjust alignment offset */ 182 if (chmp->chm_wbuf_len != PAGE_MOD(to)) { 183 chmp->chm_wbuf_len = PAGE_MOD(to); 184 /* take care of alignement to next page */ 185 if (!chmp->chm_wbuf_len) { 186 chmp->chm_wbuf_len += chmp->chm_wbuf_pagesize; 187 ret = chfs_flush_wbuf(chmp, WBUF_NOPAD); 188 if (ret) 189 goto outerr; 190 } 191 } 192 193 for (invec = 0; invec < count; invec++) { 194 int vlen = invecs[invec].iov_len; 195 u_char* v = invecs[invec].iov_base; 196 197 /* fill the whole wbuf */ 198 wbuf_retlen = chfs_fill_wbuf(chmp, v, vlen); 199 if (chmp->chm_wbuf_len == chmp->chm_wbuf_pagesize) { 200 ret = chfs_flush_wbuf(chmp, WBUF_NOPAD); 201 if (ret) { 202 goto outerr; 203 } 204 } 205 206 vlen -= wbuf_retlen; 207 outvec_to += wbuf_retlen; 208 v += wbuf_retlen; 209 donelen += wbuf_retlen; 210 211 /* if there is more residual data than the length of the wbuf 212 * write it out directly until it's fit in the wbuf */ 213 if (vlen >= chmp->chm_wbuf_pagesize) { 214 ret = chfs_write_leb(chmp, lnr, v, outvec_to, PAGE_DIV(vlen), &wbuf_retlen); 215 vlen -= wbuf_retlen; 216 outvec_to += wbuf_retlen; 217 chmp->chm_wbuf_ofs = outvec_to; 218 v += wbuf_retlen; 219 donelen += wbuf_retlen; 220 } 221 222 /* write the residual data to the wbuf */ 223 wbuf_retlen = chfs_fill_wbuf(chmp, v, vlen); 224 if (chmp->chm_wbuf_len == chmp->chm_wbuf_pagesize) { 225 ret = chfs_flush_wbuf(chmp, WBUF_NOPAD); 226 if (ret) 227 goto outerr; 228 } 229 230 outvec_to += wbuf_retlen; 231 donelen += wbuf_retlen; 232 } 233 *retlen = donelen; 234 rw_exit(&chmp->chm_lock_wbuf); 235 return ret; 236 237 outerr: 238 *retlen = 0; 239 return ret; 240 } 241 242 /* 243 * chfs_flush_peding_wbuf - write wbuf to the flash 244 * Used when we must flush wbuf right now. 245 * If wbuf has free space, pad it to the size of wbuf and write out. 246 */ 247 int chfs_flush_pending_wbuf(struct chfs_mount *chmp) 248 { 249 int err; 250 KASSERT(mutex_owned(&chmp->chm_lock_mountfields)); 251 mutex_enter(&chmp->chm_lock_sizes); 252 rw_enter(&chmp->chm_lock_wbuf, RW_WRITER); 253 err = chfs_flush_wbuf(chmp, WBUF_SETPAD); 254 rw_exit(&chmp->chm_lock_wbuf); 255 mutex_exit(&chmp->chm_lock_sizes); 256 return err; 257 } 258