1 /* $NetBSD: chfs_write.c,v 1.4 2012/08/10 09:26:58 ttoth Exp $ */ 2 3 /*- 4 * Copyright (c) 2010 Department of Software Engineering, 5 * University of Szeged, Hungary 6 * Copyright (C) 2010 David Tengeri <dtengeri@inf.u-szeged.hu> 7 * Copyright (C) 2010 Tamas Toth <ttoth@inf.u-szeged.hu> 8 * Copyright (C) 2010 Adam Hoka <ahoka@NetBSD.org> 9 * All rights reserved. 10 * 11 * This code is derived from software contributed to The NetBSD Foundation 12 * by the Department of Software Engineering, University of Szeged, Hungary 13 * 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions 16 * are met: 17 * 1. Redistributions of source code must retain the above copyright 18 * notice, this list of conditions and the following disclaimer. 19 * 2. Redistributions in binary form must reproduce the above copyright 20 * notice, this list of conditions and the following disclaimer in the 21 * documentation and/or other materials provided with the distribution. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 30 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 31 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 /* 37 * chfs_write.c 38 * 39 * Created on: 2010.02.17. 40 * Author: dtengeri 41 */ 42 43 #include <sys/param.h> 44 #include <sys/buf.h> 45 46 #include "chfs.h" 47 48 int 49 chfs_write_flash_vnode(struct chfs_mount *chmp, 50 struct chfs_inode *ip, int prio) 51 { 52 KASSERT(mutex_owned(&chmp->chm_lock_mountfields)); 53 54 struct chfs_flash_vnode *fvnode; 55 struct chfs_vnode_cache* chvc; 56 struct chfs_node_ref *nref; 57 struct iovec vec; 58 size_t size, retlen; 59 int err = 0, retries = 0; 60 61 if (ip->ino == CHFS_ROOTINO) 62 return 0; 63 64 fvnode = chfs_alloc_flash_vnode(); 65 if (!fvnode) 66 return ENOMEM; 67 68 chvc = ip->chvc; 69 70 /* setting up flash_vnode members */ 71 size = sizeof(*fvnode); 72 //dbg("size: %zu | PADDED: %zu\n", size, CHFS_PAD(size)); 73 fvnode->magic = htole16(CHFS_FS_MAGIC_BITMASK); 74 fvnode->type = htole16(CHFS_NODETYPE_VNODE); 75 fvnode->length = htole32(CHFS_PAD(size)); 76 fvnode->hdr_crc = htole32(crc32(0, (uint8_t *)fvnode, 77 CHFS_NODE_HDR_SIZE - 4)); 78 fvnode->vno = htole64(ip->ino); 79 fvnode->version = htole64(++ip->chvc->highest_version); 80 fvnode->mode = htole32(ip->mode); 81 fvnode->dn_size = htole32(ip->size); 82 fvnode->atime = htole32(ip->atime); 83 fvnode->ctime = htole32(ip->ctime); 84 fvnode->mtime = htole32(ip->mtime); 85 fvnode->gid = htole32(ip->gid); 86 fvnode->uid = htole32(ip->uid); 87 fvnode->node_crc = htole32(crc32(0, (uint8_t *)fvnode, size - 4)); 88 89 /* write out flash_vnode */ 90 retry: 91 if (prio == ALLOC_GC) { 92 /* the GC calls this function */ 93 err = chfs_reserve_space_gc(chmp, CHFS_PAD(size)); 94 if (err) 95 goto out; 96 } else { 97 chfs_gc_trigger(chmp); 98 if (prio == ALLOC_NORMAL) 99 err = chfs_reserve_space_normal(chmp, 100 CHFS_PAD(size), ALLOC_NORMAL); 101 else 102 err = chfs_reserve_space_normal(chmp, 103 CHFS_PAD(size), ALLOC_DELETION); 104 if (err) 105 goto out; 106 } 107 108 nref = chfs_alloc_node_ref(chmp->chm_nextblock); 109 if (!nref) { 110 err = ENOMEM; 111 goto out; 112 } 113 114 mutex_enter(&chmp->chm_lock_sizes); 115 116 nref->nref_offset = chmp->chm_ebh->eb_size - chmp->chm_nextblock->free_size; 117 chfs_change_size_free(chmp, chmp->chm_nextblock, -CHFS_PAD(size)); 118 vec.iov_base = fvnode; 119 vec.iov_len = CHFS_PAD(size); 120 err = chfs_write_wbuf(chmp, &vec, 1, nref->nref_offset, &retlen); 121 if (err || retlen != CHFS_PAD(size)) { 122 chfs_err("error while writing out flash vnode to the media\n"); 123 chfs_err("err: %d | size: %zu | retlen : %zu\n", 124 err, CHFS_PAD(size), retlen); 125 chfs_change_size_dirty(chmp, 126 chmp->chm_nextblock, CHFS_PAD(size)); 127 if (retries) { 128 err = EIO; 129 mutex_exit(&chmp->chm_lock_sizes); 130 goto out; 131 } 132 133 retries++; 134 mutex_exit(&chmp->chm_lock_sizes); 135 goto retry; 136 } 137 //Everything went well 138 chfs_change_size_used(chmp, 139 &chmp->chm_blocks[nref->nref_lnr], CHFS_PAD(size)); 140 mutex_exit(&chmp->chm_lock_sizes); 141 142 mutex_enter(&chmp->chm_lock_vnocache); 143 chfs_add_vnode_ref_to_vc(chmp, chvc, nref); 144 mutex_exit(&chmp->chm_lock_vnocache); 145 KASSERT(chmp->chm_blocks[nref->nref_lnr].used_size <= chmp->chm_ebh->eb_size); 146 out: 147 chfs_free_flash_vnode(fvnode); 148 return err; 149 } 150 151 int 152 chfs_write_flash_dirent(struct chfs_mount *chmp, struct chfs_inode *pdir, 153 struct chfs_inode *ip, struct chfs_dirent *fd, 154 ino_t ino, int prio) 155 { 156 KASSERT(mutex_owned(&chmp->chm_lock_mountfields)); 157 158 struct chfs_flash_dirent_node *fdirent; 159 struct chfs_node_ref *nref; 160 struct iovec vec[2]; 161 size_t size, retlen; 162 int err = 0, retries = 0; 163 uint8_t *name; 164 size_t namelen; 165 166 KASSERT(fd->vno != CHFS_ROOTINO); 167 168 fdirent = chfs_alloc_flash_dirent(); 169 if (!fdirent) 170 return ENOMEM; 171 172 size = sizeof(*fdirent) + fd->nsize; 173 namelen = CHFS_PAD(size) - sizeof(*fdirent); 174 175 name = kmem_zalloc(namelen, KM_SLEEP); 176 memcpy(name, fd->name, fd->nsize); 177 //dbg("namelen: %zu | nsize: %hhu\n", namelen, fd->nsize); 178 179 180 //dbg("size: %zu | PADDED: %zu\n", size, CHFS_PAD(size)); 181 fdirent->magic = htole16(CHFS_FS_MAGIC_BITMASK); 182 fdirent->type = htole16(CHFS_NODETYPE_DIRENT); 183 fdirent->length = htole32(CHFS_PAD(size)); 184 fdirent->hdr_crc = htole32(crc32(0, (uint8_t *)fdirent, 185 CHFS_NODE_HDR_SIZE - 4)); 186 fdirent->vno = htole64(ino); 187 fdirent->pvno = htole64(pdir->ino); 188 fdirent->version = htole64(++pdir->chvc->highest_version); 189 fdirent->mctime = ip?ip->ctime:0; 190 fdirent->nsize = fd->nsize; 191 fdirent->dtype = fd->type; 192 fdirent->name_crc = crc32(0, (uint8_t *)&(fd->name), fd->nsize); 193 fdirent->node_crc = crc32(0, (uint8_t *)fdirent, sizeof(*fdirent) - 4); 194 195 vec[0].iov_base = fdirent; 196 vec[0].iov_len = sizeof(*fdirent); 197 vec[1].iov_base = name; 198 vec[1].iov_len = namelen; 199 200 retry: 201 if (prio == ALLOC_GC) { 202 /* the GC calls this function */ 203 err = chfs_reserve_space_gc(chmp, CHFS_PAD(size)); 204 if (err) 205 goto out; 206 } else { 207 chfs_gc_trigger(chmp); 208 if (prio == ALLOC_NORMAL) 209 err = chfs_reserve_space_normal(chmp, 210 CHFS_PAD(size), ALLOC_NORMAL); 211 else 212 err = chfs_reserve_space_normal(chmp, 213 CHFS_PAD(size), ALLOC_DELETION); 214 if (err) 215 goto out; 216 } 217 218 nref = chfs_alloc_node_ref(chmp->chm_nextblock); 219 if (!nref) { 220 err = ENOMEM; 221 goto out; 222 } 223 224 mutex_enter(&chmp->chm_lock_sizes); 225 226 nref->nref_offset = chmp->chm_ebh->eb_size - chmp->chm_nextblock->free_size; 227 chfs_change_size_free(chmp, chmp->chm_nextblock, -CHFS_PAD(size)); 228 229 err = chfs_write_wbuf(chmp, vec, 2, nref->nref_offset, &retlen); 230 if (err || retlen != CHFS_PAD(size)) { 231 chfs_err("error while writing out flash dirent node to the media\n"); 232 chfs_err("err: %d | size: %zu | retlen : %zu\n", 233 err, CHFS_PAD(size), retlen); 234 chfs_change_size_dirty(chmp, 235 chmp->chm_nextblock, CHFS_PAD(size)); 236 if (retries) { 237 err = EIO; 238 mutex_exit(&chmp->chm_lock_sizes); 239 goto out; 240 } 241 242 retries++; 243 mutex_exit(&chmp->chm_lock_sizes); 244 goto retry; 245 } 246 247 248 // Everything went well 249 chfs_change_size_used(chmp, 250 &chmp->chm_blocks[nref->nref_lnr], CHFS_PAD(size)); 251 mutex_exit(&chmp->chm_lock_sizes); 252 KASSERT(chmp->chm_blocks[nref->nref_lnr].used_size <= chmp->chm_ebh->eb_size); 253 254 fd->nref = nref; 255 if (prio != ALLOC_DELETION) { 256 mutex_enter(&chmp->chm_lock_vnocache); 257 chfs_add_node_to_list(chmp, 258 pdir->chvc, nref, &pdir->chvc->dirents); 259 mutex_exit(&chmp->chm_lock_vnocache); 260 } 261 out: 262 chfs_free_flash_dirent(fdirent); 263 return err; 264 } 265 266 /** 267 * chfs_write_flash_dnode - write out a data node to flash 268 * @chmp: chfs mount structure 269 * @vp: vnode where the data belongs to 270 * @bp: buffer contains data 271 */ 272 int 273 chfs_write_flash_dnode(struct chfs_mount *chmp, struct vnode *vp, 274 struct buf *bp, struct chfs_full_dnode *fd) 275 { 276 KASSERT(mutex_owned(&chmp->chm_lock_mountfields)); 277 278 int err = 0, retries = 0; 279 size_t size, retlen; 280 off_t ofs; 281 struct chfs_flash_data_node *dnode; 282 struct chfs_node_ref *nref; 283 struct chfs_inode *ip = VTOI(vp); 284 struct iovec vec[2]; 285 uint32_t len; 286 void *tmpbuf = NULL; 287 288 KASSERT(ip->ino != CHFS_ROOTINO); 289 290 dnode = chfs_alloc_flash_dnode(); 291 if (!dnode) 292 return ENOMEM; 293 294 /* initialize flash data node */ 295 ofs = bp->b_blkno * PAGE_SIZE; 296 //dbg("vp->v_size: %ju, bp->b_blkno: %ju, bp-b_data: %p," 297 // " bp->b_resid: %ju\n", 298 // (uintmax_t )vp->v_size, (uintmax_t )bp->b_blkno, 299 // bp->b_data, (uintmax_t )bp->b_resid); 300 //dbg("[XXX]vp->v_size - ofs: %llu\n", (vp->v_size - ofs)); 301 len = MIN((vp->v_size - ofs), bp->b_resid); 302 size = sizeof(*dnode) + len; 303 304 dnode->magic = htole16(CHFS_FS_MAGIC_BITMASK); 305 dnode->type = htole16(CHFS_NODETYPE_DATA); 306 dnode->length = htole32(CHFS_PAD(size)); 307 dnode->hdr_crc = htole32(crc32(0, (uint8_t *)dnode, 308 CHFS_NODE_HDR_SIZE - 4)); 309 dnode->vno = htole64(ip->ino); 310 dnode->version = htole64(++ip->chvc->highest_version); 311 dnode->offset = htole64(ofs); 312 dnode->data_length = htole32(len); 313 dnode->data_crc = htole32(crc32(0, (uint8_t *)bp->b_data, len)); 314 dnode->node_crc = htole32(crc32(0, (uint8_t *)dnode, 315 sizeof(*dnode) - 4)); 316 317 dbg("dnode @%llu %ub v%llu\n", (unsigned long long)dnode->offset, 318 dnode->data_length, (unsigned long long)dnode->version); 319 320 if (CHFS_PAD(size) - sizeof(*dnode)) { 321 tmpbuf = kmem_zalloc(CHFS_PAD(size) 322 - sizeof(*dnode), KM_SLEEP); 323 memcpy(tmpbuf, bp->b_data, len); 324 } 325 326 /* creating iovecs for wbuf */ 327 vec[0].iov_base = dnode; 328 vec[0].iov_len = sizeof(*dnode); 329 vec[1].iov_base = tmpbuf; 330 vec[1].iov_len = CHFS_PAD(size) - sizeof(*dnode); 331 332 fd->ofs = ofs; 333 fd->size = len; 334 335 retry: 336 337 /* Reserve space for data node. This will set up the next eraseblock 338 * where to we will write. 339 */ 340 341 chfs_gc_trigger(chmp); 342 err = chfs_reserve_space_normal(chmp, 343 CHFS_PAD(size), ALLOC_NORMAL); 344 if (err) 345 goto out; 346 347 nref = chfs_alloc_node_ref(chmp->chm_nextblock); 348 if (!nref) { 349 err = ENOMEM; 350 goto out; 351 } 352 353 nref->nref_offset = 354 chmp->chm_ebh->eb_size - chmp->chm_nextblock->free_size; 355 356 KASSERT(nref->nref_offset < chmp->chm_ebh->eb_size); 357 358 mutex_enter(&chmp->chm_lock_sizes); 359 360 chfs_change_size_free(chmp, 361 chmp->chm_nextblock, -CHFS_PAD(size)); 362 363 //dbg("vno: %llu nref lnr: %u offset: %u\n", 364 // dnode->vno, nref->nref_lnr, nref->nref_offset); 365 366 err = chfs_write_wbuf(chmp, vec, 2, nref->nref_offset, &retlen); 367 if (err || retlen != CHFS_PAD(size)) { 368 chfs_err("error while writing out flash data node to the media\n"); 369 chfs_err("err: %d | size: %zu | retlen : %zu\n", 370 err, size, retlen); 371 chfs_change_size_dirty(chmp, 372 chmp->chm_nextblock, CHFS_PAD(size)); 373 if (retries) { 374 err = EIO; 375 mutex_exit(&chmp->chm_lock_sizes); 376 goto out; 377 } 378 379 retries++; 380 mutex_exit(&chmp->chm_lock_sizes); 381 goto retry; 382 } 383 /* Everything went well */ 384 ip->write_size += fd->size; 385 chfs_change_size_used(chmp, 386 &chmp->chm_blocks[nref->nref_lnr], CHFS_PAD(size)); 387 mutex_exit(&chmp->chm_lock_sizes); 388 389 mutex_enter(&chmp->chm_lock_vnocache); 390 if (fd->nref != NULL) { 391 chfs_remove_frags_of_node(chmp, &ip->fragtree, fd->nref); 392 chfs_remove_and_obsolete(chmp, ip->chvc, fd->nref, &ip->chvc->dnode); 393 } 394 395 KASSERT(chmp->chm_blocks[nref->nref_lnr].used_size <= chmp->chm_ebh->eb_size); 396 fd->nref = nref; 397 chfs_add_node_to_list(chmp, ip->chvc, nref, &ip->chvc->dnode); 398 mutex_exit(&chmp->chm_lock_vnocache); 399 out: 400 chfs_free_flash_dnode(dnode); 401 if (CHFS_PAD(size) - sizeof(*dnode)) { 402 kmem_free(tmpbuf, CHFS_PAD(size) - sizeof(*dnode)); 403 } 404 405 return err; 406 } 407 408 /** 409 * chfs_do_link - makes a copy from a node 410 * @old: old node 411 * @oldfd: dirent of old node 412 * @parent: parent of new node 413 * @name: name of new node 414 * @namelen: length of name 415 * This function writes the dirent of the new node to the media. 416 */ 417 int 418 chfs_do_link(struct chfs_inode *ip, struct chfs_inode *parent, const char *name, int namelen, enum chtype type) 419 { 420 int error = 0; 421 struct vnode *vp = ITOV(ip); 422 struct ufsmount *ump = VFSTOUFS(vp->v_mount); 423 struct chfs_mount *chmp = ump->um_chfs; 424 struct chfs_dirent *newfd = NULL; 425 // struct chfs_dirent *fd = NULL; 426 427 //dbg("link vno: %llu\n", ip->ino); 428 429 newfd = chfs_alloc_dirent(namelen + 1); 430 431 newfd->vno = ip->ino; 432 newfd->type = type; 433 newfd->nsize = namelen; 434 memcpy(newfd->name, name, namelen); 435 newfd->name[newfd->nsize] = 0; 436 // newfd->next = NULL; 437 438 ip->chvc->nlink++; 439 parent->chvc->nlink++; 440 ip->iflag |= IN_CHANGE; 441 chfs_update(vp, NULL, NULL, UPDATE_WAIT); 442 443 mutex_enter(&chmp->chm_lock_mountfields); 444 445 error = chfs_write_flash_vnode(chmp, ip, ALLOC_NORMAL); 446 if (error) 447 return error; 448 449 error = chfs_write_flash_dirent(chmp, 450 parent, ip, newfd, ip->ino, ALLOC_NORMAL); 451 /* TODO: what should we do if error isn't zero? */ 452 453 mutex_exit(&chmp->chm_lock_mountfields); 454 455 /* add fd to the fd list */ 456 TAILQ_INSERT_TAIL(&parent->dents, newfd, fds); 457 #if 0 458 fd = parent->dents; 459 if (!fd) { 460 parent->dents = newfd; 461 } else { 462 while (fd->next) 463 fd = fd->next; 464 fd->next = newfd; 465 } 466 #endif 467 468 return error; 469 } 470 471 472 /** 473 * chfs_do_unlink - delete a node 474 * @ip: node what we'd like to delete 475 * @parent: parent of the node 476 * @name: name of the node 477 * @namelen: length of name 478 * This function set the nlink and vno of the node zero and write its dirent to the media. 479 */ 480 int 481 chfs_do_unlink(struct chfs_inode *ip, 482 struct chfs_inode *parent, const char *name, int namelen) 483 { 484 struct chfs_dirent *fd, *tmpfd; 485 int error = 0; 486 struct vnode *vp = ITOV(ip); 487 struct ufsmount *ump = VFSTOUFS(vp->v_mount); 488 struct chfs_mount *chmp = ump->um_chfs; 489 struct chfs_node_ref *nref; 490 491 //dbg("unlink vno: %llu\n", ip->ino); 492 493 vflushbuf(vp, 0); 494 495 mutex_enter(&chmp->chm_lock_mountfields); 496 497 /* remove the full direntry from the parent dents list */ 498 TAILQ_FOREACH_SAFE(fd, &parent->dents, fds, tmpfd) { 499 if (fd->vno == ip->ino && 500 fd->nsize == namelen && 501 !memcmp(fd->name, name, fd->nsize)) { 502 503 chfs_kill_fragtree(chmp, &ip->fragtree); 504 505 if (fd->type == CHT_DIR && ip->chvc->nlink == 2) 506 ip->chvc->nlink = 0; 507 else 508 ip->chvc->nlink--; 509 510 fd->type = CHT_BLANK; 511 512 TAILQ_REMOVE(&parent->dents, fd, fds); 513 514 mutex_enter(&chmp->chm_lock_vnocache); 515 516 dbg("FD->NREF vno: %llu, lnr: %u, ofs: %u\n", 517 fd->vno, fd->nref->nref_lnr, fd->nref->nref_offset); 518 chfs_remove_and_obsolete(chmp, parent->chvc, fd->nref, 519 &parent->chvc->dirents); 520 521 error = chfs_write_flash_dirent(chmp, 522 parent, ip, fd, 0, ALLOC_DELETION); 523 524 dbg("FD->NREF vno: %llu, lnr: %u, ofs: %u\n", 525 fd->vno, fd->nref->nref_lnr, fd->nref->nref_offset); 526 // set nref_next field 527 chfs_add_node_to_list(chmp, parent->chvc, fd->nref, 528 &parent->chvc->dirents); 529 // remove from the list 530 chfs_remove_and_obsolete(chmp, parent->chvc, fd->nref, 531 &parent->chvc->dirents); 532 533 // clean dnode list 534 while (ip->chvc->dnode != (struct chfs_node_ref *)ip->chvc) { 535 nref = ip->chvc->dnode; 536 chfs_remove_frags_of_node(chmp, &ip->fragtree, nref); 537 chfs_remove_and_obsolete(chmp, ip->chvc, nref, &ip->chvc->dnode); 538 } 539 540 // clean v list 541 while (ip->chvc->v != (struct chfs_node_ref *)ip->chvc) { 542 nref = ip->chvc->v; 543 chfs_remove_and_obsolete(chmp, ip->chvc, nref, &ip->chvc->v); 544 } 545 546 parent->chvc->nlink--; 547 548 mutex_exit(&chmp->chm_lock_vnocache); 549 //TODO: if error 550 } 551 } 552 mutex_exit(&chmp->chm_lock_mountfields); 553 554 return error; 555 } 556