1 /* $NetBSD: chfs_write.c,v 1.5 2012/10/19 12:44:39 ttoth Exp $ */
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>
11 * This code is derived from software contributed to The NetBSD Foundation
12 * by the Department of Software Engineering, University of Szeged, Hungary
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
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.
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
37 #include <sys/param.h>
43 /* chfs_write_flash_vnode - writes out a vnode information to flash */
45 chfs_write_flash_vnode(struct chfs_mount
*chmp
,
46 struct chfs_inode
*ip
, int prio
)
48 KASSERT(mutex_owned(&chmp
->chm_lock_mountfields
));
50 struct chfs_flash_vnode
*fvnode
;
51 struct chfs_vnode_cache
* chvc
;
52 struct chfs_node_ref
*nref
;
55 int err
= 0, retries
= 0;
57 /* root vnode is in-memory only */
58 if (ip
->ino
== CHFS_ROOTINO
)
61 fvnode
= chfs_alloc_flash_vnode();
67 /* setting up flash_vnode's fields */
68 size
= sizeof(*fvnode
);
69 fvnode
->magic
= htole16(CHFS_FS_MAGIC_BITMASK
);
70 fvnode
->type
= htole16(CHFS_NODETYPE_VNODE
);
71 fvnode
->length
= htole32(CHFS_PAD(size
));
72 fvnode
->hdr_crc
= htole32(crc32(0, (uint8_t *)fvnode
,
73 CHFS_NODE_HDR_SIZE
- 4));
74 fvnode
->vno
= htole64(ip
->ino
);
75 fvnode
->version
= htole64(++ip
->chvc
->highest_version
);
76 fvnode
->mode
= htole32(ip
->mode
);
77 fvnode
->dn_size
= htole32(ip
->size
);
78 fvnode
->atime
= htole32(ip
->atime
);
79 fvnode
->ctime
= htole32(ip
->ctime
);
80 fvnode
->mtime
= htole32(ip
->mtime
);
81 fvnode
->gid
= htole32(ip
->gid
);
82 fvnode
->uid
= htole32(ip
->uid
);
83 fvnode
->node_crc
= htole32(crc32(0, (uint8_t *)fvnode
, size
- 4));
86 /* setting up the next eraseblock where we will write */
87 if (prio
== ALLOC_GC
) {
88 /* GC called this function */
89 err
= chfs_reserve_space_gc(chmp
, CHFS_PAD(size
));
93 chfs_gc_trigger(chmp
);
94 if (prio
== ALLOC_NORMAL
)
95 err
= chfs_reserve_space_normal(chmp
,
96 CHFS_PAD(size
), ALLOC_NORMAL
);
98 err
= chfs_reserve_space_normal(chmp
,
99 CHFS_PAD(size
), ALLOC_DELETION
);
104 /* allocating a new node reference */
105 nref
= chfs_alloc_node_ref(chmp
->chm_nextblock
);
111 mutex_enter(&chmp
->chm_lock_sizes
);
113 /* caculating offset and sizes */
114 nref
->nref_offset
= chmp
->chm_ebh
->eb_size
- chmp
->chm_nextblock
->free_size
;
115 chfs_change_size_free(chmp
, chmp
->chm_nextblock
, -CHFS_PAD(size
));
116 vec
.iov_base
= fvnode
;
117 vec
.iov_len
= CHFS_PAD(size
);
119 /* write it into the writebuffer */
120 err
= chfs_write_wbuf(chmp
, &vec
, 1, nref
->nref_offset
, &retlen
);
121 if (err
|| retlen
!= CHFS_PAD(size
)) {
122 /* there was an error during write */
123 chfs_err("error while writing out flash vnode to the media\n");
124 chfs_err("err: %d | size: %zu | retlen : %zu\n",
125 err
, CHFS_PAD(size
), retlen
);
126 chfs_change_size_dirty(chmp
,
127 chmp
->chm_nextblock
, CHFS_PAD(size
));
130 mutex_exit(&chmp
->chm_lock_sizes
);
136 mutex_exit(&chmp
->chm_lock_sizes
);
140 /* everything went well */
141 chfs_change_size_used(chmp
,
142 &chmp
->chm_blocks
[nref
->nref_lnr
], CHFS_PAD(size
));
143 mutex_exit(&chmp
->chm_lock_sizes
);
145 /* add the new nref to vnode cache */
146 mutex_enter(&chmp
->chm_lock_vnocache
);
147 chfs_add_vnode_ref_to_vc(chmp
, chvc
, nref
);
148 mutex_exit(&chmp
->chm_lock_vnocache
);
149 KASSERT(chmp
->chm_blocks
[nref
->nref_lnr
].used_size
<= chmp
->chm_ebh
->eb_size
);
151 chfs_free_flash_vnode(fvnode
);
155 /* chfs_write_flash_dirent - writes out a directory entry to flash */
157 chfs_write_flash_dirent(struct chfs_mount
*chmp
, struct chfs_inode
*pdir
,
158 struct chfs_inode
*ip
, struct chfs_dirent
*fd
,
161 KASSERT(mutex_owned(&chmp
->chm_lock_mountfields
));
163 struct chfs_flash_dirent_node
*fdirent
;
164 struct chfs_node_ref
*nref
;
167 int err
= 0, retries
= 0;
171 KASSERT(fd
->vno
!= CHFS_ROOTINO
);
173 /* setting up flash_dirent's fields */
174 fdirent
= chfs_alloc_flash_dirent();
178 size
= sizeof(*fdirent
) + fd
->nsize
;
179 namelen
= CHFS_PAD(size
) - sizeof(*fdirent
);
181 name
= kmem_zalloc(namelen
, KM_SLEEP
);
182 memcpy(name
, fd
->name
, fd
->nsize
);
184 fdirent
->magic
= htole16(CHFS_FS_MAGIC_BITMASK
);
185 fdirent
->type
= htole16(CHFS_NODETYPE_DIRENT
);
186 fdirent
->length
= htole32(CHFS_PAD(size
));
187 fdirent
->hdr_crc
= htole32(crc32(0, (uint8_t *)fdirent
,
188 CHFS_NODE_HDR_SIZE
- 4));
189 fdirent
->vno
= htole64(ino
);
190 fdirent
->pvno
= htole64(pdir
->ino
);
191 fdirent
->version
= htole64(++pdir
->chvc
->highest_version
);
192 fdirent
->mctime
= ip
?ip
->ctime
:0;
193 fdirent
->nsize
= fd
->nsize
;
194 fdirent
->dtype
= fd
->type
;
195 fdirent
->name_crc
= crc32(0, (uint8_t *)&(fd
->name
), fd
->nsize
);
196 fdirent
->node_crc
= crc32(0, (uint8_t *)fdirent
, sizeof(*fdirent
) - 4);
198 /* directory's name is written out right after the dirent */
199 vec
[0].iov_base
= fdirent
;
200 vec
[0].iov_len
= sizeof(*fdirent
);
201 vec
[1].iov_base
= name
;
202 vec
[1].iov_len
= namelen
;
205 /* setting up the next eraseblock where we will write */
206 if (prio
== ALLOC_GC
) {
207 /* the GC calls this function */
208 err
= chfs_reserve_space_gc(chmp
, CHFS_PAD(size
));
212 chfs_gc_trigger(chmp
);
213 if (prio
== ALLOC_NORMAL
)
214 err
= chfs_reserve_space_normal(chmp
,
215 CHFS_PAD(size
), ALLOC_NORMAL
);
217 err
= chfs_reserve_space_normal(chmp
,
218 CHFS_PAD(size
), ALLOC_DELETION
);
223 /* allocating a new node reference */
224 nref
= chfs_alloc_node_ref(chmp
->chm_nextblock
);
230 mutex_enter(&chmp
->chm_lock_sizes
);
232 nref
->nref_offset
= chmp
->chm_ebh
->eb_size
- chmp
->chm_nextblock
->free_size
;
233 chfs_change_size_free(chmp
, chmp
->chm_nextblock
, -CHFS_PAD(size
));
235 /* write it into the writebuffer */
236 err
= chfs_write_wbuf(chmp
, vec
, 2, nref
->nref_offset
, &retlen
);
237 if (err
|| retlen
!= CHFS_PAD(size
)) {
238 /* there was an error during write */
239 chfs_err("error while writing out flash dirent node to the media\n");
240 chfs_err("err: %d | size: %zu | retlen : %zu\n",
241 err
, CHFS_PAD(size
), retlen
);
242 chfs_change_size_dirty(chmp
,
243 chmp
->chm_nextblock
, CHFS_PAD(size
));
246 mutex_exit(&chmp
->chm_lock_sizes
);
252 mutex_exit(&chmp
->chm_lock_sizes
);
257 /* everything went well */
258 chfs_change_size_used(chmp
,
259 &chmp
->chm_blocks
[nref
->nref_lnr
], CHFS_PAD(size
));
260 mutex_exit(&chmp
->chm_lock_sizes
);
261 KASSERT(chmp
->chm_blocks
[nref
->nref_lnr
].used_size
<= chmp
->chm_ebh
->eb_size
);
263 /* add the new nref to the directory chain of vnode cache */
265 if (prio
!= ALLOC_DELETION
) {
266 mutex_enter(&chmp
->chm_lock_vnocache
);
267 chfs_add_node_to_list(chmp
,
268 pdir
->chvc
, nref
, &pdir
->chvc
->dirents
);
269 mutex_exit(&chmp
->chm_lock_vnocache
);
272 chfs_free_flash_dirent(fdirent
);
276 /* chfs_write_flash_dnode - writes out a data node to flash */
278 chfs_write_flash_dnode(struct chfs_mount
*chmp
, struct vnode
*vp
,
279 struct buf
*bp
, struct chfs_full_dnode
*fd
)
281 KASSERT(mutex_owned(&chmp
->chm_lock_mountfields
));
283 int err
= 0, retries
= 0;
286 struct chfs_flash_data_node
*dnode
;
287 struct chfs_node_ref
*nref
;
288 struct chfs_inode
*ip
= VTOI(vp
);
293 KASSERT(ip
->ino
!= CHFS_ROOTINO
);
295 dnode
= chfs_alloc_flash_dnode();
299 /* initialize flash data node */
300 ofs
= bp
->b_blkno
* PAGE_SIZE
;
301 len
= MIN((vp
->v_size
- ofs
), bp
->b_resid
);
302 size
= sizeof(*dnode
) + len
;
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));
317 dbg("dnode @%llu %ub v%llu\n", (unsigned long long)dnode
->offset
,
318 dnode
->data_length
, (unsigned long long)dnode
->version
);
320 /* pad data if needed */
321 if (CHFS_PAD(size
) - sizeof(*dnode
)) {
322 tmpbuf
= kmem_zalloc(CHFS_PAD(size
)
323 - sizeof(*dnode
), KM_SLEEP
);
324 memcpy(tmpbuf
, bp
->b_data
, len
);
327 /* creating iovecs for writebuffer
328 * data is written out right after the data node */
329 vec
[0].iov_base
= dnode
;
330 vec
[0].iov_len
= sizeof(*dnode
);
331 vec
[1].iov_base
= tmpbuf
;
332 vec
[1].iov_len
= CHFS_PAD(size
) - sizeof(*dnode
);
338 /* Reserve space for data node. This will set up the next eraseblock
339 * where to we will write.
341 chfs_gc_trigger(chmp
);
342 err
= chfs_reserve_space_normal(chmp
,
343 CHFS_PAD(size
), ALLOC_NORMAL
);
347 /* allocating a new node reference */
348 nref
= chfs_alloc_node_ref(chmp
->chm_nextblock
);
355 chmp
->chm_ebh
->eb_size
- chmp
->chm_nextblock
->free_size
;
357 KASSERT(nref
->nref_offset
< chmp
->chm_ebh
->eb_size
);
359 mutex_enter(&chmp
->chm_lock_sizes
);
361 chfs_change_size_free(chmp
,
362 chmp
->chm_nextblock
, -CHFS_PAD(size
));
364 /* write it into the writebuffer */
365 err
= chfs_write_wbuf(chmp
, vec
, 2, nref
->nref_offset
, &retlen
);
366 if (err
|| retlen
!= CHFS_PAD(size
)) {
367 /* there was an error during write */
368 chfs_err("error while writing out flash data node to the media\n");
369 chfs_err("err: %d | size: %zu | retlen : %zu\n",
371 chfs_change_size_dirty(chmp
,
372 chmp
->chm_nextblock
, CHFS_PAD(size
));
375 mutex_exit(&chmp
->chm_lock_sizes
);
381 mutex_exit(&chmp
->chm_lock_sizes
);
384 /* everything went well */
385 ip
->write_size
+= fd
->size
;
386 chfs_change_size_used(chmp
,
387 &chmp
->chm_blocks
[nref
->nref_lnr
], CHFS_PAD(size
));
388 mutex_exit(&chmp
->chm_lock_sizes
);
390 mutex_enter(&chmp
->chm_lock_vnocache
);
391 if (fd
->nref
!= NULL
) {
392 chfs_remove_frags_of_node(chmp
, &ip
->fragtree
, fd
->nref
);
393 chfs_remove_and_obsolete(chmp
, ip
->chvc
, fd
->nref
, &ip
->chvc
->dnode
);
396 /* add the new nref to the data node chain of vnode cache */
397 KASSERT(chmp
->chm_blocks
[nref
->nref_lnr
].used_size
<= chmp
->chm_ebh
->eb_size
);
399 chfs_add_node_to_list(chmp
, ip
->chvc
, nref
, &ip
->chvc
->dnode
);
400 mutex_exit(&chmp
->chm_lock_vnocache
);
402 chfs_free_flash_dnode(dnode
);
403 if (CHFS_PAD(size
) - sizeof(*dnode
)) {
404 kmem_free(tmpbuf
, CHFS_PAD(size
) - sizeof(*dnode
));
411 * chfs_do_link - makes a copy from a node
412 * This function writes the dirent of the new node to the media.
415 chfs_do_link(struct chfs_inode
*ip
, struct chfs_inode
*parent
, const char *name
, int namelen
, enum chtype type
)
418 struct vnode
*vp
= ITOV(ip
);
419 struct ufsmount
*ump
= VFSTOUFS(vp
->v_mount
);
420 struct chfs_mount
*chmp
= ump
->um_chfs
;
421 struct chfs_dirent
*newfd
= NULL
;
423 /* setting up the new directory entry */
424 newfd
= chfs_alloc_dirent(namelen
+ 1);
426 newfd
->vno
= ip
->ino
;
428 newfd
->nsize
= namelen
;
429 memcpy(newfd
->name
, name
, namelen
);
430 newfd
->name
[newfd
->nsize
] = 0;
433 parent
->chvc
->nlink
++;
434 ip
->iflag
|= IN_CHANGE
;
435 chfs_update(vp
, NULL
, NULL
, UPDATE_WAIT
);
437 mutex_enter(&chmp
->chm_lock_mountfields
);
439 /* update vnode information */
440 error
= chfs_write_flash_vnode(chmp
, ip
, ALLOC_NORMAL
);
444 /* write out the new dirent */
445 error
= chfs_write_flash_dirent(chmp
,
446 parent
, ip
, newfd
, ip
->ino
, ALLOC_NORMAL
);
447 /* TODO: what should we do if error isn't zero? */
449 mutex_exit(&chmp
->chm_lock_mountfields
);
451 /* add fd to the fd list */
452 TAILQ_INSERT_TAIL(&parent
->dents
, newfd
, fds
);
459 * chfs_do_unlink - delete a node
460 * This function set the nlink and vno of the node to zero and
461 * write its dirent to the media.
464 chfs_do_unlink(struct chfs_inode
*ip
,
465 struct chfs_inode
*parent
, const char *name
, int namelen
)
467 struct chfs_dirent
*fd
, *tmpfd
;
469 struct vnode
*vp
= ITOV(ip
);
470 struct ufsmount
*ump
= VFSTOUFS(vp
->v_mount
);
471 struct chfs_mount
*chmp
= ump
->um_chfs
;
472 struct chfs_node_ref
*nref
;
476 mutex_enter(&chmp
->chm_lock_mountfields
);
478 /* remove the full direntry from the parent dents list */
479 TAILQ_FOREACH_SAFE(fd
, &parent
->dents
, fds
, tmpfd
) {
480 if (fd
->vno
== ip
->ino
&&
481 fd
->nsize
== namelen
&&
482 !memcmp(fd
->name
, name
, fd
->nsize
)) {
484 /* remove every fragment of the file */
485 chfs_kill_fragtree(chmp
, &ip
->fragtree
);
487 /* decrease number of links to the file */
488 if (fd
->type
== CHT_DIR
&& ip
->chvc
->nlink
== 2)
493 fd
->type
= CHT_BLANK
;
495 /* remove from parent's directory entries */
496 TAILQ_REMOVE(&parent
->dents
, fd
, fds
);
498 mutex_enter(&chmp
->chm_lock_vnocache
);
500 dbg("FD->NREF vno: %llu, lnr: %u, ofs: %u\n",
501 fd
->vno
, fd
->nref
->nref_lnr
, fd
->nref
->nref_offset
);
502 chfs_remove_and_obsolete(chmp
, parent
->chvc
, fd
->nref
,
503 &parent
->chvc
->dirents
);
505 error
= chfs_write_flash_dirent(chmp
,
506 parent
, ip
, fd
, 0, ALLOC_DELETION
);
508 dbg("FD->NREF vno: %llu, lnr: %u, ofs: %u\n",
509 fd
->vno
, fd
->nref
->nref_lnr
, fd
->nref
->nref_offset
);
510 /* set nref_next field */
511 chfs_add_node_to_list(chmp
, parent
->chvc
, fd
->nref
,
512 &parent
->chvc
->dirents
);
513 /* remove from the list */
514 chfs_remove_and_obsolete(chmp
, parent
->chvc
, fd
->nref
,
515 &parent
->chvc
->dirents
);
517 /* clean dnode list */
518 while (ip
->chvc
->dnode
!= (struct chfs_node_ref
*)ip
->chvc
) {
519 nref
= ip
->chvc
->dnode
;
520 chfs_remove_frags_of_node(chmp
, &ip
->fragtree
, nref
);
521 chfs_remove_and_obsolete(chmp
, ip
->chvc
, nref
, &ip
->chvc
->dnode
);
524 /* clean vnode information (list) */
525 while (ip
->chvc
->v
!= (struct chfs_node_ref
*)ip
->chvc
) {
527 chfs_remove_and_obsolete(chmp
, ip
->chvc
, nref
, &ip
->chvc
->v
);
530 /* decrease number of links to parent */
531 parent
->chvc
->nlink
--;
533 mutex_exit(&chmp
->chm_lock_vnocache
);
537 mutex_exit(&chmp
->chm_lock_mountfields
);