Import CHFS, which was formerly known as ChewieFS.
CHFS is a file system for flash devices developed by the Software Engineering Department at University of Szeged, Hungary. http://chewiefs.sed.hu/ Thanks for all who made it possible.
This commit is contained in:
parent
277dab7940
commit
288addd0db
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: vnode.h,v 1.235 2011/11/21 04:36:06 christos Exp $ */
|
||||
/* $NetBSD: vnode.h,v 1.236 2011/11/24 15:51:30 ahoka Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2008 The NetBSD Foundation, Inc.
|
||||
|
@ -105,7 +105,7 @@ enum vtagtype {
|
|||
VT_AFS, VT_ISOFS, VT_UNION, VT_ADOSFS, VT_EXT2FS, VT_CODA,
|
||||
VT_FILECORE, VT_NTFS, VT_VFS, VT_OVERLAY, VT_SMBFS, VT_PTYFS,
|
||||
VT_TMPFS, VT_UDF, VT_SYSVBFS, VT_PUFFS, VT_HFS, VT_EFS, VT_ZFS,
|
||||
VT_RUMP, VT_NILFS, VT_V7FS
|
||||
VT_RUMP, VT_NILFS, VT_V7FS, VT_CHFS
|
||||
};
|
||||
|
||||
#define VNODE_TAGS \
|
||||
|
@ -114,7 +114,7 @@ enum vtagtype {
|
|||
"VT_AFS", "VT_ISOFS", "VT_UNION", "VT_ADOSFS", "VT_EXT2FS", "VT_CODA", \
|
||||
"VT_FILECORE", "VT_NTFS", "VT_VFS", "VT_OVERLAY", "VT_SMBFS", "VT_PTYFS", \
|
||||
"VT_TMPFS", "VT_UDF", "VT_SYSVBFS", "VT_PUFFS", "VT_HFS", "VT_EFS", \
|
||||
"VT_ZFS", "VT_RUMP", "VT_NILFS", "VT_V7FS"
|
||||
"VT_ZFS", "VT_RUMP", "VT_NILFS", "VT_V7FS", "VT_CHFS"
|
||||
|
||||
struct vnode;
|
||||
struct buf;
|
||||
|
@ -223,7 +223,7 @@ typedef struct vnode vnode_t;
|
|||
"\20\1ROOT\2SYSTEM\3ISTTY\4MAPPED\5MPSAFE\6LOCKSWORK\11TEXT\12EXECMAP" \
|
||||
"\13WRMAP\14WRMAPDIRTY\15XLOCK\17ONWORKLST\20MARKER" \
|
||||
"\22LAYER\24CLEAN\25INACTPEND\26INACTREDO" \
|
||||
"\30INACTNOW\31DIROP"
|
||||
"\30INACTNOW\31DIROP"
|
||||
|
||||
#define VSIZENOTSET ((voff_t)-1)
|
||||
|
||||
|
|
|
@ -0,0 +1,910 @@
|
|||
/* $NetBSD: chfs.h,v 1.1 2011/11/24 15:51:31 ahoka Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2010 Department of Software Engineering,
|
||||
* University of Szeged, Hungary
|
||||
* Copyright (C) 2009 Ferenc Havasi <havasi@inf.u-szeged.hu>
|
||||
* Copyright (C) 2009 Zoltan Sogor <weth@inf.u-szeged.hu>
|
||||
* Copyright (C) 2009 David Tengeri <dtengeri@inf.u-szeged.hu>
|
||||
* Copyright (C) 2009 Tamas Toth <ttoth@inf.u-szeged.hu>
|
||||
* Copyright (C) 2010 Adam Hoka <ahoka@NetBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by the Department of Software Engineering, University of Szeged, Hungary
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __CHFS_H__
|
||||
#define __CHFS_H__
|
||||
|
||||
#define DBG_MSG
|
||||
#define DBG_MSG_GC
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/tree.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/kmem.h>
|
||||
#include <sys/endian.h>
|
||||
#include <sys/rwlock.h>
|
||||
#include <sys/condvar.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/kthread.h>
|
||||
#include <sys/rbtree.h>
|
||||
#include <sys/vnode.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/hash.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/dirent.h>
|
||||
|
||||
#include <ufs/ufs/quota.h>
|
||||
#include <ufs/ufs/ufsmount.h>
|
||||
#include <ufs/ufs/dir.h>
|
||||
|
||||
/* XXX shouldnt be defined here, but needed by chfs_inode.h */
|
||||
TAILQ_HEAD(chfs_dirent_list, chfs_dirent);
|
||||
|
||||
#include "chfs_pool.h"
|
||||
#include "ebh.h"
|
||||
#include "media.h"
|
||||
#include "chfs_inode.h"
|
||||
|
||||
#ifndef MOUNT_CHFS
|
||||
#define MOUNT_CHFS "chfs"
|
||||
#endif
|
||||
|
||||
#define CHFS_ROOTINO ROOTINO /* ROOTINO == 2 */
|
||||
|
||||
enum {
|
||||
VNO_STATE_UNCHECKED, /* CRC checks not yet done */
|
||||
VNO_STATE_CHECKING, /* CRC checks in progress */
|
||||
VNO_STATE_PRESENT, /* In core */
|
||||
VNO_STATE_CHECKEDABSENT,/* Checked, cleared again */
|
||||
VNO_STATE_GC, /* GCing a 'pristine' node */
|
||||
VNO_STATE_READING, /* In read_inode() */
|
||||
VNO_STATE_CLEARING /* In clear_inode() */
|
||||
};
|
||||
|
||||
#define VNODECACHE_SIZE 128
|
||||
|
||||
#define MAX_READ_FREE(chmp) (((chmp)->chm_ebh)->eb_size / 8)
|
||||
/* an eraseblock will be clean if its dirty size is smaller than this */
|
||||
#define MAX_DIRTY_TO_CLEAN 255
|
||||
#define VERY_DIRTY(chmp, size) ((size) >= (((chmp)->chm_ebh)->eb_size / 2))
|
||||
|
||||
#define CHFS_PAD(x) (((x)+3)&~3)
|
||||
|
||||
enum {
|
||||
CHFS_NODE_OK = 0,
|
||||
CHFS_NODE_BADMAGIC,
|
||||
CHFS_NODE_BADCRC,
|
||||
CHFS_NODE_BADNAMECRC
|
||||
};
|
||||
|
||||
enum {
|
||||
CHFS_BLK_STATE_FREE = 100,
|
||||
CHFS_BLK_STATE_CLEAN,
|
||||
CHFS_BLK_STATE_PARTDIRTY,
|
||||
CHFS_BLK_STATE_ALLDIRTY
|
||||
};
|
||||
|
||||
extern struct pool chfs_inode_pool;
|
||||
extern const struct genfs_ops chfs_genfsops;
|
||||
|
||||
/**
|
||||
* struct chfs_node_ref - a reference to a node
|
||||
* @lnr: logical identifier of the eraseblock where the node is
|
||||
* @offset: offset int hte eraseblock where the node starts
|
||||
* @next: used at data and dirent nodes, it points to the next data node which
|
||||
* belongs to the same vnode
|
||||
*/
|
||||
struct chfs_node_ref
|
||||
{
|
||||
struct chfs_node_ref *nref_next;
|
||||
uint32_t nref_lnr;
|
||||
uint32_t nref_offset;
|
||||
};
|
||||
|
||||
/* Constants for allocating node refs */
|
||||
#define REFS_BLOCK_LEN (255/sizeof(struct chfs_node_ref))
|
||||
#define REF_EMPTY_NODE (UINT_MAX)
|
||||
#define REF_LINK_TO_NEXT (UINT_MAX - 1)
|
||||
|
||||
enum {
|
||||
CHFS_NORMAL_NODE_MASK,
|
||||
CHFS_UNCHECKED_NODE_MASK,
|
||||
CHFS_OBSOLETE_NODE_MASK,
|
||||
CHFS_PRISTINE_NODE_MASK
|
||||
};
|
||||
|
||||
#define CHFS_REF_FLAGS(ref) ((ref)->nref_offset & 3)
|
||||
#define CHFS_REF_OBSOLETE(ref) (((ref)->nref_offset & 3) == CHFS_OBSOLETE_NODE_MASK)
|
||||
#define CHFS_MARK_REF_NORMAL(ref) \
|
||||
do { \
|
||||
(ref)->nref_offset = CHFS_GET_OFS((ref)->nref_offset) | CHFS_NORMAL_NODE_MASK; \
|
||||
} while(0)
|
||||
|
||||
#define CHFS_GET_OFS(ofs) (ofs & ~ 3)
|
||||
|
||||
static inline struct chfs_node_ref *
|
||||
node_next(struct chfs_node_ref *nref)
|
||||
{
|
||||
//dbg("node next: %u : %u\n", nref->nref_lnr, nref->nref_offset);
|
||||
nref++;
|
||||
//dbg("nref++: %u : %u\n", nref->nref_lnr, nref->nref_offset);
|
||||
|
||||
if (nref->nref_lnr == REF_LINK_TO_NEXT) {
|
||||
//dbg("link to next\n");
|
||||
nref = nref->nref_next;
|
||||
if (!nref)
|
||||
return nref;
|
||||
}
|
||||
|
||||
if (nref->nref_lnr == REF_EMPTY_NODE) {
|
||||
//dbg("empty\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return nref;
|
||||
}
|
||||
|
||||
/**
|
||||
* struct chfs_dirent - full representation of a directory entry
|
||||
*/
|
||||
struct chfs_dirent
|
||||
{
|
||||
struct chfs_node_ref *nref;
|
||||
// struct chfs_dirent *next;
|
||||
TAILQ_ENTRY(chfs_dirent) fds;
|
||||
uint64_t version;
|
||||
ino_t vno;
|
||||
uint32_t nhash;
|
||||
enum vtype type;
|
||||
uint8_t nsize;
|
||||
uint8_t name[0];
|
||||
|
||||
/* used by chfs_alloc_dirent and free counterpart */
|
||||
// size_t alloc_size;
|
||||
};
|
||||
|
||||
struct chfs_tmp_dnode {
|
||||
struct chfs_full_dnode *node;
|
||||
uint64_t version;
|
||||
uint32_t data_crc;
|
||||
//uint32_t partial_crc;
|
||||
//uint16_t csize;
|
||||
uint16_t overlapped;
|
||||
struct chfs_tmp_dnode *next;
|
||||
};
|
||||
|
||||
struct chfs_tmp_dnode_info {
|
||||
struct rb_node rb_node;
|
||||
struct chfs_tmp_dnode *tmpnode;
|
||||
};
|
||||
|
||||
struct chfs_readinode_info {
|
||||
struct rb_tree tdi_root;
|
||||
struct chfs_tmp_dnode_info *mdata_tn;
|
||||
uint64_t highest_version;
|
||||
struct chfs_node_ref *latest_ref;
|
||||
};
|
||||
|
||||
struct chfs_full_dnode {
|
||||
struct chfs_node_ref *nref;
|
||||
uint64_t ofs;
|
||||
uint32_t size;
|
||||
uint32_t frags;
|
||||
};
|
||||
|
||||
struct chfs_node_frag {
|
||||
struct rb_node rb_node;
|
||||
struct chfs_full_dnode *node;
|
||||
uint32_t size;
|
||||
uint64_t ofs;
|
||||
};
|
||||
|
||||
static inline struct chfs_node_frag *
|
||||
frag_first(struct rb_tree *tree)
|
||||
{
|
||||
struct chfs_node_frag *frag;
|
||||
|
||||
frag = (struct chfs_node_frag *)RB_TREE_MIN(tree);
|
||||
|
||||
return frag;
|
||||
}
|
||||
|
||||
static inline struct chfs_node_frag *
|
||||
frag_last(struct rb_tree *tree)
|
||||
{
|
||||
struct chfs_node_frag *frag;
|
||||
|
||||
frag = (struct chfs_node_frag *)RB_TREE_MAX(tree);
|
||||
|
||||
return frag;
|
||||
}
|
||||
|
||||
#define frag_next(tree, frag) (struct chfs_node_frag *)rb_tree_iterate(tree, frag, RB_DIR_RIGHT)
|
||||
#define frag_prev(tree, frag) (struct chfs_node_frag *)rb_tree_iterate(tree, frag, RB_DIR_LEFT)
|
||||
|
||||
|
||||
/* XXX hack
|
||||
#ifndef CHFS_FRAG_TREE
|
||||
#define CHFS_FRAG_TREE
|
||||
RB_HEAD(chfs_frag_tree, chfs_node_frag);
|
||||
#endif
|
||||
*/
|
||||
|
||||
/* for prototypes, properly defined in chfs_inode.h */
|
||||
//struct chfs_inode_ext;
|
||||
|
||||
/**
|
||||
* struct chfs_vnode_cache - in memory representation of a vnode
|
||||
* @v: pointer to the vnode info node
|
||||
* @dnode: pointer to the list of data nodes
|
||||
* @dirents: pointer to the list of directory entries
|
||||
* @vno_version: used only during scan, holds the current version number of
|
||||
* chfs_flash_vnode
|
||||
* @scan_dirents: used only during scan, holds the full representation of
|
||||
* directory entries of this vnode
|
||||
* @pvno: parent vnode number
|
||||
* @nlink: number of links to this vnode
|
||||
*/
|
||||
struct chfs_vnode_cache {
|
||||
// struct chfs_dirent *scan_dirents;
|
||||
void *p;
|
||||
struct chfs_dirent_list scan_dirents;
|
||||
|
||||
struct chfs_node_ref *v;
|
||||
struct chfs_node_ref *dnode;
|
||||
struct chfs_node_ref *dirents;
|
||||
|
||||
uint64_t *vno_version;
|
||||
uint64_t highest_version;
|
||||
|
||||
uint8_t flags;
|
||||
uint16_t state;
|
||||
ino_t vno;
|
||||
ino_t pvno;
|
||||
struct chfs_vnode_cache* next;
|
||||
uint32_t nlink;
|
||||
};
|
||||
|
||||
struct chfs_eraseblock
|
||||
{
|
||||
uint32_t lnr;
|
||||
|
||||
TAILQ_ENTRY(chfs_eraseblock) queue;
|
||||
//uint32_t bad_count;
|
||||
uint32_t unchecked_size;
|
||||
uint32_t used_size;
|
||||
uint32_t dirty_size;
|
||||
uint32_t free_size;
|
||||
uint32_t wasted_size;
|
||||
|
||||
struct chfs_node_ref *first_node;
|
||||
struct chfs_node_ref *last_node;
|
||||
|
||||
struct chfs_node_ref *gc_node; /* Next block to be garbage collected */
|
||||
};
|
||||
|
||||
TAILQ_HEAD(chfs_eraseblock_queue, chfs_eraseblock);
|
||||
|
||||
#define ALLOC_NORMAL 0
|
||||
#define ALLOC_DELETION 1
|
||||
#define ALLOC_GC 2
|
||||
|
||||
struct garbage_collector_thread {
|
||||
lwp_t *gcth_thread;
|
||||
kcondvar_t gcth_wakeup;
|
||||
bool gcth_running;
|
||||
};
|
||||
|
||||
#define CHFS_MP_FLAG_SCANNING 2
|
||||
#define CHFS_MP_FLAG_BUILDING 4
|
||||
|
||||
/**
|
||||
* struct chfs_mount - CHFS main descriptor structure
|
||||
* @ebh: eraseblock handler
|
||||
* @fl_index: index of flash device in the flash layer
|
||||
* @fs_version: filesystem version descriptor
|
||||
* @gbl_version: global version number
|
||||
* @max_vno: max vnode id
|
||||
* @chm_lock_mountfields:
|
||||
* @vnocache_hash: hash table of vnode caches
|
||||
* @vnocache_lock:
|
||||
* @blocks: array of eraseblocks on flash
|
||||
* @chm_root: used to protect all fields
|
||||
* @free_size: free size on the flash
|
||||
* @dirty_size: dirtied size on flash
|
||||
* @unchecked_size: size of unchecked data on flash
|
||||
* @free_queue: queue of free eraseblocks
|
||||
* @clean_queue: queue of clean eraseblocks
|
||||
* @dirty_queue: queue of dirty eraseblocks
|
||||
* @very_dirty_queue: queue of very dirty eraseblocks
|
||||
* @erase_pending_queue: queue of eraseblocks waiting for erasing
|
||||
* @erasable_pending_wbuf_queue: queue of eraseblocks waiting for erasing and
|
||||
* have data to write to them
|
||||
* @nextblock: next eraseblock to write to
|
||||
*
|
||||
* @nr_free_blocks: number of free blocks on the free_queue
|
||||
* @nr_erasable_blocks: number of blocks that can be erased and are on the
|
||||
* erasable_queue
|
||||
*
|
||||
*/
|
||||
struct chfs_mount {
|
||||
struct mount *chm_fsmp;
|
||||
// dev_t dev;
|
||||
// struct vnode *devvp;
|
||||
|
||||
struct chfs_ebh *chm_ebh;
|
||||
// int chm_fl_index;
|
||||
int chm_fs_version;
|
||||
uint64_t chm_gbl_version;
|
||||
ino_t chm_max_vno;
|
||||
ino_t chm_checked_vno;
|
||||
unsigned int chm_flags;
|
||||
|
||||
/* chm_lock_mountfields:
|
||||
* Used to protect all the following fields. */
|
||||
kmutex_t chm_lock_mountfields;
|
||||
|
||||
struct chfs_vnode_cache **chm_vnocache_hash;
|
||||
/* chm_lock_vnocache:
|
||||
* Used to protect the vnode cache.
|
||||
* If you have to lock chm_lock_mountfields and also chm_lock_vnocache,
|
||||
* you must lock chm_lock_mountfields first. */
|
||||
kmutex_t chm_lock_vnocache;
|
||||
|
||||
struct chfs_eraseblock *chm_blocks;
|
||||
|
||||
struct chfs_node *chm_root;
|
||||
|
||||
uint32_t chm_free_size;
|
||||
uint32_t chm_dirty_size;
|
||||
uint32_t chm_unchecked_size;
|
||||
uint32_t chm_used_size;
|
||||
uint32_t chm_wasted_size;
|
||||
/* chm_lock_sizes:
|
||||
* Used to protect the (free, used, etc.) sizes of the FS
|
||||
* (and also the sizes of each eraseblock).
|
||||
* If you have to lock chm_lock_mountfields and also chm_lock_sizes,
|
||||
* you must lock chm_lock_mountfields first. */
|
||||
kmutex_t chm_lock_sizes;
|
||||
|
||||
struct chfs_eraseblock_queue chm_free_queue;
|
||||
struct chfs_eraseblock_queue chm_clean_queue;
|
||||
struct chfs_eraseblock_queue chm_dirty_queue;
|
||||
struct chfs_eraseblock_queue chm_very_dirty_queue;
|
||||
struct chfs_eraseblock_queue chm_erasable_pending_wbuf_queue;
|
||||
struct chfs_eraseblock_queue chm_erase_pending_queue;
|
||||
|
||||
uint8_t chm_resv_blocks_deletion;
|
||||
uint8_t chm_resv_blocks_write;
|
||||
uint8_t chm_resv_blocks_gctrigger;
|
||||
uint8_t chm_resv_blocks_gcmerge;
|
||||
uint8_t chm_nospc_dirty;
|
||||
|
||||
uint8_t chm_vdirty_blocks_gctrigger;
|
||||
|
||||
struct chfs_eraseblock *chm_nextblock;
|
||||
|
||||
struct garbage_collector_thread chm_gc_thread;
|
||||
struct chfs_eraseblock *chm_gcblock;
|
||||
|
||||
int chm_nr_free_blocks;
|
||||
int chm_nr_erasable_blocks;
|
||||
|
||||
int32_t chm_fs_bmask;
|
||||
int32_t chm_fs_bsize;
|
||||
int32_t chm_fs_qbmask;
|
||||
int32_t chm_fs_bshift;
|
||||
int32_t chm_fs_fmask;
|
||||
int64_t chm_fs_qfmask;
|
||||
|
||||
/* TODO will we use these? */
|
||||
unsigned int chm_pages_max;
|
||||
unsigned int chm_pages_used;
|
||||
unsigned int chm_nodes_max;
|
||||
unsigned int chm_nodes_cnt;
|
||||
struct chfs_pool chm_dirent_pool;
|
||||
struct chfs_pool chm_node_pool;
|
||||
struct chfs_str_pool chm_str_pool;
|
||||
/**/
|
||||
|
||||
size_t chm_wbuf_pagesize;
|
||||
unsigned char* chm_wbuf;
|
||||
size_t chm_wbuf_ofs;
|
||||
size_t chm_wbuf_len;
|
||||
/* chm_lock_wbuf:
|
||||
* Used to protect the write buffer.
|
||||
* If you have to lock chm_lock_mountfields and also chm_lock_wbuf,
|
||||
* you must lock chm_lock_mountfields first. */
|
||||
krwlock_t chm_lock_wbuf;
|
||||
};
|
||||
|
||||
#define sleep_on_spinunlock(s) \
|
||||
do { \
|
||||
kmutex_t sleep_mtx; \
|
||||
kcondvar_t sleep_cnd; \
|
||||
cv_init(&sleep_cnd, "sleep_cnd"); \
|
||||
mutex_init(&sleep_mtx, MUTEX_DEFAULT, IPL_NONE); \
|
||||
mutex_spin_exit(s); \
|
||||
mutex_enter(&sleep_mtx); \
|
||||
cv_timedwait(&sleep_cnd, &sleep_mtx, mstohz(50)); \
|
||||
mutex_exit(&sleep_mtx); \
|
||||
mutex_destroy(&sleep_mtx); \
|
||||
cv_destroy(&sleep_cnd); \
|
||||
} while (0)
|
||||
#undef sleep_on_spinunlock
|
||||
|
||||
/*
|
||||
* TODO we should move here all of these from the bottom of the file
|
||||
* Macros/functions to convert from generic data structures to chfs
|
||||
* specific ones.
|
||||
*/
|
||||
|
||||
#define CHFS_OFFSET_DOT 0
|
||||
#define CHFS_OFFSET_DOTDOT 1
|
||||
#define CHFS_OFFSET_EOF 2
|
||||
#define CHFS_OFFSET_FIRST 3
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
/* chfs_build.c */
|
||||
void chfs_calc_trigger_levels(struct chfs_mount *);
|
||||
int chfs_build_filesystem(struct chfs_mount *);
|
||||
void chfs_build_set_vnodecache_nlink(struct chfs_mount *chmp,struct chfs_vnode_cache *vc);
|
||||
void chfs_build_remove_unlinked_vnode(struct chfs_mount *chmp,struct chfs_vnode_cache *vc, struct chfs_dirent_list *unlinked);
|
||||
|
||||
/* chfs_scan.c */
|
||||
int chfs_scan_eraseblock(struct chfs_mount *, struct chfs_eraseblock *);
|
||||
struct chfs_vnode_cache *chfs_scan_make_vnode_cache(struct chfs_mount *chmp, ino_t vno);
|
||||
int chfs_scan_check_node_hdr(struct chfs_flash_node_hdr *nhdr);
|
||||
int chfs_scan_check_vnode(struct chfs_mount *chmp, struct chfs_eraseblock *cheb, void *buf, off_t ofs);
|
||||
int chfs_scan_mark_dirent_obsolete(struct chfs_mount *chmp,struct chfs_vnode_cache *vc, struct chfs_dirent *fd);
|
||||
void chfs_add_fd_to_list(struct chfs_mount *chmp,struct chfs_dirent *new, struct chfs_vnode_cache *pvc);
|
||||
int chfs_scan_check_dirent_node(struct chfs_mount *chmp, struct chfs_eraseblock *cheb, void *buf, off_t ofs);
|
||||
int chfs_scan_check_data_node(struct chfs_mount *chmp, struct chfs_eraseblock *cheb, void *buf, off_t ofs);
|
||||
int chfs_scan_classify_cheb(struct chfs_mount *chmp,struct chfs_eraseblock *cheb);
|
||||
/* chfs_nodeops.c */
|
||||
int chfs_update_eb_dirty(struct chfs_mount *, struct chfs_eraseblock *, uint32_t);
|
||||
void chfs_add_node_to_list(struct chfs_mount *, struct chfs_vnode_cache *,
|
||||
struct chfs_node_ref *, struct chfs_node_ref **);
|
||||
void chfs_add_fd_to_inode(struct chfs_mount *,
|
||||
struct chfs_inode *, struct chfs_dirent *);
|
||||
void chfs_add_vnode_ref_to_vc(struct chfs_mount *, struct chfs_vnode_cache *,
|
||||
struct chfs_node_ref *);
|
||||
struct chfs_node_ref* chfs_nref_next(struct chfs_node_ref *);
|
||||
int chfs_nref_len(struct chfs_mount *,
|
||||
struct chfs_eraseblock *, struct chfs_node_ref *);
|
||||
int chfs_close_eraseblock(struct chfs_mount *,
|
||||
struct chfs_eraseblock *);
|
||||
int chfs_reserve_space_normal(struct chfs_mount *, uint32_t, int);
|
||||
int chfs_reserve_space_gc(struct chfs_mount *, uint32_t);
|
||||
int chfs_reserve_space(struct chfs_mount *, uint32_t);
|
||||
void chfs_mark_node_obsolete(struct chfs_mount *, struct chfs_node_ref *);
|
||||
|
||||
static inline struct chfs_vnode_cache *
|
||||
chfs_nref_to_vc(struct chfs_node_ref *nref)
|
||||
{
|
||||
while (nref->nref_next) {
|
||||
nref = nref->nref_next;
|
||||
//dbg("lnr: %u, ofs: %u\n", nref->nref_lnr, nref->nref_offset);
|
||||
//dbg("vno: %llu\n", ((struct chfs_vnode_cache *)(nref))->vno);
|
||||
//dbg("scan_dirents: %p\n", ((struct chfs_vnode_cache *)(nref))->scan_dirents);
|
||||
if (nref->nref_lnr == REF_LINK_TO_NEXT) {
|
||||
dbg("Link to next!\n");
|
||||
} else if (nref->nref_lnr == REF_EMPTY_NODE) {
|
||||
dbg("Empty!\n");
|
||||
}
|
||||
}
|
||||
//dbg("vno: %llu\n", ((struct chfs_vnode_cache *)(nref))->vno);
|
||||
|
||||
//dbg("NREF_TO_VC: GET IT\n");
|
||||
//dbg("nref_next: %p, lnr: %u, ofs: %u\n", nref->nref_next, nref->nref_lnr, nref->nref_offset);
|
||||
struct chfs_vnode_cache *vc = (struct chfs_vnode_cache *) nref;
|
||||
dbg("vno: %llu, pvno: %llu, hv: %llu, nlink: %u\n", vc->vno, vc->pvno, vc->highest_version, vc->nlink);
|
||||
//return ((struct chfs_vnode_cache *)nref);
|
||||
return vc;
|
||||
}
|
||||
|
||||
|
||||
/* chfs_malloc.c */
|
||||
int chfs_alloc_pool_caches(void);
|
||||
void chfs_destroy_pool_caches(void);
|
||||
struct chfs_vnode_cache* chfs_vnode_cache_alloc(ino_t);
|
||||
void chfs_vnode_cache_free(struct chfs_vnode_cache *);
|
||||
struct chfs_node_ref* chfs_alloc_node_ref(
|
||||
struct chfs_eraseblock *);
|
||||
void chfs_free_node_refs(struct chfs_eraseblock *cheb);
|
||||
struct chfs_dirent* chfs_alloc_dirent(int);
|
||||
void chfs_free_dirent(struct chfs_dirent *);
|
||||
struct chfs_flash_vnode* chfs_alloc_flash_vnode(void);
|
||||
void chfs_free_flash_vnode(struct chfs_flash_vnode *);
|
||||
struct chfs_flash_dirent_node* chfs_alloc_flash_dirent(void);
|
||||
void chfs_free_flash_dirent(struct chfs_flash_dirent_node *);
|
||||
struct chfs_flash_data_node* chfs_alloc_flash_dnode(void);
|
||||
void chfs_free_flash_dnode(struct chfs_flash_data_node *);
|
||||
struct chfs_node_frag* chfs_alloc_node_frag(void);
|
||||
void chfs_free_node_frag(struct chfs_node_frag *);
|
||||
struct chfs_node_ref* chfs_alloc_refblock(void);
|
||||
void chfs_free_refblock(struct chfs_node_ref *nref);
|
||||
struct chfs_full_dnode* chfs_alloc_full_dnode(void);
|
||||
void chfs_free_full_dnode(struct chfs_full_dnode *fd);
|
||||
struct chfs_tmp_dnode * chfs_alloc_tmp_dnode(void);
|
||||
void chfs_free_tmp_dnode(struct chfs_tmp_dnode *);
|
||||
struct chfs_tmp_dnode_info * chfs_alloc_tmp_dnode_info(void);
|
||||
void chfs_free_tmp_dnode_info(struct chfs_tmp_dnode_info *);
|
||||
|
||||
/* chfs_readinode.c */
|
||||
int chfs_read_inode(struct chfs_mount *, struct chfs_inode *);
|
||||
int chfs_read_inode_internal(struct chfs_mount *, struct chfs_inode *);
|
||||
void chfs_kill_fragtree(struct rb_tree *);
|
||||
uint32_t chfs_truncate_fragtree(struct chfs_mount *,
|
||||
struct rb_tree *, uint32_t);
|
||||
int chfs_add_full_dnode_to_inode(struct chfs_mount *,
|
||||
struct chfs_inode *,
|
||||
struct chfs_full_dnode *);
|
||||
int chfs_read_data(struct chfs_mount*, struct vnode *,
|
||||
struct buf *);
|
||||
|
||||
/* chfs_erase.c */
|
||||
int chfs_remap_leb(struct chfs_mount *chmp);
|
||||
|
||||
/* chfs_ihash.c */
|
||||
void chfs_ihashinit(void);
|
||||
void chfs_ihashreinit(void);
|
||||
void chfs_ihashdone(void);
|
||||
struct vnode *chfs_ihashlookup(dev_t, ino_t);
|
||||
struct vnode *chfs_ihashget(dev_t, ino_t, int);
|
||||
void chfs_ihashins(struct chfs_inode *);
|
||||
void chfs_ihashrem(struct chfs_inode *);
|
||||
|
||||
extern kmutex_t chfs_ihash_lock;
|
||||
extern kmutex_t chfs_hashlock;
|
||||
|
||||
/* chfs_gc.c */
|
||||
void chfs_gc_trigger(struct chfs_mount *);
|
||||
int chfs_gc_thread_should_wake(struct chfs_mount *);
|
||||
void chfs_gc_thread(void *);
|
||||
void chfs_gc_thread_start(struct chfs_mount *);
|
||||
void chfs_gc_thread_stop(struct chfs_mount *);
|
||||
int chfs_gcollect_pass(struct chfs_mount *);
|
||||
|
||||
/* chfs_vfsops.c*/
|
||||
int chfs_gop_alloc(struct vnode *vp, off_t off, off_t len, int flags,kauth_cred_t cred);
|
||||
int chfs_mountfs(struct vnode *devvp, struct mount *mp);
|
||||
|
||||
/* chfs_vnops.c */
|
||||
extern int (**chfs_vnodeop_p)(void *);
|
||||
extern int (**chfs_specop_p)(void *);
|
||||
extern int (**chfs_fifoop_p)(void *);
|
||||
int chfs_lookup(void *);
|
||||
int chfs_create(void *);
|
||||
int chfs_mknod(void *);
|
||||
int chfs_open(void *);
|
||||
int chfs_close(void *);
|
||||
int chfs_access(void *);
|
||||
int chfs_getattr(void *);
|
||||
int chfs_setattr(void *);
|
||||
int chfs_chown(struct vnode *, uid_t, gid_t, kauth_cred_t);
|
||||
int chfs_chmod(struct vnode *, int, kauth_cred_t);
|
||||
int chfs_read(void *);
|
||||
int chfs_write(void *);
|
||||
int chfs_fsync(void *);
|
||||
int chfs_remove(void *);
|
||||
int chfs_link(void *);
|
||||
int chfs_rename(void *);
|
||||
int chfs_mkdir(void *);
|
||||
int chfs_rmdir(void *);
|
||||
int chfs_symlink(void *);
|
||||
int chfs_readdir(void *);
|
||||
int chfs_readlink(void *);
|
||||
int chfs_inactive(void *);
|
||||
int chfs_reclaim(void *);
|
||||
int chfs_advlock(void *);
|
||||
int chfs_strategy(void *);
|
||||
int chfs_bmap(void *);
|
||||
|
||||
/* chfs_vnode.c */
|
||||
struct vnode *chfs_vnode_lookup(struct chfs_mount *, ino_t);
|
||||
int chfs_readvnode(struct mount *, ino_t, struct vnode **);
|
||||
int chfs_readdirent(struct mount *, struct chfs_node_ref *,
|
||||
struct chfs_inode *);
|
||||
int chfs_makeinode(int, struct vnode *, struct vnode **,
|
||||
struct componentname *, int );
|
||||
void chfs_set_vnode_size(struct vnode *, size_t);
|
||||
void chfs_change_size_free(struct chfs_mount *,
|
||||
struct chfs_eraseblock *, int);
|
||||
void chfs_change_size_dirty(struct chfs_mount *,
|
||||
struct chfs_eraseblock *, int);
|
||||
void chfs_change_size_unchecked(struct chfs_mount *,
|
||||
struct chfs_eraseblock *, int);
|
||||
void chfs_change_size_used(struct chfs_mount *,
|
||||
struct chfs_eraseblock *, int);
|
||||
void chfs_change_size_wasted(struct chfs_mount *,
|
||||
struct chfs_eraseblock *, int);
|
||||
|
||||
/* chfs_vnode_cache.c */
|
||||
struct chfs_vnode_cache **chfs_vnocache_hash_init(void);
|
||||
void chfs_vnocache_hash_destroy(struct chfs_vnode_cache **);
|
||||
void chfs_vnode_cache_set_state(struct chfs_mount *,
|
||||
struct chfs_vnode_cache *, int);
|
||||
struct chfs_vnode_cache* chfs_vnode_cache_get(
|
||||
struct chfs_mount *, ino_t);
|
||||
void chfs_vnode_cache_add(struct chfs_mount *,
|
||||
struct chfs_vnode_cache *);
|
||||
void chfs_vnode_cache_remove(struct chfs_mount *,
|
||||
struct chfs_vnode_cache *);
|
||||
|
||||
/* chfs_wbuf.c */
|
||||
int chfs_write_wbuf(struct chfs_mount*, const struct iovec *, long,
|
||||
off_t, size_t *);
|
||||
int chfs_flush_pending_wbuf(struct chfs_mount *);
|
||||
|
||||
/* chfs_write.c */
|
||||
int chfs_write_flash_vnode(struct chfs_mount *, struct chfs_inode *, int);
|
||||
int chfs_write_flash_dirent(struct chfs_mount *, struct chfs_inode *,
|
||||
struct chfs_inode *, struct chfs_dirent *, ino_t, int);
|
||||
int chfs_write_flash_dnode(struct chfs_mount *, struct vnode *,
|
||||
struct buf *, struct chfs_full_dnode *);
|
||||
int chfs_do_link(struct chfs_inode *, struct chfs_inode *, const char *, int, enum vtype);
|
||||
int chfs_do_unlink(struct chfs_inode *, struct chfs_inode *, const char *, int);
|
||||
|
||||
/* chfs_subr.c */
|
||||
size_t chfs_mem_info(bool);
|
||||
struct chfs_dirent * chfs_dir_lookup(struct chfs_inode *,
|
||||
struct componentname *);
|
||||
int chfs_filldir (struct uio *, ino_t, const char *, int, enum vtype);
|
||||
int chfs_chsize(struct vnode *, u_quad_t, kauth_cred_t);
|
||||
int chfs_chflags(struct vnode *, int, kauth_cred_t);
|
||||
void chfs_itimes(struct chfs_inode *, const struct timespec *,
|
||||
const struct timespec *, const struct timespec *);
|
||||
int chfs_update(struct vnode *, const struct timespec *,
|
||||
const struct timespec *, int);
|
||||
//int chfs_truncate(struct vnode *, off_t);
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
/* Some inline functions temporarily placed here */
|
||||
static inline int
|
||||
chfs_map_leb(struct chfs_mount *chmp, int lnr)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = ebh_map_leb(chmp->chm_ebh, lnr);
|
||||
if (err)
|
||||
chfs_err("unmap leb %d failed, error: %d\n",lnr, err);
|
||||
|
||||
return err;
|
||||
|
||||
}
|
||||
|
||||
static inline int
|
||||
chfs_unmap_leb(struct chfs_mount *chmp, int lnr)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = ebh_unmap_leb(chmp->chm_ebh, lnr);
|
||||
if (err)
|
||||
chfs_err("unmap leb %d failed, error: %d\n",lnr, err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline int
|
||||
chfs_read_leb(struct chfs_mount *chmp, int lnr, char *buf,
|
||||
int offset, int len, size_t *retlen)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = ebh_read_leb(chmp->chm_ebh, lnr, buf, offset, len, retlen);
|
||||
if (err)
|
||||
chfs_err("read leb %d:%d failed, error: %d\n",lnr, offset, err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline int chfs_write_leb(struct chfs_mount *chmp, int lnr, char *buf,
|
||||
int offset, int len, size_t *retlen)
|
||||
{
|
||||
int err;
|
||||
err = ebh_write_leb(chmp->chm_ebh, lnr, buf, offset, len, retlen);
|
||||
if (err)
|
||||
chfs_err("write leb %d:%d failed, error: %d\n",lnr, offset, err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/* Code from dummyfs.h */
|
||||
/******************************************************************************/
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
#define CHFS_PAGES_RESERVED (4 * 1024 * 1024 / PAGE_SIZE)
|
||||
|
||||
static __inline size_t
|
||||
CHFS_PAGES_MAX(struct chfs_mount *chmp)
|
||||
{
|
||||
size_t freepages;
|
||||
|
||||
freepages = chfs_mem_info(false);
|
||||
if (freepages < CHFS_PAGES_RESERVED)
|
||||
freepages = 0;
|
||||
else
|
||||
freepages -= CHFS_PAGES_RESERVED;
|
||||
|
||||
return MIN(chmp->chm_pages_max, freepages + chmp->chm_pages_used);
|
||||
}
|
||||
|
||||
#define CHFS_ITIMES(ip, acc, mod, cre) \
|
||||
while ((ip)->iflag & (IN_ACCESS | IN_CHANGE | IN_UPDATE | IN_MODIFY)) \
|
||||
chfs_itimes(ip, acc, mod, cre)
|
||||
/* used for KASSERTs */
|
||||
|
||||
#define IMPLIES(a, b) (!(a) || (b))
|
||||
#define IFF(a, b) (IMPLIES(a, b) && IMPLIES(b, a))
|
||||
|
||||
/*
|
||||
struct chfs_node;
|
||||
*/
|
||||
/*
|
||||
* Internal representation of a dummyfs directory entry.
|
||||
*/
|
||||
/*
|
||||
struct chfs_dirent {
|
||||
TAILQ_ENTRY(chfs_dirent) chd_entries;
|
||||
uint16_t chd_namelen;
|
||||
char * chd_name;
|
||||
struct chfs_node * chd_node;
|
||||
};
|
||||
|
||||
TAILQ_HEAD(chfs_dir, chfs_dirent);
|
||||
|
||||
|
||||
*/
|
||||
/*
|
||||
* Internal representation of a dummyfs file system node.
|
||||
*/
|
||||
/*
|
||||
struct chfs_node {
|
||||
LIST_ENTRY(chfs_node) chn_entries;
|
||||
enum vtype chn_type;
|
||||
ino_t chn_id;
|
||||
int chn_status;
|
||||
#define CHFS_NODE_ACCESSED (1 << 1)
|
||||
#define CHFS_NODE_MODIFIED (1 << 2)
|
||||
#define CHFS_NODE_CHANGED (1 << 3)
|
||||
|
||||
off_t chn_size;
|
||||
|
||||
uid_t chn_uid;
|
||||
gid_t chn_gid;
|
||||
mode_t chn_mode;
|
||||
int chn_flags;
|
||||
nlink_t chn_links;
|
||||
struct timespec chn_atime;
|
||||
struct timespec chn_mtime;
|
||||
struct timespec chn_ctime;
|
||||
struct timespec chn_birthtime;
|
||||
unsigned long chn_gen;
|
||||
|
||||
struct lockf * chn_lockf;
|
||||
|
||||
kmutex_t chn_vlock;
|
||||
struct vnode * chn_vnode;
|
||||
|
||||
union {
|
||||
struct {
|
||||
dev_t chn_rdev;
|
||||
} chn_dev;
|
||||
|
||||
struct {
|
||||
struct chfs_node * chn_parent;
|
||||
|
||||
struct chfs_dir chn_dir;
|
||||
|
||||
off_t chn_readdir_lastn;
|
||||
struct chfs_dirent * chn_readdir_lastp;
|
||||
} chn_dir;
|
||||
|
||||
struct chn_lnk {
|
||||
char * chn_link;
|
||||
} chn_lnk;
|
||||
|
||||
struct chn_reg {
|
||||
struct uvm_object * chn_aobj;
|
||||
size_t chn_aobj_pages;
|
||||
} chn_reg;
|
||||
} chn_spec;
|
||||
};
|
||||
*/
|
||||
/*
|
||||
LIST_HEAD(chfs_node_list, chfs_node);
|
||||
|
||||
#define VTOCHN(vp) ((struct chfs_node *)(vp)->v_data)
|
||||
#define CHNTOV(chnp) ((chnp)->chn_vnode)
|
||||
|
||||
*/
|
||||
/*
|
||||
* Ensures that the node pointed by 'node' is a directory and that its
|
||||
* contents are consistent with respect to directories.
|
||||
*/
|
||||
/*
|
||||
#ifdef someonefixthisplease
|
||||
#define CHFS_VALIDATE_DIR(node) \
|
||||
KASSERT((node)->chn_type == VDIR); \
|
||||
KASSERT((node)->chn_size % sizeof(struct chfs_dirent) == 0); \
|
||||
KASSERT((node)->chn_spec.chn_dir.chn_readdir_lastp == NULL || \
|
||||
chfs_dircookie((node)->chn_spec.chn_dir.chn_readdir_lastp) == \
|
||||
(node)->chn_spec.chn_dir.chn_readdir_lastn);
|
||||
#else
|
||||
#define CHFS_VALIDATE_DIR(node) \
|
||||
KASSERT((node)->chn_type == VDIR); \
|
||||
KASSERT((node)->chn_size % sizeof(struct chfs_dirent) == 0);
|
||||
#endif
|
||||
|
||||
*/
|
||||
|
||||
/* Returns the available space for the given file system. */
|
||||
/*
|
||||
#define CHFS_PAGES_AVAIL(dmp) \
|
||||
((ssize_t)(CHFS_PAGES_MAX(dmp) - (dmp)->chm_pages_used))
|
||||
|
||||
|
||||
|
||||
static __inline
|
||||
struct chfs_node *
|
||||
VP_TO_CHFS_NODE(struct vnode *vp)
|
||||
{
|
||||
struct chfs_node *node;
|
||||
|
||||
#ifdef KASSERT
|
||||
KASSERT((vp) != NULL && (vp)->v_data != NULL);
|
||||
#endif
|
||||
node = (struct chfs_node *)vp->v_data;
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
static __inline
|
||||
struct chfs_node *
|
||||
VP_TO_CHFS_DIR(struct vnode *vp)
|
||||
{
|
||||
struct chfs_node *node;
|
||||
|
||||
node = VP_TO_CHFS_NODE(vp);
|
||||
#ifdef KASSERT
|
||||
CHFS_VALIDATE_DIR(node);
|
||||
#endif
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
*/
|
||||
#endif /* __CHFS_H__ */
|
|
@ -0,0 +1,53 @@
|
|||
/* $NetBSD: chfs_args.h,v 1.1 2011/11/24 15:51:31 ahoka Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2010 Department of Software Engineering,
|
||||
* University of Szeged, Hungary
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by the Department of Software Engineering, University of Szeged, Hungary
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _FS_CHFS_CHFS_ARGS_H_
|
||||
#define _FS_CHFS_CHFS_ARGS_H_
|
||||
|
||||
#define CHFS_ARGS_VERSION 1
|
||||
|
||||
/**
|
||||
* struct chfs_args - arguments needed when mounting filesystem
|
||||
* @fl_index: index of the flash device in the flash layer
|
||||
*/
|
||||
struct chfs_args {
|
||||
//int ca_version;
|
||||
char *fspec;
|
||||
int fl_index;
|
||||
|
||||
/* Root node attributes. */
|
||||
/*uid_t ca_root_uid;
|
||||
gid_t ca_root_gid;
|
||||
mode_t ca_root_mode;*/
|
||||
};
|
||||
|
||||
#endif /* _FS_CHFS_CHFS_ARGS_H_ */
|
|
@ -0,0 +1,404 @@
|
|||
/* $NetBSD: chfs_build.c,v 1.1 2011/11/24 15:51:31 ahoka Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2010 Department of Software Engineering,
|
||||
* University of Szeged, Hungary
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by the Department of Software Engineering, University of Szeged, Hungary
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "chfs.h"
|
||||
//#include </root/xipffs/netbsd.chfs/chfs.h>
|
||||
|
||||
|
||||
void
|
||||
chfs_calc_trigger_levels(struct chfs_mount *chmp)
|
||||
{
|
||||
uint32_t size;
|
||||
|
||||
chmp->chm_resv_blocks_deletion = 2;
|
||||
|
||||
size = chmp->chm_ebh->flash_size / 50; //2% of flash size
|
||||
size += chmp->chm_ebh->peb_nr * 100;
|
||||
size += chmp->chm_ebh->eb_size - 1;
|
||||
|
||||
chmp->chm_resv_blocks_write =
|
||||
chmp->chm_resv_blocks_deletion + (size / chmp->chm_ebh->eb_size);
|
||||
chmp->chm_resv_blocks_gctrigger = chmp->chm_resv_blocks_write + 1;
|
||||
chmp->chm_resv_blocks_gcmerge = chmp->chm_resv_blocks_deletion + 1;
|
||||
chmp->chm_vdirty_blocks_gctrigger = chmp->chm_resv_blocks_gctrigger * 10;
|
||||
|
||||
chmp->chm_nospc_dirty =
|
||||
chmp->chm_ebh->eb_size + (chmp->chm_ebh->flash_size / 100);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* chfs_build_set_vnodecache_nlink - set pvno and nlink in vnodecaches
|
||||
* @chmp: CHFS main descriptor structure
|
||||
* @vc: vnode cache
|
||||
* This function travels @vc's directory entries and sets the pvno and nlink
|
||||
* attribute of the vnode where the dirent's vno points.
|
||||
*/
|
||||
void
|
||||
chfs_build_set_vnodecache_nlink(struct chfs_mount *chmp,
|
||||
struct chfs_vnode_cache *vc)
|
||||
{
|
||||
struct chfs_dirent *fd;
|
||||
//dbg("set nlink\n");
|
||||
|
||||
// for (fd = vc->scan_dirents; fd; fd = fd->next) {
|
||||
TAILQ_FOREACH(fd, &vc->scan_dirents, fds) {
|
||||
struct chfs_vnode_cache *child_vc;
|
||||
|
||||
if (!fd->vno)
|
||||
continue;
|
||||
|
||||
mutex_enter(&chmp->chm_lock_vnocache);
|
||||
child_vc = chfs_vnode_cache_get(chmp, fd->vno);
|
||||
mutex_exit(&chmp->chm_lock_vnocache);
|
||||
if (!child_vc) {
|
||||
chfs_mark_node_obsolete(chmp, fd->nref);
|
||||
continue;
|
||||
}
|
||||
if (fd->type == VDIR) {
|
||||
if (child_vc->nlink < 1)
|
||||
child_vc->nlink = 1;
|
||||
|
||||
if (child_vc->pvno) {
|
||||
chfs_err("found a hard link: child dir: %s"
|
||||
", (vno: %llu) of dir vno: %llu\n",
|
||||
fd->name, fd->vno, vc->vno);
|
||||
} else {
|
||||
//dbg("child_vc->pvno =
|
||||
// vc->vno; pvno = %d\n", child_vc->pvno);
|
||||
child_vc->pvno = vc->vno;
|
||||
}
|
||||
}
|
||||
child_vc->nlink++;
|
||||
//dbg("child_vc->nlink++;\n");
|
||||
//child_vc->nlink++;
|
||||
vc->nlink++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* chfs_build_remove_unlinked vnode
|
||||
*/
|
||||
/* static */
|
||||
void
|
||||
chfs_build_remove_unlinked_vnode(struct chfs_mount *chmp,
|
||||
struct chfs_vnode_cache *vc,
|
||||
// struct chfs_dirent **unlinked)
|
||||
struct chfs_dirent_list *unlinked)
|
||||
{
|
||||
struct chfs_node_ref *nref;
|
||||
struct chfs_dirent *fd, *tmpfd;
|
||||
|
||||
dbg("START\n");
|
||||
dbg("vno: %llu\n", vc->vno);
|
||||
|
||||
nref = vc->dnode;
|
||||
KASSERT(mutex_owned(&chmp->chm_lock_mountfields));
|
||||
// The vnode cache is at the end of the data node's chain
|
||||
while (nref != (struct chfs_node_ref *)vc) {
|
||||
struct chfs_node_ref *next = nref->nref_next;
|
||||
dbg("mark dnode\n");
|
||||
chfs_mark_node_obsolete(chmp, nref);
|
||||
nref = next;
|
||||
}
|
||||
nref = vc->dirents;
|
||||
// The vnode cache is at the end of the dirent node's chain
|
||||
while (nref != (struct chfs_node_ref *)vc) {
|
||||
struct chfs_node_ref *next = nref->nref_next;
|
||||
dbg("mark dirent\n");
|
||||
chfs_mark_node_obsolete(chmp, nref);
|
||||
nref = next;
|
||||
}
|
||||
if (!TAILQ_EMPTY(&vc->scan_dirents)) {
|
||||
TAILQ_FOREACH_SAFE(fd, &vc->scan_dirents, fds, tmpfd) {
|
||||
// while (vc->scan_dirents) {
|
||||
struct chfs_vnode_cache *child_vc;
|
||||
// fd = vc->scan_dirents;
|
||||
dbg("dirent dump:\n");
|
||||
dbg(" ->vno: %llu\n", fd->vno);
|
||||
dbg(" ->version: %llu\n", fd->version);
|
||||
dbg(" ->nhash: 0x%x\n", fd->nhash);
|
||||
dbg(" ->nsize: %d\n", fd->nsize);
|
||||
dbg(" ->name: %s\n", fd->name);
|
||||
dbg(" ->type: %d\n", fd->type);
|
||||
// vc->scan_dirents = fd->next;
|
||||
TAILQ_REMOVE(&vc->scan_dirents, fd, fds);
|
||||
|
||||
if (!fd->vno) {
|
||||
chfs_free_dirent(fd);
|
||||
continue;
|
||||
}
|
||||
mutex_enter(&chmp->chm_lock_vnocache);
|
||||
child_vc = chfs_vnode_cache_get(chmp, fd->vno);
|
||||
mutex_exit(&chmp->chm_lock_vnocache);
|
||||
if (!child_vc) {
|
||||
chfs_free_dirent(fd);
|
||||
continue;
|
||||
}
|
||||
/**
|
||||
* Decrease nlink in child. If it is 0, add to unlinked
|
||||
* dirents or just free it otherwise.
|
||||
*/
|
||||
child_vc->nlink--;
|
||||
|
||||
if (!child_vc->nlink) {
|
||||
//dbg("nlink is 0\n");
|
||||
// fd->next = *unlinked;
|
||||
// *unlinked = fd;
|
||||
// XXX HEAD or TAIL?
|
||||
// original code did HEAD, but we could add
|
||||
// it to the TAIL easily with TAILQ.
|
||||
TAILQ_INSERT_TAIL(unlinked, fd, fds);
|
||||
} else {
|
||||
chfs_free_dirent(fd);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
dbg("there are no scan dirents\n");
|
||||
}
|
||||
|
||||
nref = vc->v;
|
||||
while ((struct chfs_vnode_cache *)nref != vc) {
|
||||
if (!CHFS_REF_OBSOLETE(nref))
|
||||
chfs_mark_node_obsolete(chmp, nref);
|
||||
nref = nref->nref_next;
|
||||
}
|
||||
|
||||
mutex_enter(&chmp->chm_lock_vnocache);
|
||||
if (vc->vno != CHFS_ROOTINO)
|
||||
chfs_vnode_cache_set_state(chmp, vc, VNO_STATE_UNCHECKED);
|
||||
mutex_exit(&chmp->chm_lock_vnocache);
|
||||
dbg("END\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* chfs_build_filesystem - build in-memory representation of filesystem
|
||||
* @chmp: super block information
|
||||
*
|
||||
* Step 1:
|
||||
* This function scans through the eraseblocks mapped in EBH.
|
||||
* During scan builds up the map of vnodes and directory entries and puts them
|
||||
* into the vnode_cache.
|
||||
* Step 2:
|
||||
* Scans the directory tree and set the nlink in the vnode caches.
|
||||
* Step 3:
|
||||
* Scans vnode caches with nlink = 0
|
||||
*/
|
||||
int
|
||||
chfs_build_filesystem(struct chfs_mount *chmp)
|
||||
{
|
||||
int i,err = 0;
|
||||
struct chfs_vnode_cache *vc;
|
||||
struct chfs_dirent *fd, *tmpfd;
|
||||
// struct chfs_dirent *unlinked = NULL;
|
||||
struct chfs_node_ref **nref;
|
||||
struct chfs_dirent_list unlinked;
|
||||
struct chfs_vnode_cache *notregvc;
|
||||
|
||||
TAILQ_INIT(&unlinked);
|
||||
|
||||
mutex_enter(&chmp->chm_lock_mountfields);
|
||||
|
||||
/**
|
||||
* Step 1
|
||||
*/
|
||||
chmp->chm_flags |= CHFS_MP_FLAG_SCANNING;
|
||||
for (i = 0; i < chmp->chm_ebh->peb_nr; i++) {
|
||||
//dbg("processing block: %d\n", i);
|
||||
chmp->chm_blocks[i].lnr = i;
|
||||
chmp->chm_blocks[i].free_size = chmp->chm_ebh->eb_size;
|
||||
//If the LEB is add to free list skip it.
|
||||
if (chmp->chm_ebh->lmap[i] < 0) {
|
||||
//dbg("block %d is unmapped\n", i);
|
||||
TAILQ_INSERT_TAIL(&chmp->chm_free_queue,
|
||||
&chmp->chm_blocks[i], queue);
|
||||
chmp->chm_nr_free_blocks++;
|
||||
continue;
|
||||
}
|
||||
|
||||
err = chfs_scan_eraseblock(chmp, &chmp->chm_blocks[i]);
|
||||
switch (err) {
|
||||
case CHFS_BLK_STATE_FREE:
|
||||
chmp->chm_nr_free_blocks++;
|
||||
TAILQ_INSERT_TAIL(&chmp->chm_free_queue,
|
||||
&chmp->chm_blocks[i], queue);
|
||||
break;
|
||||
case CHFS_BLK_STATE_CLEAN:
|
||||
TAILQ_INSERT_TAIL(&chmp->chm_clean_queue,
|
||||
&chmp->chm_blocks[i], queue);
|
||||
break;
|
||||
case CHFS_BLK_STATE_PARTDIRTY:
|
||||
//dbg("free size: %d\n", chmp->chm_blocks[i].free_size);
|
||||
if (chmp->chm_blocks[i].free_size > chmp->chm_wbuf_pagesize &&
|
||||
(!chmp->chm_nextblock ||
|
||||
chmp->chm_blocks[i].free_size >
|
||||
chmp->chm_nextblock->free_size)) {
|
||||
/* convert the old nextblock's free size to
|
||||
* dirty and put it on a list */
|
||||
if (chmp->chm_nextblock) {
|
||||
err = chfs_close_eraseblock(chmp,
|
||||
chmp->chm_nextblock);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
chmp->chm_nextblock = &chmp->chm_blocks[i];
|
||||
} else {
|
||||
/* convert the scanned block's free size to
|
||||
* dirty and put it on a list */
|
||||
err = chfs_close_eraseblock(chmp,
|
||||
&chmp->chm_blocks[i]);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
break;
|
||||
case CHFS_BLK_STATE_ALLDIRTY:
|
||||
/*
|
||||
* The block has a valid EBH header, but it doesn't
|
||||
* contain any valid data.
|
||||
*/
|
||||
TAILQ_INSERT_TAIL(&chmp->chm_erase_pending_queue,
|
||||
&chmp->chm_blocks[i], queue);
|
||||
chmp->chm_nr_erasable_blocks++;
|
||||
break;
|
||||
default:
|
||||
/* It was an error, unknown state */
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
chmp->chm_flags &= ~CHFS_MP_FLAG_SCANNING;
|
||||
|
||||
|
||||
//TODO need bad block check (and bad block handling in EBH too!!)
|
||||
/* Now EBH only checks block is bad during its scan operation.
|
||||
* Need check at erase + write + read...
|
||||
*/
|
||||
|
||||
/**
|
||||
* Step 2
|
||||
*/
|
||||
chmp->chm_flags |= CHFS_MP_FLAG_BUILDING;
|
||||
for (i = 0; i < VNODECACHE_SIZE; i++) {
|
||||
vc = chmp->chm_vnocache_hash[i];
|
||||
while (vc) {
|
||||
dbg("vc->vno: %llu\n", vc->vno);
|
||||
if (!TAILQ_EMPTY(&vc->scan_dirents))
|
||||
chfs_build_set_vnodecache_nlink(chmp, vc);
|
||||
vc = vc->next;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Step 3
|
||||
* Scan for vnodes with 0 nlink.
|
||||
*/
|
||||
for (i = 0; i < VNODECACHE_SIZE; i++) {
|
||||
vc = chmp->chm_vnocache_hash[i];
|
||||
while (vc) {
|
||||
if (vc->nlink) {
|
||||
vc = vc->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
//dbg("remove unlinked start i: %d\n", i);
|
||||
chfs_build_remove_unlinked_vnode(chmp,
|
||||
vc, &unlinked);
|
||||
//dbg("remove unlinked end\n");
|
||||
vc = vc->next;
|
||||
}
|
||||
}
|
||||
/* Remove the newly unlinked vnodes. They are on the unlinked list */
|
||||
TAILQ_FOREACH_SAFE(fd, &unlinked, fds, tmpfd) {
|
||||
// while (unlinked) {
|
||||
// fd = unlinked;
|
||||
// unlinked = fd->next;
|
||||
TAILQ_REMOVE(&unlinked, fd, fds);
|
||||
mutex_enter(&chmp->chm_lock_vnocache);
|
||||
vc = chfs_vnode_cache_get(chmp, fd->vno);
|
||||
mutex_exit(&chmp->chm_lock_vnocache);
|
||||
if (vc) {
|
||||
chfs_build_remove_unlinked_vnode(chmp,
|
||||
vc, &unlinked);
|
||||
}
|
||||
chfs_free_dirent(fd);
|
||||
}
|
||||
|
||||
chmp->chm_flags &= ~CHFS_MP_FLAG_BUILDING;
|
||||
|
||||
/* Free all dirents */
|
||||
for (i = 0; i < VNODECACHE_SIZE; i++) {
|
||||
vc = chmp->chm_vnocache_hash[i];
|
||||
while (vc) {
|
||||
TAILQ_FOREACH_SAFE(fd, &vc->scan_dirents, fds, tmpfd) {
|
||||
// while (vc->scan_dirents) {
|
||||
// fd = vc->scan_dirents;
|
||||
// vc->scan_dirents = fd->next;
|
||||
TAILQ_REMOVE(&vc->scan_dirents, fd, fds);
|
||||
if (fd->vno == 0) {
|
||||
//for (nref = &vc->dirents;
|
||||
// *nref != fd->nref;
|
||||
// nref = &((*nref)->next));
|
||||
|
||||
nref = &fd->nref;
|
||||
*nref = fd->nref->nref_next;
|
||||
//fd->nref->nref_next = NULL;
|
||||
} else if (fd->type == VDIR) {
|
||||
//set state every non-VREG file's vc
|
||||
mutex_enter(&chmp->chm_lock_vnocache);
|
||||
notregvc =
|
||||
chfs_vnode_cache_get(chmp,
|
||||
fd->vno);
|
||||
chfs_vnode_cache_set_state(chmp,
|
||||
notregvc, VNO_STATE_PRESENT);
|
||||
mutex_exit(&chmp->chm_lock_vnocache);
|
||||
}
|
||||
chfs_free_dirent(fd);
|
||||
}
|
||||
// vc->scan_dirents = NULL;
|
||||
KASSERT(TAILQ_EMPTY(&vc->scan_dirents));
|
||||
vc = vc->next;
|
||||
}
|
||||
}
|
||||
|
||||
//Set up chmp->chm_wbuf_ofs for the first write
|
||||
if (chmp->chm_nextblock) {
|
||||
dbg("free_size: %d\n", chmp->chm_nextblock->free_size);
|
||||
chmp->chm_wbuf_ofs = chmp->chm_ebh->eb_size -
|
||||
chmp->chm_nextblock->free_size;
|
||||
} else {
|
||||
chmp->chm_wbuf_ofs = 0xffffffff;
|
||||
}
|
||||
mutex_exit(&chmp->chm_lock_mountfields);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
/* $NetBSD: chfs_erase.c,v 1.1 2011/11/24 15:51:31 ahoka Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2010 Department of Software Engineering,
|
||||
* University of Szeged, Hungary
|
||||
* Copyright (c) 2010 David Tengeri <dtengeri@inf.u-szeged.hu>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by the Department of Software Engineering, University of Szeged, Hungary
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* chfs_erase.c
|
||||
*
|
||||
* Copyright (C) 2010 David Tengeri <dtengeri@inf.u-szeged.hu>,
|
||||
* ...
|
||||
* University of Szeged, Hungary
|
||||
*/
|
||||
|
||||
#include "chfs.h"
|
||||
|
||||
|
||||
/**
|
||||
* chfs_remap_leb - unmap and then map a leb
|
||||
* @chmp: chfs mount structure
|
||||
*
|
||||
* This function gets an eraseblock from the erasable queue, unmaps it through
|
||||
* EBH and maps another eraseblock to the same LNR.
|
||||
* EBH will find a free eraseblock if any or will erase one if there isn't any
|
||||
* free, just dirty block.
|
||||
*
|
||||
* Returns zero on case of success, errorcode otherwise.
|
||||
*
|
||||
* Needs more brainstorming here.
|
||||
*/
|
||||
int
|
||||
chfs_remap_leb(struct chfs_mount *chmp)
|
||||
{
|
||||
int err;
|
||||
struct chfs_eraseblock *cheb;
|
||||
dbg("chfs_remap_leb\n");
|
||||
uint32_t dirty, unchecked, used, free, wasted;
|
||||
|
||||
//dbg("chmp->chm_nr_erasable_blocks: %d\n", chmp->chm_nr_erasable_blocks);
|
||||
//dbg("ltree: %p ecl: %p\n", &chmp->chm_ebh->ltree_lock, &chmp->chm_lock_sizes);
|
||||
KASSERT(!rw_write_held(&chmp->chm_lock_wbuf));
|
||||
KASSERT(mutex_owned(&chmp->chm_lock_mountfields));
|
||||
KASSERT(mutex_owned(&chmp->chm_lock_sizes));
|
||||
|
||||
if (!chmp->chm_nr_erasable_blocks) {
|
||||
//TODO
|
||||
/* We don't have any erasable blocks, need to check if there are
|
||||
* blocks on erasable_pending_wbuf_queue, flush the data and then
|
||||
* we can remap it.
|
||||
* If there aren't any blocks on that list too, we need to GC?
|
||||
*/
|
||||
if (!TAILQ_EMPTY(&chmp->chm_erasable_pending_wbuf_queue)) {
|
||||
cheb = TAILQ_FIRST(&chmp->chm_erasable_pending_wbuf_queue);
|
||||
TAILQ_REMOVE(&chmp->chm_erasable_pending_wbuf_queue, cheb, queue);
|
||||
if (chmp->chm_wbuf_len) {
|
||||
mutex_exit(&chmp->chm_lock_sizes);
|
||||
chfs_flush_pending_wbuf(chmp);
|
||||
mutex_enter(&chmp->chm_lock_sizes);
|
||||
}
|
||||
TAILQ_INSERT_TAIL(&chmp->chm_erase_pending_queue, cheb, queue);
|
||||
chmp->chm_nr_erasable_blocks++;
|
||||
} else {
|
||||
/* We can't delete any block. */
|
||||
//FIXME should we return ENOSPC?
|
||||
return ENOSPC;
|
||||
}
|
||||
}
|
||||
cheb = TAILQ_FIRST(&chmp->chm_erase_pending_queue);
|
||||
TAILQ_REMOVE(&chmp->chm_erase_pending_queue, cheb, queue);
|
||||
chmp->chm_nr_erasable_blocks--;
|
||||
|
||||
dirty = cheb->dirty_size;
|
||||
unchecked = cheb->unchecked_size;
|
||||
used = cheb->used_size;
|
||||
free = cheb->free_size;
|
||||
wasted = cheb->wasted_size;
|
||||
|
||||
// Free allocated node references for this eraseblock
|
||||
chfs_free_node_refs(cheb);
|
||||
|
||||
err = chfs_unmap_leb(chmp, cheb->lnr);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = chfs_map_leb(chmp, cheb->lnr);
|
||||
if (err)
|
||||
return err;
|
||||
// Reset state to default and change chmp sizes too
|
||||
chfs_change_size_dirty(chmp, cheb, -dirty);
|
||||
chfs_change_size_unchecked(chmp, cheb, -unchecked);
|
||||
chfs_change_size_used(chmp, cheb, -used);
|
||||
chfs_change_size_free(chmp, cheb, chmp->chm_ebh->eb_size - free);
|
||||
chfs_change_size_wasted(chmp, cheb, -wasted);
|
||||
|
||||
KASSERT(cheb->dirty_size == 0);
|
||||
KASSERT(cheb->unchecked_size == 0);
|
||||
KASSERT(cheb->used_size == 0);
|
||||
KASSERT(cheb->free_size == chmp->chm_ebh->eb_size);
|
||||
KASSERT(cheb->wasted_size == 0);
|
||||
|
||||
cheb->first_node = NULL;
|
||||
cheb->last_node = NULL;
|
||||
//put it to free_queue
|
||||
TAILQ_INSERT_TAIL(&chmp->chm_free_queue, cheb, queue);
|
||||
chmp->chm_nr_free_blocks++;
|
||||
dbg("remaped (free: %d, erasable: %d)\n", chmp->chm_nr_free_blocks, chmp->chm_nr_erasable_blocks);
|
||||
KASSERT(!TAILQ_EMPTY(&chmp->chm_free_queue));
|
||||
|
||||
return 0;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,220 @@
|
|||
/* $NetBSD: chfs_ihash.c,v 1.1 2011/11/24 15:51:31 ahoka Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2010 Department of Software Engineering,
|
||||
* University of Szeged, Hungary
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by the Department of Software Engineering, University of Szeged, Hungary
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "chfs.h"
|
||||
/*
|
||||
* Structures associated with inode cacheing.
|
||||
*/
|
||||
static LIST_HEAD(ihashhead, chfs_inode) *chfs_ihashtbl;
|
||||
static u_long chfs_ihash; /* size of hash table - 1 */
|
||||
#define INOHASH(device, inum) (((device) + (inum)) & chfs_ihash)
|
||||
|
||||
kmutex_t chfs_ihash_lock;
|
||||
kmutex_t chfs_hashlock;
|
||||
|
||||
/*
|
||||
* Initialize inode hash table.
|
||||
*/
|
||||
void
|
||||
chfs_ihashinit(void)
|
||||
{
|
||||
dbg("initing\n");
|
||||
|
||||
mutex_init(&chfs_hashlock, MUTEX_DEFAULT, IPL_NONE);
|
||||
mutex_init(&chfs_ihash_lock, MUTEX_DEFAULT, IPL_NONE);
|
||||
chfs_ihashtbl = hashinit(desiredvnodes,
|
||||
HASH_LIST, true, &chfs_ihash);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reinitialize inode hash table.
|
||||
*/
|
||||
|
||||
void
|
||||
chfs_ihashreinit(void)
|
||||
{
|
||||
struct chfs_inode *ip;
|
||||
struct ihashhead *oldhash, *hash;
|
||||
u_long oldmask, mask, val;
|
||||
int i;
|
||||
|
||||
dbg("reiniting\n");
|
||||
|
||||
hash = hashinit(desiredvnodes, HASH_LIST, true, &mask);
|
||||
mutex_enter(&chfs_ihash_lock);
|
||||
oldhash = chfs_ihashtbl;
|
||||
oldmask = chfs_ihash;
|
||||
chfs_ihashtbl = hash;
|
||||
chfs_ihash = mask;
|
||||
for (i = 0; i <= oldmask; i++) {
|
||||
while ((ip = LIST_FIRST(&oldhash[i])) != NULL) {
|
||||
LIST_REMOVE(ip, hash_entry);
|
||||
val = INOHASH(ip->dev, ip->ino);
|
||||
LIST_INSERT_HEAD(&hash[val], ip, hash_entry);
|
||||
}
|
||||
}
|
||||
mutex_exit(&chfs_ihash_lock);
|
||||
hashdone(oldhash, HASH_LIST, oldmask);
|
||||
}
|
||||
|
||||
/*
|
||||
* Free inode hash table.
|
||||
*/
|
||||
void
|
||||
chfs_ihashdone(void)
|
||||
{
|
||||
dbg("destroying\n");
|
||||
|
||||
hashdone(chfs_ihashtbl, HASH_LIST, chfs_ihash);
|
||||
mutex_destroy(&chfs_hashlock);
|
||||
mutex_destroy(&chfs_ihash_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Use the device/inum pair to find the incore inode, and return a pointer
|
||||
* to it. If it is in core, return it, even if it is locked.
|
||||
*/
|
||||
struct vnode *
|
||||
chfs_ihashlookup(dev_t dev, ino_t inum)
|
||||
{
|
||||
struct chfs_inode *ip;
|
||||
struct ihashhead *ipp;
|
||||
|
||||
dbg("dev: %ju, inum: %ju\n", (uintmax_t )dev, (uintmax_t )inum);
|
||||
|
||||
KASSERT(mutex_owned(&chfs_ihash_lock));
|
||||
|
||||
ipp = &chfs_ihashtbl[INOHASH(dev, inum)];
|
||||
LIST_FOREACH(ip, ipp, hash_entry) {
|
||||
if (inum == ip->ino && dev == ip->dev) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ip) {
|
||||
return (ITOV(ip));
|
||||
}
|
||||
|
||||
return (NULLVP);
|
||||
}
|
||||
|
||||
/*
|
||||
* Use the device/inum pair to find the incore inode, and return a pointer
|
||||
* to it. If it is in core, but locked, wait for it.
|
||||
*/
|
||||
struct vnode *
|
||||
chfs_ihashget(dev_t dev, ino_t inum, int flags)
|
||||
{
|
||||
struct ihashhead *ipp;
|
||||
struct chfs_inode *ip;
|
||||
struct vnode *vp;
|
||||
|
||||
dbg("search for ino\n");
|
||||
|
||||
loop:
|
||||
mutex_enter(&chfs_ihash_lock);
|
||||
ipp = &chfs_ihashtbl[INOHASH(dev, inum)];
|
||||
dbg("ipp: %p, chfs_ihashtbl: %p, ihash: %lu\n",
|
||||
ipp, chfs_ihashtbl, chfs_ihash);
|
||||
LIST_FOREACH(ip, ipp, hash_entry) {
|
||||
dbg("ip: %p\n", ip);
|
||||
if (inum == ip->ino && dev == ip->dev) {
|
||||
// printf("chfs_ihashget: found inode: %p\n", ip);
|
||||
vp = ITOV(ip);
|
||||
KASSERT(vp != NULL);
|
||||
//dbg("found\n");
|
||||
if (VOP_ISLOCKED(vp) == LK_EXCLUSIVE) {
|
||||
//dbg("wait for #%llu\n", ip->ino);
|
||||
mutex_exit(&chfs_ihash_lock);
|
||||
goto loop;
|
||||
}
|
||||
/*
|
||||
if (VOP_ISLOCKED(vp))
|
||||
dbg("locked\n");
|
||||
else
|
||||
dbg("isn't locked\n");
|
||||
*/
|
||||
if (flags == 0) {
|
||||
//dbg("no flags\n");
|
||||
mutex_exit(&chfs_ihash_lock);
|
||||
} else {
|
||||
//dbg("vget\n");
|
||||
mutex_enter(vp->v_interlock);
|
||||
mutex_exit(&chfs_ihash_lock);
|
||||
if (vget(vp, flags)) {
|
||||
goto loop;
|
||||
}
|
||||
//dbg("got it\n");
|
||||
}
|
||||
//dbg("return\n");
|
||||
return (vp);
|
||||
}
|
||||
}
|
||||
//dbg("not found\n");
|
||||
mutex_exit(&chfs_ihash_lock);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert the inode into the hash table, and return it locked.
|
||||
*/
|
||||
void
|
||||
chfs_ihashins(struct chfs_inode *ip)
|
||||
{
|
||||
struct ihashhead *ipp;
|
||||
|
||||
dbg("ip: %p\n", ip);
|
||||
|
||||
KASSERT(mutex_owned(&chfs_hashlock));
|
||||
|
||||
/* lock the inode, then put it on the appropriate hash list */
|
||||
VOP_LOCK(ITOV(ip), LK_EXCLUSIVE);
|
||||
|
||||
mutex_enter(&chfs_ihash_lock);
|
||||
ipp = &chfs_ihashtbl[INOHASH(ip->dev, ip->ino)];
|
||||
LIST_INSERT_HEAD(ipp, ip, hash_entry);
|
||||
mutex_exit(&chfs_ihash_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove the inode from the hash table.
|
||||
*/
|
||||
void
|
||||
chfs_ihashrem(struct chfs_inode *ip)
|
||||
{
|
||||
dbg("ip: %p\n", ip);
|
||||
|
||||
mutex_enter(&chfs_ihash_lock);
|
||||
LIST_REMOVE(ip, hash_entry);
|
||||
mutex_exit(&chfs_ihash_lock);
|
||||
}
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
/* $NetBSD: chfs_inode.h,v 1.1 2011/11/24 15:51:31 ahoka Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2010 Department of Software Engineering,
|
||||
* University of Szeged, Hungary
|
||||
* Copyright (C) 2010 Tamas Toth <ttoth@inf.u-szeged.hu>
|
||||
* Copyright (C) 2010 Adam Hoka <ahoka@NetBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by the Department of Software Engineering, University of Szeged, Hungary
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __CHFS_INODE_H__
|
||||
#define __CHFS_INODE_H__
|
||||
|
||||
#include <sys/vnode.h>
|
||||
#include <sys/stat.h>
|
||||
#include <ufs/ufs/ufsmount.h>
|
||||
#include <miscfs/genfs/genfs_node.h>
|
||||
|
||||
struct chfs_inode
|
||||
{
|
||||
kmutex_t inode_lock; /* lock the fields of chfs_inode */
|
||||
|
||||
LIST_ENTRY(chfs_inode) hash_entry; /* Hash chain. */
|
||||
|
||||
struct ufsmount *ump; /* ufs mount - TODO we should remove it */
|
||||
struct chfs_mount *chmp; /* chfs mount point - TODO we should remove it */
|
||||
|
||||
struct vnode *vp; /* vnode associated with this inode */
|
||||
ino_t ino; /* vnode identifier number */
|
||||
|
||||
struct vnode *devvp; /* vnode for block I/O */
|
||||
dev_t dev; /* device associated with the inode */
|
||||
|
||||
struct chfs_vnode_cache *chvc; /* vnode cache of this node */
|
||||
|
||||
struct chfs_dirent *fd; /* full dirent of this node */
|
||||
// struct chfs_dirent *dents; /* directory entries */
|
||||
struct chfs_dirent_list dents;
|
||||
|
||||
struct rb_tree fragtree; /* fragtree of inode */
|
||||
|
||||
uint64_t version; /* version number */
|
||||
//uint64_t highest_version; /* highest vers. num. (used at data nodes) */
|
||||
|
||||
uint32_t mode; /* mode */
|
||||
//int16_t nlink; /* link count */
|
||||
uint64_t size; /* file byte count */
|
||||
uint64_t write_size; /* increasing while write the file out to the flash */
|
||||
uint32_t uid; /* file owner */
|
||||
uint32_t gid; /* file group */
|
||||
uint32_t atime; /* access time */
|
||||
uint32_t mtime; /* modify time */
|
||||
uint32_t ctime; /* creation time */
|
||||
|
||||
uint32_t iflag; /* flags, see below */
|
||||
uint32_t flags; /* status flags (chflags) */
|
||||
|
||||
dev_t rdev; /* used if type is VCHR or VBLK or VFIFO*/
|
||||
char *target; /* used if type is VLNK */
|
||||
};
|
||||
|
||||
/* These flags are kept in chfs_inode->iflag. */
|
||||
#define IN_ACCESS 0x0001 /* Access time update request. */
|
||||
#define IN_CHANGE 0x0002 /* Inode change time update request. */
|
||||
#define IN_UPDATE 0x0004 /* Inode was written to; update mtime. */
|
||||
#define IN_MODIFY 0x2000 /* Modification time update request. */
|
||||
#define IN_MODIFIED 0x0008 /* Inode has been modified. */
|
||||
#define IN_ACCESSED 0x0010 /* Inode has been accessed. */
|
||||
#define IN_RENAME 0x0020 /* Inode is being renamed. */
|
||||
#define IN_SHLOCK 0x0040 /* File has shared lock. */
|
||||
#define IN_EXLOCK 0x0080 /* File has exclusive lock. */
|
||||
#define IN_CLEANING 0x0100 /* LFS: file is being cleaned */
|
||||
#define IN_ADIROP 0x0200 /* LFS: dirop in progress */
|
||||
#define IN_SPACECOUNTED 0x0400 /* Blocks to be freed in free count. */
|
||||
#define IN_PAGING 0x1000 /* LFS: file is on paging queue */
|
||||
|
||||
|
||||
#ifdef VTOI
|
||||
# undef VTOI
|
||||
#endif
|
||||
#ifdef ITOV
|
||||
# undef ITOV
|
||||
#endif
|
||||
|
||||
#define VTOI(vp) ((struct chfs_inode *)(vp)->v_data)
|
||||
#define ITOV(ip) ((ip)->vp)
|
||||
|
||||
/* copied from ufs_dinode.h */
|
||||
#define NDADDR 12 /* Direct addresses in inode. */
|
||||
|
||||
#define ROOTINO ((ino_t)2)
|
||||
|
||||
/* File permissions. */
|
||||
#define IEXEC 0000100 /* Executable. */
|
||||
#define IWRITE 0000200 /* Writable. */
|
||||
#define IREAD 0000400 /* Readable. */
|
||||
#define ISVTX 0001000 /* Sticky bit. */
|
||||
#define ISGID 0002000 /* Set-gid. */
|
||||
#define ISUID 0004000 /* Set-uid. */
|
||||
|
||||
/* File types. */
|
||||
#define IFMT 0170000 /* Mask of file type. */
|
||||
#define IFIFO 0010000 /* Named pipe (fifo). */
|
||||
#define IFCHR 0020000 /* Character device. */
|
||||
#define IFDIR 0040000 /* Directory file. */
|
||||
#define IFBLK 0060000 /* Block device. */
|
||||
#define IFREG 0100000 /* Regular file. */
|
||||
#define IFLNK 0120000 /* Symbolic link. */
|
||||
#define IFSOCK 0140000 /* UNIX domain socket. */
|
||||
#define IFWHT 0160000 /* Whiteout. */
|
||||
|
||||
#endif /* __CHFS_INODE_H__ */
|
|
@ -0,0 +1,396 @@
|
|||
/* $NetBSD: chfs_malloc.c,v 1.1 2011/11/24 15:51:31 ahoka Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2010 Department of Software Engineering,
|
||||
* University of Szeged, Hungary
|
||||
* Copyright (C) 2010 Tamas Toth <ttoth@inf.u-szeged.hu>
|
||||
* Copyright (C) 2010 Adam Hoka <ahoka@NetBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by the Department of Software Engineering, University of Szeged, Hungary
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "chfs.h"
|
||||
#include <sys/pool.h>
|
||||
|
||||
pool_cache_t chfs_vnode_cache;
|
||||
pool_cache_t chfs_nrefs_cache;
|
||||
pool_cache_t chfs_flash_vnode_cache;
|
||||
pool_cache_t chfs_flash_dirent_cache;
|
||||
pool_cache_t chfs_flash_dnode_cache;
|
||||
pool_cache_t chfs_node_frag_cache;
|
||||
pool_cache_t chfs_tmp_dnode_cache;
|
||||
pool_cache_t chfs_tmp_dnode_info_cache;
|
||||
|
||||
int
|
||||
chfs_alloc_pool_caches()
|
||||
{
|
||||
chfs_vnode_cache = pool_cache_init(
|
||||
sizeof(struct chfs_vnode_cache),
|
||||
0, 0, 0, "chfs_vnode_cache", NULL, IPL_NONE, NULL, NULL,
|
||||
NULL);
|
||||
if (!chfs_vnode_cache)
|
||||
goto err_vnode;
|
||||
|
||||
chfs_nrefs_cache = pool_cache_init(
|
||||
(REFS_BLOCK_LEN + 1) * sizeof(struct chfs_node_ref), 0, 0,
|
||||
0, "chfs_nrefs_pool", NULL, IPL_NONE, NULL, NULL, NULL);
|
||||
if (!chfs_nrefs_cache)
|
||||
goto err_nrefs;
|
||||
|
||||
chfs_flash_vnode_cache = pool_cache_init(
|
||||
sizeof(struct chfs_flash_vnode), 0, 0, 0,
|
||||
"chfs_flash_vnode_pool", NULL, IPL_NONE, NULL, NULL, NULL);
|
||||
if (!chfs_flash_vnode_cache)
|
||||
goto err_flash_vnode;
|
||||
|
||||
chfs_flash_dirent_cache = pool_cache_init(
|
||||
sizeof(struct chfs_flash_dirent_node), 0, 0, 0,
|
||||
"chfs_flash_dirent_pool", NULL, IPL_NONE, NULL, NULL, NULL);
|
||||
if (!chfs_flash_dirent_cache)
|
||||
goto err_flash_dirent;
|
||||
|
||||
chfs_flash_dnode_cache = pool_cache_init(
|
||||
sizeof(struct chfs_flash_data_node), 0, 0, 0,
|
||||
"chfs_flash_dnode_pool", NULL, IPL_NONE, NULL, NULL, NULL);
|
||||
if (!chfs_flash_dnode_cache)
|
||||
goto err_flash_dnode;
|
||||
|
||||
chfs_node_frag_cache = pool_cache_init(
|
||||
sizeof(struct chfs_node_frag), 0, 0, 0,
|
||||
"chfs_node_frag_pool", NULL, IPL_NONE, NULL, NULL, NULL);
|
||||
if (!chfs_node_frag_cache)
|
||||
goto err_node_frag;
|
||||
|
||||
chfs_tmp_dnode_cache = pool_cache_init(
|
||||
sizeof(struct chfs_tmp_dnode), 0, 0, 0,
|
||||
"chfs_tmp_dnode_pool", NULL, IPL_NONE, NULL, NULL, NULL);
|
||||
if (!chfs_tmp_dnode_cache)
|
||||
goto err_tmp_dnode;
|
||||
|
||||
chfs_tmp_dnode_info_cache = pool_cache_init(
|
||||
sizeof(struct chfs_tmp_dnode_info), 0, 0, 0,
|
||||
"chfs_tmp_dnode_info_pool", NULL, IPL_NONE, NULL, NULL, NULL);
|
||||
if (!chfs_tmp_dnode_info_cache)
|
||||
goto err_tmp_dnode_info;
|
||||
|
||||
return 0;
|
||||
|
||||
err_tmp_dnode_info:
|
||||
pool_cache_destroy(chfs_tmp_dnode_cache);
|
||||
err_tmp_dnode:
|
||||
pool_cache_destroy(chfs_node_frag_cache);
|
||||
err_node_frag:
|
||||
pool_cache_destroy(chfs_flash_dnode_cache);
|
||||
err_flash_dnode:
|
||||
pool_cache_destroy(chfs_flash_dirent_cache);
|
||||
err_flash_dirent:
|
||||
pool_cache_destroy(chfs_flash_vnode_cache);
|
||||
err_flash_vnode:
|
||||
pool_cache_destroy(chfs_nrefs_cache);
|
||||
err_nrefs:
|
||||
pool_cache_destroy(chfs_vnode_cache);
|
||||
err_vnode:
|
||||
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
void
|
||||
chfs_destroy_pool_caches()
|
||||
{
|
||||
if (chfs_vnode_cache)
|
||||
pool_cache_destroy(chfs_vnode_cache);
|
||||
|
||||
if (chfs_nrefs_cache)
|
||||
pool_cache_destroy(chfs_nrefs_cache);
|
||||
|
||||
if (chfs_flash_vnode_cache)
|
||||
pool_cache_destroy(chfs_flash_vnode_cache);
|
||||
|
||||
if (chfs_flash_dirent_cache)
|
||||
pool_cache_destroy(chfs_flash_dirent_cache);
|
||||
|
||||
if (chfs_flash_dnode_cache)
|
||||
pool_cache_destroy(chfs_flash_dnode_cache);
|
||||
|
||||
if (chfs_node_frag_cache)
|
||||
pool_cache_destroy(chfs_node_frag_cache);
|
||||
|
||||
if (chfs_tmp_dnode_cache)
|
||||
pool_cache_destroy(chfs_tmp_dnode_cache);
|
||||
|
||||
if (chfs_tmp_dnode_info_cache)
|
||||
pool_cache_destroy(chfs_tmp_dnode_info_cache);
|
||||
}
|
||||
|
||||
struct chfs_vnode_cache *
|
||||
chfs_vnode_cache_alloc(ino_t vno)
|
||||
{
|
||||
struct chfs_vnode_cache* vc;
|
||||
vc = pool_cache_get(chfs_vnode_cache, PR_WAITOK);
|
||||
|
||||
memset(vc, 0, sizeof(*vc));
|
||||
vc->vno = vno;
|
||||
vc->v = (void *)vc;
|
||||
vc->dirents = (void *)vc;
|
||||
vc->dnode = (void *)vc;
|
||||
TAILQ_INIT(&vc->scan_dirents);
|
||||
vc->highest_version = 0;
|
||||
|
||||
return vc;
|
||||
}
|
||||
|
||||
void
|
||||
chfs_vnode_cache_free(struct chfs_vnode_cache *vc)
|
||||
{
|
||||
//kmem_free(vc->vno_version, sizeof(uint64_t));
|
||||
pool_cache_put(chfs_vnode_cache, vc);
|
||||
}
|
||||
|
||||
/**
|
||||
* chfs_alloc_refblock - allocating a refblock
|
||||
*
|
||||
* Returns a pointer of the first element in the block.
|
||||
*
|
||||
* We are not allocating just one node ref, instead we allocating REFS_BLOCK_LEN
|
||||
* number of node refs, the last element will be a pointer to the next block.
|
||||
* We do this, because we need a chain of nodes which have been ordered by the
|
||||
* physical address of them.
|
||||
*
|
||||
*/
|
||||
struct chfs_node_ref*
|
||||
chfs_alloc_refblock(void)
|
||||
{
|
||||
int i;
|
||||
struct chfs_node_ref *nref;
|
||||
nref = pool_cache_get(chfs_nrefs_cache, PR_WAITOK);
|
||||
|
||||
for (i = 0; i < REFS_BLOCK_LEN; i++) {
|
||||
nref[i].nref_lnr = REF_EMPTY_NODE;
|
||||
nref[i].nref_next = NULL;
|
||||
}
|
||||
i = REFS_BLOCK_LEN;
|
||||
nref[i].nref_lnr = REF_LINK_TO_NEXT;
|
||||
nref[i].nref_next = NULL;
|
||||
|
||||
return nref;
|
||||
}
|
||||
|
||||
/**
|
||||
* chfs_free_refblock - freeing a refblock
|
||||
*/
|
||||
void
|
||||
chfs_free_refblock(struct chfs_node_ref *nref)
|
||||
{
|
||||
pool_cache_put(chfs_nrefs_cache, nref);
|
||||
}
|
||||
|
||||
/**
|
||||
* chfs_alloc_node_ref - allocating a node ref from a refblock
|
||||
* @cheb: eraseblock information structure
|
||||
*
|
||||
* Allocating a node ref from a refblock, it there isn't any free element in the
|
||||
* block, a new block will be allocated and be linked to the current block.
|
||||
*/
|
||||
struct chfs_node_ref*
|
||||
chfs_alloc_node_ref(struct chfs_eraseblock *cheb)
|
||||
{
|
||||
struct chfs_node_ref *nref, *new, *old;
|
||||
old = cheb->last_node;
|
||||
nref = cheb->last_node;
|
||||
|
||||
if (!nref) {
|
||||
//There haven't been any nref allocated for this block yet
|
||||
nref = chfs_alloc_refblock();
|
||||
|
||||
cheb->first_node = nref;
|
||||
cheb->last_node = nref;
|
||||
nref->nref_lnr = cheb->lnr;
|
||||
KASSERT(cheb->lnr == nref->nref_lnr);
|
||||
|
||||
return nref;
|
||||
}
|
||||
|
||||
nref++;
|
||||
if (nref->nref_lnr == REF_LINK_TO_NEXT) {
|
||||
new = chfs_alloc_refblock();
|
||||
nref->nref_next = new;
|
||||
nref = new;
|
||||
}
|
||||
|
||||
cheb->last_node = nref;
|
||||
nref->nref_lnr = cheb->lnr;
|
||||
|
||||
KASSERT(old->nref_lnr == nref->nref_lnr &&
|
||||
nref->nref_lnr == cheb->lnr);
|
||||
|
||||
return nref;
|
||||
}
|
||||
|
||||
/**
|
||||
* chfs_free_node_refs - freeing an eraseblock's node refs
|
||||
* @cheb: eraseblock information structure
|
||||
*/
|
||||
void
|
||||
chfs_free_node_refs(struct chfs_eraseblock *cheb)
|
||||
{
|
||||
struct chfs_node_ref *nref, *block;
|
||||
|
||||
block = nref = cheb->first_node;
|
||||
|
||||
while (nref) {
|
||||
if (nref->nref_lnr == REF_LINK_TO_NEXT) {
|
||||
nref = nref->nref_next;
|
||||
chfs_free_refblock(block);
|
||||
block = nref;
|
||||
continue;
|
||||
}
|
||||
nref++;
|
||||
}
|
||||
}
|
||||
|
||||
struct chfs_dirent*
|
||||
chfs_alloc_dirent(int namesize)
|
||||
{
|
||||
struct chfs_dirent *ret;
|
||||
size_t size = sizeof(struct chfs_dirent) + namesize;
|
||||
|
||||
ret = kmem_alloc(size, KM_SLEEP);
|
||||
//ret->alloc_size = size;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
chfs_free_dirent(struct chfs_dirent *dirent)
|
||||
{
|
||||
//size_t size = dirent->alloc_size;
|
||||
size_t size = sizeof(struct chfs_dirent) + dirent->nsize + 1;
|
||||
|
||||
kmem_free(dirent, size);
|
||||
}
|
||||
|
||||
struct chfs_full_dnode*
|
||||
chfs_alloc_full_dnode()
|
||||
{
|
||||
struct chfs_full_dnode *ret;
|
||||
ret = kmem_alloc(sizeof(struct chfs_full_dnode), KM_SLEEP);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
chfs_free_full_dnode(struct chfs_full_dnode *fd)
|
||||
{
|
||||
kmem_free(fd,(sizeof(struct chfs_full_dnode)));
|
||||
}
|
||||
|
||||
struct chfs_flash_vnode*
|
||||
chfs_alloc_flash_vnode()
|
||||
{
|
||||
struct chfs_flash_vnode *ret;
|
||||
ret = pool_cache_get(chfs_flash_vnode_cache, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
chfs_free_flash_vnode(struct chfs_flash_vnode *fvnode)
|
||||
{
|
||||
pool_cache_put(chfs_flash_vnode_cache, fvnode);
|
||||
}
|
||||
|
||||
struct chfs_flash_dirent_node*
|
||||
chfs_alloc_flash_dirent()
|
||||
{
|
||||
struct chfs_flash_dirent_node *ret;
|
||||
ret = pool_cache_get(chfs_flash_dirent_cache, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
chfs_free_flash_dirent(struct chfs_flash_dirent_node *fdnode)
|
||||
{
|
||||
pool_cache_put(chfs_flash_dirent_cache, fdnode);
|
||||
}
|
||||
|
||||
struct chfs_flash_data_node*
|
||||
chfs_alloc_flash_dnode()
|
||||
{
|
||||
struct chfs_flash_data_node *ret;
|
||||
ret = pool_cache_get(chfs_flash_dnode_cache, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
chfs_free_flash_dnode(struct chfs_flash_data_node *fdnode)
|
||||
{
|
||||
pool_cache_put(chfs_flash_dnode_cache, fdnode);
|
||||
}
|
||||
|
||||
|
||||
struct chfs_node_frag*
|
||||
chfs_alloc_node_frag()
|
||||
{
|
||||
struct chfs_node_frag *ret;
|
||||
ret = pool_cache_get(chfs_node_frag_cache, 0);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
chfs_free_node_frag(struct chfs_node_frag *frag)
|
||||
{
|
||||
pool_cache_put(chfs_node_frag_cache, frag);
|
||||
}
|
||||
|
||||
struct chfs_tmp_dnode *
|
||||
chfs_alloc_tmp_dnode()
|
||||
{
|
||||
struct chfs_tmp_dnode *ret;
|
||||
ret = pool_cache_get(chfs_tmp_dnode_cache, 0);
|
||||
ret->next = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
chfs_free_tmp_dnode(struct chfs_tmp_dnode *td)
|
||||
{
|
||||
pool_cache_put(chfs_tmp_dnode_cache, td);
|
||||
}
|
||||
|
||||
struct chfs_tmp_dnode_info *
|
||||
chfs_alloc_tmp_dnode_info()
|
||||
{
|
||||
struct chfs_tmp_dnode_info *ret;
|
||||
ret = pool_cache_get(chfs_tmp_dnode_info_cache, 0);
|
||||
ret->tmpnode = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
chfs_free_tmp_dnode_info(struct chfs_tmp_dnode_info *di)
|
||||
{
|
||||
pool_cache_put(chfs_tmp_dnode_info_cache, di);
|
||||
}
|
||||
|
|
@ -0,0 +1,570 @@
|
|||
/* $NetBSD: chfs_nodeops.c,v 1.1 2011/11/24 15:51:31 ahoka Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2010 Department of Software Engineering,
|
||||
* University of Szeged, Hungary
|
||||
* Copyright (C) 2010 David Tengeri <dtengeri@inf.u-szeged.hu>
|
||||
* Copyright (C) 2010 Tamas Toth <ttoth@inf.u-szeged.hu>
|
||||
* Copyright (C) 2010 Adam Hoka <ahoka@NetBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by the Department of Software Engineering, University of Szeged, Hungary
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "chfs.h"
|
||||
|
||||
/**
|
||||
* chfs_update_eb_dirty - updates dirty and free space, first and
|
||||
* last node references
|
||||
* @sbi: CHFS main descriptor structure
|
||||
* @cheb: eraseblock to update
|
||||
* @size: increase dirty space size with this
|
||||
* Returns zero in case of success, %1 in case of fail.
|
||||
*/
|
||||
int
|
||||
chfs_update_eb_dirty(struct chfs_mount *chmp,
|
||||
struct chfs_eraseblock *cheb, uint32_t size)
|
||||
{
|
||||
KASSERT(mutex_owned(&chmp->chm_lock_mountfields));
|
||||
KASSERT(!mutex_owned(&chmp->chm_lock_sizes));
|
||||
|
||||
if (!size)
|
||||
return 0;
|
||||
|
||||
if (size > cheb->free_size) {
|
||||
chfs_err("free_size (%d) is less then dirty space (%d) "
|
||||
"on block (%d)\n", cheb->free_size, size, cheb->lnr);
|
||||
return 1;
|
||||
}
|
||||
mutex_enter(&chmp->chm_lock_sizes);
|
||||
//dbg("BEFORE: free_size: %d\n", cheb->free_size);
|
||||
chfs_change_size_free(chmp, cheb, -size);
|
||||
chfs_change_size_dirty(chmp, cheb, size);
|
||||
//dbg(" AFTER: free_size: %d\n", cheb->free_size);
|
||||
mutex_exit(&chmp->chm_lock_sizes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* chfs_add_node_to_list - adds a data node ref to vnode cache's dnode list
|
||||
* @sbi: super block informations
|
||||
* @new: node ref to insert
|
||||
* @list: head of the list
|
||||
* This function inserts a data node ref to the list of vnode cache.
|
||||
* The list is sorted by data node's lnr and offset.
|
||||
*/
|
||||
void
|
||||
chfs_add_node_to_list(struct chfs_mount *chmp,
|
||||
struct chfs_vnode_cache *vc,
|
||||
struct chfs_node_ref *new, struct chfs_node_ref **list)
|
||||
{
|
||||
struct chfs_node_ref *nextref = *list;
|
||||
struct chfs_node_ref *prevref = NULL;
|
||||
|
||||
while (nextref && nextref != (struct chfs_node_ref *)vc &&
|
||||
(nextref->nref_lnr <= new->nref_lnr)) {
|
||||
if (nextref->nref_lnr == new->nref_lnr) {
|
||||
while (nextref && nextref !=
|
||||
(struct chfs_node_ref *)vc &&
|
||||
(CHFS_GET_OFS(nextref->nref_offset) <
|
||||
CHFS_GET_OFS(new->nref_offset))) {
|
||||
prevref = nextref;
|
||||
nextref = nextref->nref_next;
|
||||
}
|
||||
break;
|
||||
}
|
||||
prevref = nextref;
|
||||
nextref = nextref->nref_next;
|
||||
}
|
||||
|
||||
if (nextref && nextref != (struct chfs_node_ref *)vc &&
|
||||
nextref->nref_lnr == new->nref_lnr &&
|
||||
CHFS_GET_OFS(nextref->nref_offset) ==
|
||||
CHFS_GET_OFS(new->nref_offset)) {
|
||||
new->nref_next = nextref->nref_next;
|
||||
} else {
|
||||
new->nref_next = nextref;
|
||||
}
|
||||
|
||||
if (prevref) {
|
||||
prevref->nref_next = new;
|
||||
} else {
|
||||
*list = new;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
chfs_add_fd_to_inode(struct chfs_mount *chmp,
|
||||
struct chfs_inode *parent, struct chfs_dirent *new)
|
||||
{
|
||||
// struct chfs_dirent **prev = &parent->dents;
|
||||
struct chfs_dirent *fd, *tmpfd;
|
||||
|
||||
if (new->version > parent->chvc->highest_version) {
|
||||
parent->chvc->highest_version = new->version;
|
||||
}
|
||||
|
||||
//mutex_enter(&parent->inode_lock);
|
||||
TAILQ_FOREACH_SAFE(fd, &parent->dents, fds, tmpfd) {
|
||||
if (fd->nhash > new->nhash) {
|
||||
/* insert new before fd */
|
||||
TAILQ_INSERT_BEFORE(fd, new, fds);
|
||||
return;
|
||||
} else if (fd->nhash == new->nhash &&
|
||||
!strcmp(fd->name, new->name)) {
|
||||
if (new->version > fd->version) {
|
||||
// new->next = fd->next;
|
||||
/* replace fd with new */
|
||||
TAILQ_INSERT_BEFORE(fd, new, fds);
|
||||
TAILQ_REMOVE(&parent->dents, fd, fds);
|
||||
if (fd->nref) {
|
||||
chfs_mark_node_obsolete(chmp,
|
||||
fd->nref);
|
||||
}
|
||||
chfs_free_dirent(fd);
|
||||
// *prev = new;//XXX
|
||||
} else {
|
||||
chfs_mark_node_obsolete(chmp, new->nref);
|
||||
chfs_free_dirent(new);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* if we couldnt fit it elsewhere, lets add to the end */
|
||||
/* FIXME insert tail or insert head? */
|
||||
TAILQ_INSERT_HEAD(&parent->dents, new, fds);
|
||||
//mutex_exit(&parent->inode_lock);
|
||||
#if 0
|
||||
while ((*prev) && (*prev)->nhash <= new->nhash) {
|
||||
if ((*prev)->nhash == new->nhash &&
|
||||
!strcmp((*prev)->name, new->name)) {
|
||||
if (new->version > (*prev)->version) {
|
||||
new->next = (*prev)->next;
|
||||
if ((*prev)->nref) {
|
||||
chfs_mark_node_obsolete(chmp,
|
||||
(*prev)->nref);
|
||||
}
|
||||
chfs_free_dirent(*prev);
|
||||
*prev = new;
|
||||
} else {
|
||||
chfs_mark_node_obsolete(chmp, new->nref);
|
||||
chfs_free_dirent(new);
|
||||
}
|
||||
return;
|
||||
}
|
||||
prev = &((*prev)->next);
|
||||
}
|
||||
|
||||
new->next = *prev;
|
||||
*prev = new;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
chfs_add_vnode_ref_to_vc(struct chfs_mount *chmp,
|
||||
struct chfs_vnode_cache *vc, struct chfs_node_ref *new)
|
||||
{
|
||||
if ((struct chfs_vnode_cache*)(vc->v) != vc) {
|
||||
chfs_mark_node_obsolete(chmp, vc->v);
|
||||
new->nref_next = vc->v->nref_next;
|
||||
} else {
|
||||
new->nref_next = vc->v;
|
||||
}
|
||||
vc->v = new;
|
||||
}
|
||||
|
||||
struct chfs_node_ref *
|
||||
chfs_nref_next(struct chfs_node_ref *nref)
|
||||
{
|
||||
// dbg("check nref: %u - %u\n", nref->nref_lnr, nref->nref_offset);
|
||||
nref++;
|
||||
// dbg("next nref: %u - %u\n", nref->nref_lnr, nref->nref_offset);
|
||||
if (nref->nref_lnr == REF_LINK_TO_NEXT) {
|
||||
//End of chain
|
||||
if (!nref->nref_next)
|
||||
return NULL;
|
||||
|
||||
nref = nref->nref_next;
|
||||
}
|
||||
//end of chain
|
||||
if (nref->nref_lnr == REF_EMPTY_NODE)
|
||||
return NULL;
|
||||
|
||||
return nref;
|
||||
}
|
||||
|
||||
int
|
||||
chfs_nref_len(struct chfs_mount *chmp,
|
||||
struct chfs_eraseblock *cheb, struct chfs_node_ref *nref)
|
||||
{
|
||||
struct chfs_node_ref *next;
|
||||
|
||||
KASSERT(mutex_owned(&chmp->chm_lock_mountfields));
|
||||
|
||||
if (!cheb)
|
||||
cheb = &chmp->chm_blocks[nref->nref_lnr];
|
||||
|
||||
next = chfs_nref_next(nref);
|
||||
|
||||
if (!next) {
|
||||
//dbg("next null\n");
|
||||
return chmp->chm_ebh->eb_size - cheb->free_size -
|
||||
CHFS_GET_OFS(nref->nref_offset);
|
||||
}
|
||||
//dbg("size: %d\n", CHFS_GET_OFS(next->nref_offset) - CHFS_GET_OFS(nref->nref_offset));
|
||||
return CHFS_GET_OFS(next->nref_offset) -
|
||||
CHFS_GET_OFS(nref->nref_offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* chfs_mark_node_obsolete - marks a node obsolete
|
||||
*/
|
||||
void
|
||||
chfs_mark_node_obsolete(struct chfs_mount *chmp,
|
||||
struct chfs_node_ref *nref)
|
||||
{
|
||||
int len;
|
||||
struct chfs_eraseblock *cheb;
|
||||
|
||||
KASSERT(mutex_owned(&chmp->chm_lock_mountfields));
|
||||
|
||||
KASSERT(!CHFS_REF_OBSOLETE(nref));
|
||||
|
||||
KASSERT(nref->nref_lnr <= chmp->chm_ebh->peb_nr);
|
||||
cheb = &chmp->chm_blocks[nref->nref_lnr];
|
||||
|
||||
#ifdef DIAGNOSTIC
|
||||
if (cheb->used_size + cheb->free_size + cheb->dirty_size +
|
||||
cheb->unchecked_size + cheb->wasted_size != chmp->chm_ebh->eb_size) {
|
||||
dbg("eraseblock leak detected!\nused: %u\nfree: %u\n"
|
||||
"dirty: %u\nunchecked: %u\nwasted: %u\ntotal: %u\nshould be: %zu\n",
|
||||
cheb->used_size, cheb->free_size, cheb->dirty_size,
|
||||
cheb->unchecked_size, cheb->wasted_size, cheb->used_size + cheb->free_size +
|
||||
cheb->dirty_size + cheb->unchecked_size + cheb->wasted_size,
|
||||
chmp->chm_ebh->eb_size);
|
||||
}
|
||||
#endif
|
||||
|
||||
len = chfs_nref_len(chmp, cheb, nref);
|
||||
//dbg("len: %u\n", len);
|
||||
//dbg("1. used: %u\n", cheb->used_size);
|
||||
|
||||
mutex_enter(&chmp->chm_lock_sizes);
|
||||
|
||||
if (CHFS_REF_FLAGS(nref) == CHFS_UNCHECKED_NODE_MASK) {
|
||||
//dbg("UNCHECKED mark an unchecked node\n");
|
||||
chfs_change_size_unchecked(chmp, cheb, -len);
|
||||
//dbg("unchecked: %u\n", chmp->chm_unchecked_size);
|
||||
} else {
|
||||
chfs_change_size_used(chmp, cheb, -len);
|
||||
|
||||
//dbg("2. used: %u\n", cheb->used_size);
|
||||
KASSERT(cheb->used_size <= chmp->chm_ebh->eb_size);
|
||||
}
|
||||
chfs_change_size_dirty(chmp, cheb, len);
|
||||
|
||||
#ifdef DIAGNOSTIC
|
||||
if (cheb->used_size + cheb->free_size + cheb->dirty_size +
|
||||
cheb->unchecked_size + cheb->wasted_size != chmp->chm_ebh->eb_size) {
|
||||
panic("eraseblock leak detected!\nused: %u\nfree: %u\n"
|
||||
"dirty: %u\nunchecked: %u\nwasted: %u\ntotal: %u\nshould be: %zu\n",
|
||||
cheb->used_size, cheb->free_size, cheb->dirty_size,
|
||||
cheb->unchecked_size, cheb->wasted_size, cheb->used_size + cheb->free_size +
|
||||
cheb->dirty_size + cheb->unchecked_size + cheb->wasted_size,
|
||||
chmp->chm_ebh->eb_size);
|
||||
}
|
||||
#endif
|
||||
nref->nref_offset = CHFS_GET_OFS(nref->nref_offset) |
|
||||
CHFS_OBSOLETE_NODE_MASK;
|
||||
|
||||
if (chmp->chm_flags & CHFS_MP_FLAG_SCANNING) {
|
||||
/*Scan is in progress, do nothing now*/
|
||||
mutex_exit(&chmp->chm_lock_sizes);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cheb == chmp->chm_nextblock) {
|
||||
dbg("Not moving nextblock to dirty/erase_pending list\n");
|
||||
} else if (!cheb->used_size && !cheb->unchecked_size) {
|
||||
if (cheb == chmp->chm_gcblock) {
|
||||
dbg("gcblock is completely dirtied\n");
|
||||
chmp->chm_gcblock = NULL;
|
||||
} else {
|
||||
//remove from a tailq, but we don't know which tailq contains this cheb
|
||||
//so we remove it from the dirty list now
|
||||
//TAILQ_REMOVE(&chmp->chm_dirty_queue, cheb, queue);
|
||||
int removed = 0;
|
||||
struct chfs_eraseblock *eb, *tmpeb;
|
||||
//XXX ugly code
|
||||
TAILQ_FOREACH_SAFE(eb, &chmp->chm_free_queue, queue, tmpeb) {
|
||||
if (eb == cheb) {
|
||||
TAILQ_REMOVE(&chmp->chm_free_queue, cheb, queue);
|
||||
removed = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (removed == 0) {
|
||||
TAILQ_FOREACH_SAFE(eb, &chmp->chm_dirty_queue, queue, tmpeb) {
|
||||
if (eb == cheb) {
|
||||
TAILQ_REMOVE(&chmp->chm_dirty_queue, cheb, queue);
|
||||
removed = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (removed == 0) {
|
||||
TAILQ_FOREACH_SAFE(eb, &chmp->chm_very_dirty_queue, queue, tmpeb) {
|
||||
if (eb == cheb) {
|
||||
TAILQ_REMOVE(&chmp->chm_very_dirty_queue, cheb, queue);
|
||||
removed = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (removed == 0) {
|
||||
TAILQ_FOREACH_SAFE(eb, &chmp->chm_clean_queue, queue, tmpeb) {
|
||||
if (eb == cheb) {
|
||||
TAILQ_REMOVE(&chmp->chm_clean_queue, cheb, queue);
|
||||
removed = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (chmp->chm_wbuf_len) {
|
||||
dbg("Adding block to erasable pending wbuf queue\n");
|
||||
TAILQ_INSERT_TAIL(&chmp->chm_erasable_pending_wbuf_queue,
|
||||
cheb, queue);
|
||||
} else {
|
||||
TAILQ_INSERT_TAIL(&chmp->chm_erase_pending_queue,
|
||||
cheb, queue);
|
||||
chmp->chm_nr_erasable_blocks++;
|
||||
}
|
||||
chfs_remap_leb(chmp);
|
||||
} else if (cheb == chmp->chm_gcblock) {
|
||||
dbg("Not moving gcblock to dirty list\n");
|
||||
} else if (cheb->dirty_size > MAX_DIRTY_TO_CLEAN &&
|
||||
cheb->dirty_size - len <= MAX_DIRTY_TO_CLEAN) {
|
||||
dbg("Freshly dirtied, remove it from clean queue and "
|
||||
"add it to dirty\n");
|
||||
TAILQ_REMOVE(&chmp->chm_clean_queue, cheb, queue);
|
||||
TAILQ_INSERT_TAIL(&chmp->chm_dirty_queue, cheb, queue);
|
||||
} else if (VERY_DIRTY(chmp, cheb->dirty_size) &&
|
||||
!VERY_DIRTY(chmp, cheb->dirty_size - len)) {
|
||||
dbg("Becomes now very dirty, remove it from dirty "
|
||||
"queue and add it to very dirty\n");
|
||||
TAILQ_REMOVE(&chmp->chm_dirty_queue, cheb, queue);
|
||||
TAILQ_INSERT_TAIL(&chmp->chm_very_dirty_queue, cheb, queue);
|
||||
} else {
|
||||
dbg("Leave cheb where it is\n");
|
||||
}
|
||||
mutex_exit(&chmp->chm_lock_sizes);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* chfs_close_eraseblock - close an eraseblock
|
||||
* @chmp: chfs mount structure
|
||||
* @cheb: eraseblock informations
|
||||
*
|
||||
* This function close the physical chain of the nodes on the eraseblock,
|
||||
* convert its free size to dirty and add it to clean, dirty or very dirty list.
|
||||
*/
|
||||
int
|
||||
chfs_close_eraseblock(struct chfs_mount *chmp,
|
||||
struct chfs_eraseblock *cheb)
|
||||
{
|
||||
uint32_t offset;
|
||||
struct chfs_node_ref *nref;
|
||||
|
||||
KASSERT(mutex_owned(&chmp->chm_lock_mountfields));
|
||||
|
||||
offset = chmp->chm_ebh->eb_size - cheb->free_size;
|
||||
|
||||
// Close the chain
|
||||
nref = chfs_alloc_node_ref(cheb);
|
||||
if (!nref)
|
||||
return ENOMEM;
|
||||
|
||||
nref->nref_next = NULL;
|
||||
nref->nref_offset = offset;
|
||||
|
||||
// Mark space as dirty
|
||||
chfs_update_eb_dirty(chmp, cheb, cheb->free_size);
|
||||
|
||||
if (cheb->dirty_size < MAX_DIRTY_TO_CLEAN) {
|
||||
TAILQ_INSERT_TAIL(&chmp->chm_clean_queue, cheb, queue);
|
||||
} else if (VERY_DIRTY(chmp, cheb->dirty_size)) {
|
||||
TAILQ_INSERT_TAIL(&chmp->chm_very_dirty_queue, cheb, queue);
|
||||
} else {
|
||||
TAILQ_INSERT_TAIL(&chmp->chm_dirty_queue, cheb, queue);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
chfs_reserve_space_normal(struct chfs_mount *chmp, uint32_t size, int prio)
|
||||
{
|
||||
int ret;
|
||||
|
||||
KASSERT(mutex_owned(&chmp->chm_lock_mountfields));
|
||||
|
||||
mutex_enter(&chmp->chm_lock_sizes);
|
||||
while (chmp->chm_nr_free_blocks + chmp->chm_nr_erasable_blocks < chmp->chm_resv_blocks_write) {
|
||||
dbg("free: %d, erasable: %d, resv: %d\n", chmp->chm_nr_free_blocks, chmp->chm_nr_erasable_blocks, chmp->chm_resv_blocks_write);
|
||||
uint32_t avail, dirty;
|
||||
if (prio == ALLOC_DELETION && chmp->chm_nr_free_blocks + chmp->chm_nr_erasable_blocks >= chmp->chm_resv_blocks_deletion)
|
||||
break;
|
||||
|
||||
dirty = chmp->chm_dirty_size - chmp->chm_nr_erasable_blocks * chmp->chm_ebh->eb_size + chmp->chm_unchecked_size;
|
||||
if (dirty < chmp->chm_nospc_dirty) {
|
||||
dbg("dirty: %u < nospc_dirty: %u\n", dirty, chmp->chm_nospc_dirty);
|
||||
ret = ENOSPC;
|
||||
mutex_exit(&chmp->chm_lock_sizes);
|
||||
goto out;
|
||||
}
|
||||
|
||||
avail = chmp->chm_free_size - (chmp->chm_resv_blocks_write * chmp->chm_ebh->eb_size);
|
||||
if (size > avail) {
|
||||
dbg("size: %u > avail: %u\n", size, avail);
|
||||
ret = ENOSPC;
|
||||
mutex_exit(&chmp->chm_lock_sizes);
|
||||
goto out;
|
||||
}
|
||||
|
||||
mutex_exit(&chmp->chm_lock_sizes);
|
||||
ret = chfs_gcollect_pass(chmp);
|
||||
/* gcollect_pass exits chm_lock_mountfields */
|
||||
mutex_enter(&chmp->chm_lock_mountfields);
|
||||
mutex_enter(&chmp->chm_lock_sizes);
|
||||
|
||||
if (chmp->chm_nr_erasable_blocks ||
|
||||
!TAILQ_EMPTY(&chmp->chm_erasable_pending_wbuf_queue) ||
|
||||
ret == EAGAIN) {
|
||||
ret = chfs_remap_leb(chmp);
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
mutex_exit(&chmp->chm_lock_sizes);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_exit(&chmp->chm_lock_sizes);
|
||||
ret = chfs_reserve_space(chmp, size);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
chfs_reserve_space_gc(struct chfs_mount *chmp, uint32_t size)
|
||||
{
|
||||
int ret;
|
||||
|
||||
KASSERT(mutex_owned(&chmp->chm_lock_mountfields));
|
||||
|
||||
mutex_enter(&chmp->chm_lock_sizes);
|
||||
chfs_remap_leb(chmp);
|
||||
|
||||
if (size > chmp->chm_free_size) {
|
||||
dbg("size: %u\n", size);
|
||||
mutex_exit(&chmp->chm_lock_sizes);
|
||||
return ENOSPC;
|
||||
}
|
||||
|
||||
mutex_exit(&chmp->chm_lock_sizes);
|
||||
ret = chfs_reserve_space(chmp, size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* chfs_reserve_space - finds a block which free size is >= requested size
|
||||
* @chmp: chfs mount point
|
||||
* @size: requested size
|
||||
* @len: reserved spaced will be returned in this variable;
|
||||
* Returns zero in case of success, error code in case of fail.
|
||||
*/
|
||||
int
|
||||
chfs_reserve_space(struct chfs_mount *chmp, uint32_t size)
|
||||
{
|
||||
//TODO define minimum reserved blocks, which is needed for writing
|
||||
//TODO check we have enough free blocks to write
|
||||
//TODO if no: need erase and GC
|
||||
|
||||
int err;
|
||||
struct chfs_eraseblock *cheb;
|
||||
|
||||
KASSERT(mutex_owned(&chmp->chm_lock_mountfields));
|
||||
KASSERT(!mutex_owned(&chmp->chm_lock_sizes));
|
||||
|
||||
cheb = chmp->chm_nextblock;
|
||||
//if (cheb)
|
||||
//dbg("cheb->free_size %u\n", cheb->free_size);
|
||||
if (cheb && size > cheb->free_size) {
|
||||
dbg("size: %u > free_size: %u\n", size, cheb->free_size);
|
||||
/*
|
||||
* There isn't enough space on this eraseblock, we mark this as
|
||||
* dirty and close the physical chain of the node refs.
|
||||
*/
|
||||
//Write out pending data if any
|
||||
if (chmp->chm_wbuf_len) {
|
||||
chfs_flush_pending_wbuf(chmp);
|
||||
//FIXME need goto restart here?
|
||||
}
|
||||
|
||||
while (chmp->chm_wbuf_ofs < chmp->chm_ebh->eb_size) {
|
||||
dbg("wbuf ofs: %zu - eb_size: %zu\n",
|
||||
chmp->chm_wbuf_ofs, chmp->chm_ebh->eb_size);
|
||||
chfs_flush_pending_wbuf(chmp);
|
||||
}
|
||||
|
||||
if (!(chmp->chm_wbuf_ofs % chmp->chm_ebh->eb_size) && !chmp->chm_wbuf_len)
|
||||
chmp->chm_wbuf_ofs = 0xffffffff;
|
||||
|
||||
err = chfs_close_eraseblock(chmp, cheb);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
cheb = NULL;
|
||||
}
|
||||
if (!cheb) {
|
||||
//get a block for nextblock
|
||||
if (TAILQ_EMPTY(&chmp->chm_free_queue)) {
|
||||
// If this succeeds there will be a block on free_queue
|
||||
dbg("cheb remap (free: %d)\n", chmp->chm_nr_free_blocks);
|
||||
err = chfs_remap_leb(chmp);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
cheb = TAILQ_FIRST(&chmp->chm_free_queue);
|
||||
TAILQ_REMOVE(&chmp->chm_free_queue, cheb, queue);
|
||||
chmp->chm_nextblock = cheb;
|
||||
chmp->chm_nr_free_blocks--;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,211 @@
|
|||
/* $NetBSD: chfs_pool.c,v 1.1 2011/11/24 15:51:31 ahoka Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2010 Department of Software Engineering,
|
||||
* University of Szeged, Hungary
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by the Department of Software Engineering, University of Szeged, Hungary
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Pool allocator and convenience routines for chfs.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/pool.h>
|
||||
#include <sys/atomic.h>
|
||||
|
||||
#include <uvm/uvm.h>
|
||||
|
||||
#include "chfs.h"
|
||||
//#include </root/xipffs/netbsd.chfs/chfs.h>
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
void * chfs_pool_page_alloc(struct pool *, int);
|
||||
void chfs_pool_page_free(struct pool *, void *);
|
||||
|
||||
extern void* pool_page_alloc_nointr(struct pool *, int);
|
||||
extern void pool_page_free_nointr(struct pool *, void *);
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
struct pool_allocator chfs_pool_allocator = {
|
||||
.pa_alloc = chfs_pool_page_alloc,
|
||||
.pa_free = chfs_pool_page_free,
|
||||
};
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
void
|
||||
chfs_pool_init(struct chfs_pool *chpp, size_t size, const char *what,
|
||||
struct chfs_mount *chmp)
|
||||
{
|
||||
int cnt;
|
||||
|
||||
cnt = snprintf(chpp->chp_name, sizeof(chpp->chp_name),
|
||||
"%s_chfs_%p", what, chmp);
|
||||
KASSERT(cnt < sizeof(chpp->chp_name));
|
||||
|
||||
pool_init(&chpp->chp_pool, size, 0, 0, 0, chpp->chp_name,
|
||||
&chfs_pool_allocator, IPL_NONE);
|
||||
chpp->chp_mount = chmp;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
void
|
||||
chfs_pool_destroy(struct chfs_pool *chpp)
|
||||
{
|
||||
pool_destroy((struct pool *)chpp);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
void *
|
||||
chfs_pool_page_alloc(struct pool *pp, int flags)
|
||||
{
|
||||
struct chfs_pool *chpp;
|
||||
struct chfs_mount *chmp;
|
||||
unsigned int pages;
|
||||
void *page;
|
||||
dbg("CHFS: pool_page_alloc()\n");
|
||||
|
||||
chpp = (struct chfs_pool *)pp;
|
||||
chmp = chpp->chp_mount;
|
||||
|
||||
pages = atomic_inc_uint_nv(&chmp->chm_pages_used);
|
||||
if (pages >= CHFS_PAGES_MAX(chmp)) {
|
||||
atomic_dec_uint(&chmp->chm_pages_used);
|
||||
return NULL;
|
||||
}
|
||||
page = pool_page_alloc_nointr(pp, flags | PR_WAITOK);
|
||||
if (page == NULL) {
|
||||
atomic_dec_uint(&chmp->chm_pages_used);
|
||||
}
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
void
|
||||
chfs_pool_page_free(struct pool *pp, void *v)
|
||||
{
|
||||
struct chfs_pool *chpp;
|
||||
struct chfs_mount *chmp;
|
||||
dbg("CHFS: pool_page_free()\n");
|
||||
|
||||
chpp = (struct chfs_pool *)pp;
|
||||
chmp = chpp->chp_mount;
|
||||
|
||||
atomic_dec_uint(&chmp->chm_pages_used);
|
||||
pool_page_free_nointr(pp, v);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
void
|
||||
chfs_str_pool_init(struct chfs_str_pool *chsp, struct chfs_mount *chmp)
|
||||
{
|
||||
dbg("CHFS: str_pool_init()\n");
|
||||
|
||||
chfs_pool_init(&chsp->chsp_pool_16, 16, "str", chmp);
|
||||
chfs_pool_init(&chsp->chsp_pool_32, 32, "str", chmp);
|
||||
chfs_pool_init(&chsp->chsp_pool_64, 64, "str", chmp);
|
||||
chfs_pool_init(&chsp->chsp_pool_128, 128, "str", chmp);
|
||||
chfs_pool_init(&chsp->chsp_pool_256, 256, "str", chmp);
|
||||
chfs_pool_init(&chsp->chsp_pool_512, 512, "str", chmp);
|
||||
chfs_pool_init(&chsp->chsp_pool_1024, 1024, "str", chmp);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
void
|
||||
chfs_str_pool_destroy(struct chfs_str_pool *chsp)
|
||||
{
|
||||
dbg("CHFS: str_pool_destroy()\n");
|
||||
|
||||
chfs_pool_destroy(&chsp->chsp_pool_16);
|
||||
chfs_pool_destroy(&chsp->chsp_pool_32);
|
||||
chfs_pool_destroy(&chsp->chsp_pool_64);
|
||||
chfs_pool_destroy(&chsp->chsp_pool_128);
|
||||
chfs_pool_destroy(&chsp->chsp_pool_256);
|
||||
chfs_pool_destroy(&chsp->chsp_pool_512);
|
||||
chfs_pool_destroy(&chsp->chsp_pool_1024);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
char *
|
||||
chfs_str_pool_get(struct chfs_str_pool *chsp, size_t len, int flags)
|
||||
{
|
||||
struct chfs_pool *p;
|
||||
dbg("CHFS: str_pool_get()\n");
|
||||
|
||||
KASSERT(len <= 1024);
|
||||
|
||||
if (len <= 16) p = &chsp->chsp_pool_16;
|
||||
else if (len <= 32) p = &chsp->chsp_pool_32;
|
||||
else if (len <= 64) p = &chsp->chsp_pool_64;
|
||||
else if (len <= 128) p = &chsp->chsp_pool_128;
|
||||
else if (len <= 256) p = &chsp->chsp_pool_256;
|
||||
else if (len <= 512) p = &chsp->chsp_pool_512;
|
||||
else if (len <= 1024) p = &chsp->chsp_pool_1024;
|
||||
else {
|
||||
KASSERT(0);
|
||||
p = NULL; /* Silence compiler warnings */
|
||||
}
|
||||
|
||||
return (char *)CHFS_POOL_GET(p, flags);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
void
|
||||
chfs_str_pool_put(struct chfs_str_pool *chsp, char *str, size_t len)
|
||||
{
|
||||
struct chfs_pool *p;
|
||||
dbg("CHFS: str_pool_put()\n");
|
||||
|
||||
KASSERT(len <= 1024);
|
||||
|
||||
if (len <= 16) p = &chsp->chsp_pool_16;
|
||||
else if (len <= 32) p = &chsp->chsp_pool_32;
|
||||
else if (len <= 64) p = &chsp->chsp_pool_64;
|
||||
else if (len <= 128) p = &chsp->chsp_pool_128;
|
||||
else if (len <= 256) p = &chsp->chsp_pool_256;
|
||||
else if (len <= 512) p = &chsp->chsp_pool_512;
|
||||
else if (len <= 1024) p = &chsp->chsp_pool_1024;
|
||||
else {
|
||||
KASSERT(0);
|
||||
p = NULL; /* Silence compiler warnings */
|
||||
}
|
||||
|
||||
CHFS_POOL_PUT(p, str);
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/* $NetBSD: chfs_pool.h,v 1.1 2011/11/24 15:51:31 ahoka Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2010 Department of Software Engineering,
|
||||
* University of Szeged, Hungary
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by the Department of Software Engineering, University of Szeged, Hungary
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _FS_CHFS_CHFS_POOL_H_
|
||||
#define _FS_CHFS_CHFS_POOL_H_
|
||||
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
struct chfs_pool {
|
||||
struct pool chp_pool;
|
||||
struct chfs_mount * chp_mount;
|
||||
char chp_name[64];
|
||||
};
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
struct chfs_str_pool {
|
||||
struct chfs_pool chsp_pool_16;
|
||||
struct chfs_pool chsp_pool_32;
|
||||
struct chfs_pool chsp_pool_64;
|
||||
struct chfs_pool chsp_pool_128;
|
||||
struct chfs_pool chsp_pool_256;
|
||||
struct chfs_pool chsp_pool_512;
|
||||
struct chfs_pool chsp_pool_1024;
|
||||
};
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
#ifdef _KERNEL
|
||||
|
||||
/*
|
||||
* Convenience functions and macros to manipulate a chfs_pool.
|
||||
*/
|
||||
|
||||
void chfs_pool_init(struct chfs_pool *chpp, size_t size,
|
||||
const char *what, struct chfs_mount *chmp);
|
||||
void chfs_pool_destroy(struct chfs_pool *chpp);
|
||||
|
||||
#define CHFS_POOL_GET(chpp, flags) pool_get((struct pool *)(chpp), flags)
|
||||
#define CHFS_POOL_PUT(chpp, v) pool_put((struct pool *)(chpp), v)
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Functions to manipulate a chfs_str_pool.
|
||||
*/
|
||||
|
||||
void chfs_str_pool_init(struct chfs_str_pool *, struct chfs_mount *);
|
||||
void chfs_str_pool_destroy(struct chfs_str_pool *);
|
||||
char * chfs_str_pool_get(struct chfs_str_pool *, size_t, int);
|
||||
void chfs_str_pool_put(struct chfs_str_pool *, char *, size_t);
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* _FS_CHFS_CHFS_POOL_H_ */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,740 @@
|
|||
/* $NetBSD: chfs_scan.c,v 1.1 2011/11/24 15:51:31 ahoka Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2010 Department of Software Engineering,
|
||||
* University of Szeged, Hungary
|
||||
* Copyright (c) 2010 David Tengeri <dtengeri@inf.u-szeged.hu>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by the Department of Software Engineering, University of Szeged, Hungary
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* chfs_scan.c
|
||||
*
|
||||
* Created on: 2009.11.05.
|
||||
* Author: dtengeri
|
||||
*/
|
||||
|
||||
#include "chfs.h"
|
||||
|
||||
/**
|
||||
* chfs_scan_make_vnode_cache - makes a new vnode cache during scan
|
||||
* @chmp: CHFS main descriptor structure
|
||||
* @vno: vnode identifier
|
||||
* This function returns a vnode cache belonging to @vno.
|
||||
*/
|
||||
struct chfs_vnode_cache *
|
||||
chfs_scan_make_vnode_cache(struct chfs_mount *chmp, ino_t vno)
|
||||
{
|
||||
struct chfs_vnode_cache *vc;
|
||||
|
||||
KASSERT(mutex_owned(&chmp->chm_lock_vnocache));
|
||||
|
||||
vc = chfs_vnode_cache_get(chmp, vno);
|
||||
if (vc) {
|
||||
return vc;
|
||||
}
|
||||
|
||||
if (vno > chmp->chm_max_vno) {
|
||||
chmp->chm_max_vno = vno;
|
||||
}
|
||||
|
||||
vc = chfs_vnode_cache_alloc(vno);
|
||||
|
||||
//mutex_enter(&chmp->chm_lock_vnocache);
|
||||
|
||||
chfs_vnode_cache_add(chmp, vc);
|
||||
|
||||
//mutex_exit(&chmp->chm_lock_vnocache);
|
||||
|
||||
if (vno == CHFS_ROOTINO) {
|
||||
vc->nlink = 2;
|
||||
vc->pvno = CHFS_ROOTINO;
|
||||
chfs_vnode_cache_set_state(chmp,
|
||||
vc, VNO_STATE_CHECKEDABSENT);
|
||||
}
|
||||
|
||||
return vc;
|
||||
}
|
||||
|
||||
/**
|
||||
* chfs_scan_check_node_hdr - checks node magic and crc
|
||||
* @nhdr: node header to check
|
||||
* Returns 0 if everything is OK, error code otherwise.
|
||||
*/
|
||||
int
|
||||
chfs_scan_check_node_hdr(struct chfs_flash_node_hdr *nhdr)
|
||||
{
|
||||
uint16_t magic;
|
||||
uint32_t crc, hdr_crc;
|
||||
|
||||
magic = le16toh(nhdr->magic);
|
||||
|
||||
if (magic != CHFS_FS_MAGIC_BITMASK) {
|
||||
dbg("bad magic\n");
|
||||
return CHFS_NODE_BADMAGIC;
|
||||
}
|
||||
|
||||
hdr_crc = le32toh(nhdr->hdr_crc);
|
||||
crc = crc32(0, (uint8_t *)nhdr, CHFS_NODE_HDR_SIZE - 4);
|
||||
|
||||
if (crc != hdr_crc) {
|
||||
dbg("bad crc\n");
|
||||
return CHFS_NODE_BADCRC;
|
||||
}
|
||||
|
||||
return CHFS_NODE_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* chfs_scan_check_vnode - check vnode crc and add to vnode cache
|
||||
* @chmp: CHFS main descriptor structure
|
||||
* @cheb: eraseblock informations
|
||||
* @buf: vnode to check
|
||||
* @ofs: offset in eraseblock where vnode starts
|
||||
*/
|
||||
int
|
||||
chfs_scan_check_vnode(struct chfs_mount *chmp,
|
||||
struct chfs_eraseblock *cheb, void *buf, off_t ofs)
|
||||
{
|
||||
KASSERT(mutex_owned(&chmp->chm_lock_mountfields));
|
||||
struct chfs_vnode_cache *vc;
|
||||
struct chfs_flash_vnode *vnode = buf;
|
||||
struct chfs_node_ref *nref;
|
||||
int err;
|
||||
uint32_t crc;
|
||||
ino_t vno;
|
||||
|
||||
crc = crc32(0, (uint8_t *)vnode,
|
||||
sizeof(struct chfs_flash_vnode) - 4);
|
||||
|
||||
if (crc != le32toh(vnode->node_crc)) {
|
||||
err = chfs_update_eb_dirty(chmp,
|
||||
cheb, le32toh(vnode->length));
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return CHFS_NODE_BADCRC;
|
||||
}
|
||||
|
||||
vno = le64toh(vnode->vno);
|
||||
|
||||
mutex_enter(&chmp->chm_lock_vnocache);
|
||||
vc = chfs_vnode_cache_get(chmp, vno);
|
||||
if (!vc) {
|
||||
vc = chfs_scan_make_vnode_cache(chmp, vno);
|
||||
if (!vc) {
|
||||
mutex_exit(&chmp->chm_lock_vnocache);
|
||||
return ENOMEM;
|
||||
}
|
||||
}
|
||||
mutex_exit(&chmp->chm_lock_vnocache);
|
||||
|
||||
nref = chfs_alloc_node_ref(cheb);
|
||||
|
||||
nref->nref_offset = ofs;
|
||||
|
||||
KASSERT(nref->nref_lnr == cheb->lnr);
|
||||
|
||||
/* Check version of vnode. */
|
||||
if ((struct chfs_vnode_cache *)vc->v != vc) {
|
||||
if (le64toh(vnode->version) > *vc->vno_version) {
|
||||
//err = chfs_update_eb_dirty(chmp, &chmp->chm_blocks[vc->v->lnr],
|
||||
// sizeof(struct chfs_flash_vnode));
|
||||
*vc->vno_version = le64toh(vnode->version);
|
||||
chfs_add_vnode_ref_to_vc(chmp, vc, nref);
|
||||
} else {
|
||||
err = chfs_update_eb_dirty(chmp, cheb,
|
||||
sizeof(struct chfs_flash_vnode));
|
||||
return CHFS_NODE_OK;
|
||||
}
|
||||
} else {
|
||||
vc->vno_version = kmem_alloc(sizeof(uint64_t), KM_SLEEP);
|
||||
if (!vc->vno_version)
|
||||
return ENOMEM;
|
||||
*vc->vno_version = le64toh(vnode->version);
|
||||
chfs_add_vnode_ref_to_vc(chmp, vc, nref);
|
||||
}
|
||||
|
||||
mutex_enter(&chmp->chm_lock_sizes);
|
||||
//dbg("B:lnr: %d |free_size: %d node's size: %d\n", cheb->lnr, cheb->free_size, le32toh(vnode->length));
|
||||
chfs_change_size_free(chmp, cheb, -le32toh(vnode->length));
|
||||
chfs_change_size_used(chmp, cheb, le32toh(vnode->length));
|
||||
mutex_exit(&chmp->chm_lock_sizes);
|
||||
|
||||
KASSERT(cheb->used_size <= chmp->chm_ebh->eb_size);
|
||||
|
||||
KASSERT(cheb->used_size + cheb->free_size + cheb->dirty_size + cheb->unchecked_size + cheb->wasted_size == chmp->chm_ebh->eb_size);
|
||||
|
||||
//dbg(" A: free_size: %d\n", cheb->free_size);
|
||||
|
||||
/*dbg("vnode dump:\n");
|
||||
dbg(" ->magic: 0x%x\n", le16toh(vnode->magic));
|
||||
dbg(" ->type: %d\n", le16toh(vnode->type));
|
||||
dbg(" ->length: %d\n", le32toh(vnode->length));
|
||||
dbg(" ->hdr_crc: 0x%x\n", le32toh(vnode->hdr_crc));
|
||||
dbg(" ->vno: %d\n", le64toh(vnode->vno));
|
||||
dbg(" ->version: %ld\n", le64toh(vnode->version));
|
||||
dbg(" ->uid: %d\n", le16toh(vnode->uid));
|
||||
dbg(" ->gid: %d\n", le16toh(vnode->gid));
|
||||
dbg(" ->mode: %d\n", le32toh(vnode->mode));
|
||||
dbg(" ->dn_size: %d\n", le32toh(vnode->dn_size));
|
||||
dbg(" ->atime: %d\n", le32toh(vnode->atime));
|
||||
dbg(" ->mtime: %d\n", le32toh(vnode->mtime));
|
||||
dbg(" ->ctime: %d\n", le32toh(vnode->ctime));
|
||||
dbg(" ->dsize: %d\n", le32toh(vnode->dsize));
|
||||
dbg(" ->node_crc: 0x%x\n", le32toh(vnode->node_crc));*/
|
||||
|
||||
return CHFS_NODE_OK;
|
||||
}
|
||||
|
||||
int
|
||||
chfs_scan_mark_dirent_obsolete(struct chfs_mount *chmp,
|
||||
struct chfs_vnode_cache *vc, struct chfs_dirent *fd)
|
||||
{
|
||||
//int size;
|
||||
struct chfs_eraseblock *cheb;
|
||||
struct chfs_node_ref *prev, *nref;
|
||||
|
||||
nref = fd->nref;
|
||||
cheb = &chmp->chm_blocks[fd->nref->nref_lnr];
|
||||
|
||||
/* Remove dirent's node ref from vnode cache */
|
||||
prev = vc->dirents;
|
||||
if (prev && prev == nref) {
|
||||
vc->dirents = prev->nref_next;
|
||||
} else if (prev && prev != (void *)vc) {
|
||||
while (prev->nref_next && prev->nref_next !=
|
||||
(void *)vc && prev->nref_next != nref) {
|
||||
prev = prev->nref_next;
|
||||
}
|
||||
|
||||
if (prev->nref_next == nref) {
|
||||
prev->nref_next = nref->nref_next;
|
||||
}
|
||||
}
|
||||
/*dbg("XXX - start\n");
|
||||
//nref = vc->dirents;
|
||||
struct chfs_dirent *tmp;
|
||||
tmp = vc->scan_dirents;
|
||||
while (tmp) {
|
||||
dbg(" ->tmp->name: %s\n", tmp->name);
|
||||
dbg(" ->tmp->version: %ld\n", tmp->version);
|
||||
dbg(" ->tmp->vno: %d\n", tmp->vno);
|
||||
tmp = tmp->next;
|
||||
}
|
||||
dbg("XXX - end\n");*/
|
||||
//size = CHFS_PAD(sizeof(struct chfs_flash_dirent_node) + fd->nsize);
|
||||
|
||||
KASSERT(cheb->used_size + cheb->free_size + cheb->dirty_size +
|
||||
cheb->unchecked_size + cheb->wasted_size == chmp->chm_ebh->eb_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
chfs_add_fd_to_list(struct chfs_mount *chmp,
|
||||
struct chfs_dirent *new, struct chfs_vnode_cache *pvc)
|
||||
{
|
||||
KASSERT(mutex_owned(&chmp->chm_lock_mountfields));
|
||||
int size;
|
||||
struct chfs_eraseblock *cheb, *oldcheb;
|
||||
// struct chfs_dirent **prev;
|
||||
struct chfs_dirent *fd, *tmpfd;
|
||||
|
||||
dbg("adding fd to list: %s\n", new->name);
|
||||
|
||||
if ((new->version > pvc->highest_version))
|
||||
pvc->highest_version = new->version;
|
||||
|
||||
size = CHFS_PAD(sizeof(struct chfs_flash_dirent_node) +
|
||||
new->nsize);
|
||||
cheb = &chmp->chm_blocks[new->nref->nref_lnr];
|
||||
|
||||
mutex_enter(&chmp->chm_lock_sizes);
|
||||
TAILQ_FOREACH_SAFE(fd, &pvc->scan_dirents, fds, tmpfd) {
|
||||
if (fd->nhash > new->nhash) {
|
||||
/* insert new before fd */
|
||||
TAILQ_INSERT_BEFORE(fd, new, fds);
|
||||
goto out;
|
||||
} else if (fd->nhash == new->nhash &&
|
||||
!strcmp(fd->name, new->name)) {
|
||||
if (new->version > fd->version) {
|
||||
// new->next = fd->next;
|
||||
/* replace fd with new */
|
||||
TAILQ_INSERT_BEFORE(fd, new, fds);
|
||||
chfs_change_size_free(chmp, cheb, -size);
|
||||
chfs_change_size_used(chmp, cheb, size);
|
||||
|
||||
TAILQ_REMOVE(&pvc->scan_dirents, fd, fds);
|
||||
if (fd->nref) {
|
||||
size = CHFS_PAD(sizeof(struct chfs_flash_dirent_node) + fd->nsize);
|
||||
chfs_scan_mark_dirent_obsolete(chmp, pvc, fd);
|
||||
oldcheb = &chmp->chm_blocks[fd->nref->nref_lnr];
|
||||
chfs_change_size_used(chmp, oldcheb, -size);
|
||||
chfs_change_size_dirty(chmp, oldcheb, size);
|
||||
}
|
||||
chfs_free_dirent(fd);
|
||||
// *prev = new;//XXX
|
||||
} else {
|
||||
chfs_scan_mark_dirent_obsolete(chmp, pvc, new);
|
||||
chfs_change_size_free(chmp, cheb, -size);
|
||||
chfs_change_size_dirty(chmp, cheb, size);
|
||||
chfs_free_dirent(new);
|
||||
}
|
||||
/*dbg("START\n");
|
||||
fd = pvc->scan_dirents;
|
||||
while (fd) {
|
||||
dbg("dirent dump:\n");
|
||||
dbg(" ->vno: %d\n", fd->vno);
|
||||
dbg(" ->version: %ld\n", fd->version);
|
||||
dbg(" ->nhash: 0x%x\n", fd->nhash);
|
||||
dbg(" ->nsize: %d\n", fd->nsize);
|
||||
dbg(" ->name: %s\n", fd->name);
|
||||
dbg(" ->type: %d\n", fd->type);
|
||||
fd = fd->next;
|
||||
}
|
||||
dbg("END\n");*/
|
||||
mutex_exit(&chmp->chm_lock_sizes);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* if we couldnt fit it elsewhere, lets add to the end */
|
||||
TAILQ_INSERT_TAIL(&pvc->scan_dirents, new, fds);
|
||||
|
||||
out:
|
||||
//dbg("B:lnr: %d |free_size: %d size: %d\n", cheb->lnr, cheb->free_size, size);
|
||||
chfs_change_size_free(chmp, cheb, -size);
|
||||
chfs_change_size_used(chmp, cheb, size);
|
||||
mutex_exit(&chmp->chm_lock_sizes);
|
||||
|
||||
KASSERT(cheb->used_size <= chmp->chm_ebh->eb_size);
|
||||
//dbg(" A: free_size: %d\n", cheb->free_size);
|
||||
|
||||
KASSERT(cheb->used_size + cheb->free_size + cheb->dirty_size + cheb->unchecked_size + cheb->wasted_size == chmp->chm_ebh->eb_size);
|
||||
|
||||
|
||||
// fd = pvc->scan_dirents;
|
||||
/*dbg("START\n");
|
||||
while (fd) {
|
||||
dbg("dirent dump:\n");
|
||||
dbg(" ->vno: %d\n", fd->vno);
|
||||
dbg(" ->version: %ld\n", fd->version);
|
||||
dbg(" ->nhash: 0x%x\n", fd->nhash);
|
||||
dbg(" ->nsize: %d\n", fd->nsize);
|
||||
dbg(" ->name: %s\n", fd->name);
|
||||
dbg(" ->type: %d\n", fd->type);
|
||||
fd = fd->next;
|
||||
}
|
||||
dbg("END\n");*/
|
||||
}
|
||||
/**
|
||||
* chfs_scan_check_dirent_node - check vnode crc and add to vnode cache
|
||||
* @chmp: CHFS main descriptor structure
|
||||
* @cheb: eraseblock informations
|
||||
* @buf: directory entry to check
|
||||
* @ofs: offset in eraseblock where dirent starts
|
||||
*/
|
||||
int
|
||||
chfs_scan_check_dirent_node(struct chfs_mount *chmp,
|
||||
struct chfs_eraseblock *cheb, void *buf, off_t ofs)
|
||||
{
|
||||
int err, namelen;
|
||||
uint32_t crc;
|
||||
struct chfs_dirent *fd;
|
||||
struct chfs_vnode_cache *vc;
|
||||
struct chfs_flash_dirent_node *dirent = buf;
|
||||
|
||||
//struct chfs_node_ref *tmp;
|
||||
|
||||
crc = crc32(0, (uint8_t *)dirent, sizeof(*dirent) - 4);
|
||||
if (crc != le32toh(dirent->node_crc)) {
|
||||
err = chfs_update_eb_dirty(chmp, cheb, le32toh(dirent->length));
|
||||
if (err)
|
||||
return err;
|
||||
return CHFS_NODE_BADCRC;
|
||||
}
|
||||
namelen = dirent->nsize;
|
||||
|
||||
fd = chfs_alloc_dirent(namelen + 1);
|
||||
if (!fd)
|
||||
return ENOMEM;
|
||||
|
||||
fd->nref = chfs_alloc_node_ref(cheb);
|
||||
if (!fd->nref)
|
||||
return ENOMEM;
|
||||
|
||||
KASSERT(fd->nref->nref_lnr == cheb->lnr);
|
||||
|
||||
memcpy(&fd->name, dirent->name, namelen);
|
||||
fd->nsize = namelen;
|
||||
fd->name[namelen] = 0;
|
||||
crc = crc32(0, fd->name, dirent->nsize);
|
||||
if (crc != le32toh(dirent->name_crc)) {
|
||||
chfs_err("Directory entry's name has bad crc: read: 0x%x, "
|
||||
"calculated: 0x%x\n", le32toh(dirent->name_crc), crc);
|
||||
chfs_free_dirent(fd);
|
||||
err = chfs_update_eb_dirty(chmp, cheb, le32toh(dirent->length));
|
||||
if (err)
|
||||
return err;
|
||||
return CHFS_NODE_BADNAMECRC;
|
||||
}
|
||||
|
||||
/* Check vnode_cache of parent node */
|
||||
mutex_enter(&chmp->chm_lock_vnocache);
|
||||
vc = chfs_scan_make_vnode_cache(chmp, le64toh(dirent->pvno));
|
||||
mutex_exit(&chmp->chm_lock_vnocache);
|
||||
if (!vc) {
|
||||
chfs_free_dirent(fd);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
fd->nref->nref_offset = ofs;
|
||||
|
||||
dbg("add dirent to #%llu\n", vc->vno);
|
||||
chfs_add_node_to_list(chmp, vc, fd->nref, &vc->dirents);
|
||||
/*tmp = vc->dirents;
|
||||
dbg("START|vno: %d dirents dump\n", vc->vno);
|
||||
while (tmp) {
|
||||
dbg(" ->nref->nref_lnr: %d\n", tmp->lnr);
|
||||
dbg(" ->nref->nref_offset: %d\n", tmp->offset);
|
||||
tmp = tmp->next;
|
||||
}
|
||||
dbg(" END|vno: %d dirents dump\n", vc->vno);*/
|
||||
|
||||
// fd->next = NULL;
|
||||
fd->vno = le64toh(dirent->vno);
|
||||
fd->version = le64toh(dirent->version);
|
||||
fd->nhash = hash32_buf(fd->name, namelen, HASH32_BUF_INIT);
|
||||
fd->type = dirent->dtype;
|
||||
|
||||
/*dbg("dirent dump:\n");
|
||||
dbg(" ->vno: %d\n", fd->vno);
|
||||
dbg(" ->version: %ld\n", fd->version);
|
||||
dbg(" ->nhash: 0x%x\n", fd->nhash);
|
||||
dbg(" ->nsize: %d\n", fd->nsize);
|
||||
dbg(" ->name: %s\n", fd->name);
|
||||
dbg(" ->type: %d\n", fd->type);*/
|
||||
|
||||
chfs_add_fd_to_list(chmp, fd, vc);
|
||||
|
||||
/*struct chfs_node_ref *tmp;
|
||||
tmp = vc->dirents;
|
||||
dbg("START|vno: %d dirents dump\n", vc->vno);
|
||||
while (tmp) {
|
||||
dbg(" ->nref->nref_lnr: %d\n", tmp->lnr);
|
||||
dbg(" ->nref->nref_offset: %d\n", tmp->offset);
|
||||
tmp = tmp->next;
|
||||
}
|
||||
dbg(" END|vno: %d dirents dump\n", vc->vno);*/
|
||||
|
||||
/*dbg("dirent dump:\n");
|
||||
dbg(" ->magic: 0x%x\n", le16toh(dirent->magic));
|
||||
dbg(" ->type: %d\n", le16toh(dirent->type));
|
||||
dbg(" ->length: %d\n", le32toh(dirent->length));
|
||||
dbg(" ->hdr_crc: 0x%x\n", le32toh(dirent->hdr_crc));
|
||||
dbg(" ->vno: %d\n", le64toh(dirent->vno));
|
||||
dbg(" ->pvno: %d\n", le64toh(dirent->pvno));
|
||||
dbg(" ->version: %ld\n", le64toh(dirent->version));
|
||||
dbg(" ->mctime: %d\n", le32toh(dirent->mctime));
|
||||
dbg(" ->nsize: %d\n", dirent->nsize);
|
||||
dbg(" ->dtype: %d\n", dirent->dtype);
|
||||
dbg(" ->name_crc: 0x%x\n", le32toh(dirent->name_crc));
|
||||
dbg(" ->node_crc: 0x%x\n", le32toh(dirent->node_crc));
|
||||
dbg(" ->name: %s\n", dirent->name);*/
|
||||
|
||||
return CHFS_NODE_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* chfs_scan_check_data_node - check vnode crc and add to vnode cache
|
||||
* @chmp: CHFS main descriptor structure
|
||||
* @cheb: eraseblock informations
|
||||
* @buf: data node to check
|
||||
* @ofs: offset in eraseblock where data node starts
|
||||
*/
|
||||
int
|
||||
chfs_scan_check_data_node(struct chfs_mount *chmp,
|
||||
struct chfs_eraseblock *cheb, void *buf, off_t ofs)
|
||||
{
|
||||
KASSERT(mutex_owned(&chmp->chm_lock_mountfields));
|
||||
int err;
|
||||
uint32_t crc, vno;
|
||||
struct chfs_node_ref *nref;
|
||||
struct chfs_vnode_cache *vc;
|
||||
struct chfs_flash_data_node *dnode = buf;
|
||||
|
||||
crc = crc32(0, (uint8_t *)dnode, sizeof(struct chfs_flash_data_node) - 4);
|
||||
if (crc != le32toh(dnode->node_crc)) {
|
||||
err = chfs_update_eb_dirty(chmp, cheb, le32toh(dnode->length));
|
||||
if (err)
|
||||
return err;
|
||||
return CHFS_NODE_BADCRC;
|
||||
}
|
||||
/**
|
||||
* Don't check data nodes crc and version here, it will be done in
|
||||
* the background GC thread.
|
||||
*/
|
||||
nref = chfs_alloc_node_ref(cheb);
|
||||
if (!nref)
|
||||
return ENOMEM;
|
||||
|
||||
nref->nref_offset = ofs | CHFS_UNCHECKED_NODE_MASK;
|
||||
|
||||
KASSERT(nref->nref_lnr == cheb->lnr);
|
||||
|
||||
vno = le64toh(dnode->vno);
|
||||
mutex_enter(&chmp->chm_lock_vnocache);
|
||||
vc = chfs_vnode_cache_get(chmp, vno);
|
||||
if (!vc) {
|
||||
vc = chfs_scan_make_vnode_cache(chmp, vno);
|
||||
if (!vc)
|
||||
return ENOMEM;
|
||||
}
|
||||
mutex_exit(&chmp->chm_lock_vnocache);
|
||||
chfs_add_node_to_list(chmp, vc, nref, &vc->dnode);
|
||||
|
||||
dbg("chmpfree: %u, chebfree: %u, dnode: %u\n", chmp->chm_free_size, cheb->free_size, dnode->length);
|
||||
|
||||
mutex_enter(&chmp->chm_lock_sizes);
|
||||
chfs_change_size_free(chmp, cheb, -dnode->length);
|
||||
chfs_change_size_unchecked(chmp, cheb, dnode->length);
|
||||
mutex_exit(&chmp->chm_lock_sizes);
|
||||
return CHFS_NODE_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* chfs_scan_classify_cheb - determine eraseblock's state
|
||||
* @chmp: CHFS main descriptor structure
|
||||
* @cheb: eraseblock to classify
|
||||
*/
|
||||
int
|
||||
chfs_scan_classify_cheb(struct chfs_mount *chmp,
|
||||
struct chfs_eraseblock *cheb)
|
||||
{
|
||||
if (cheb->free_size == chmp->chm_ebh->eb_size)
|
||||
return CHFS_BLK_STATE_FREE;
|
||||
else if (cheb->dirty_size < MAX_DIRTY_TO_CLEAN)
|
||||
return CHFS_BLK_STATE_CLEAN;
|
||||
else if (cheb->used_size || cheb->unchecked_size)
|
||||
return CHFS_BLK_STATE_PARTDIRTY;
|
||||
else
|
||||
return CHFS_BLK_STATE_ALLDIRTY;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* chfs_scan_eraseblock - scans an eraseblock and looking for nodes
|
||||
* @chmp: CHFS main descriptor structure
|
||||
* @cheb: eraseblock to scan
|
||||
*
|
||||
* This function scans a whole eraseblock, checks the nodes on it and add them
|
||||
* to the vnode cache.
|
||||
* Returns eraseblock state on success, error code if fails.
|
||||
*/
|
||||
int
|
||||
chfs_scan_eraseblock(struct chfs_mount *chmp,
|
||||
struct chfs_eraseblock *cheb) {
|
||||
|
||||
int err;
|
||||
size_t len, retlen;
|
||||
off_t ofs = 0;
|
||||
int lnr = cheb->lnr;
|
||||
u_char *buf;
|
||||
struct chfs_flash_node_hdr *nhdr;
|
||||
int read_free = 0;
|
||||
struct chfs_node_ref *nref;
|
||||
|
||||
|
||||
dbg("scanning eraseblock content: %d free_size: %d\n", cheb->lnr, cheb->free_size);
|
||||
dbg("scanned physical block: %d\n", chmp->chm_ebh->lmap[lnr]);
|
||||
buf = kmem_alloc(CHFS_MAX_NODE_SIZE, KM_SLEEP);
|
||||
|
||||
while((ofs + CHFS_NODE_HDR_SIZE) < chmp->chm_ebh->eb_size) {
|
||||
memset(buf, 0 , CHFS_MAX_NODE_SIZE);
|
||||
err = chfs_read_leb(chmp,
|
||||
lnr, buf, ofs, CHFS_NODE_HDR_SIZE, &retlen);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (retlen != CHFS_NODE_HDR_SIZE) {
|
||||
chfs_err("Error reading node header: "
|
||||
"read: %zu instead of: %zu\n",
|
||||
CHFS_NODE_HDR_SIZE, retlen);
|
||||
return EIO;
|
||||
}
|
||||
|
||||
/* first we check if the buffer we read is full with 0xff, if yes maybe
|
||||
* the blocks remaining area is free. We increase read_free and if it
|
||||
* reaches MAX_READ_FREE we stop reading the block*/
|
||||
if (check_pattern(buf, 0xff, 0, CHFS_NODE_HDR_SIZE)) {
|
||||
read_free += CHFS_NODE_HDR_SIZE;
|
||||
if (read_free >= MAX_READ_FREE(chmp)) {
|
||||
dbg("rest of the block is free. Size: %d\n", cheb->free_size);
|
||||
return chfs_scan_classify_cheb(chmp, cheb);
|
||||
}
|
||||
ofs += CHFS_NODE_HDR_SIZE;
|
||||
continue;
|
||||
} else {
|
||||
chfs_update_eb_dirty(chmp, cheb, read_free);
|
||||
read_free = 0;
|
||||
}
|
||||
|
||||
nhdr = (struct chfs_flash_node_hdr *)buf;
|
||||
|
||||
err = chfs_scan_check_node_hdr(nhdr);
|
||||
if (err) {
|
||||
dbg("node hdr error\n");
|
||||
err = chfs_update_eb_dirty(chmp, cheb, 4);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
ofs += 4;
|
||||
continue;
|
||||
}
|
||||
ofs += CHFS_NODE_HDR_SIZE;
|
||||
if (ofs > chmp->chm_ebh->eb_size) {
|
||||
chfs_err("Second part of node is on the next eraseblock.\n");
|
||||
return EIO;
|
||||
}
|
||||
switch (le16toh(nhdr->type)) {
|
||||
case CHFS_NODETYPE_VNODE:
|
||||
/* Read up the node */
|
||||
//dbg("nodetype vnode\n");
|
||||
len = le32toh(nhdr->length) - CHFS_NODE_HDR_SIZE;
|
||||
err = chfs_read_leb(chmp,
|
||||
lnr, buf + CHFS_NODE_HDR_SIZE,
|
||||
ofs, len, &retlen);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (retlen != len) {
|
||||
chfs_err("Error reading vnode: read: %zu instead of: %zu\n",
|
||||
len, retlen);
|
||||
return EIO;
|
||||
}
|
||||
KASSERT(lnr == cheb->lnr);
|
||||
err = chfs_scan_check_vnode(chmp,
|
||||
cheb, buf, ofs - CHFS_NODE_HDR_SIZE);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
//dbg("XXX5end\n");
|
||||
break;
|
||||
case CHFS_NODETYPE_DIRENT:
|
||||
/* Read up the node */
|
||||
//dbg("nodetype dirent\n");
|
||||
len = le32toh(nhdr->length) - CHFS_NODE_HDR_SIZE;
|
||||
|
||||
err = chfs_read_leb(chmp,
|
||||
lnr, buf + CHFS_NODE_HDR_SIZE,
|
||||
ofs, len, &retlen);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (retlen != len) {
|
||||
chfs_err("Error reading dirent node: read: %zu "
|
||||
"instead of: %zu\n", len, retlen);
|
||||
return EIO;
|
||||
}
|
||||
|
||||
KASSERT(lnr == cheb->lnr);
|
||||
|
||||
err = chfs_scan_check_dirent_node(chmp,
|
||||
cheb, buf, ofs - CHFS_NODE_HDR_SIZE);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
//dbg("XXX6end\n");
|
||||
break;
|
||||
case CHFS_NODETYPE_DATA:
|
||||
//dbg("nodetype data\n");
|
||||
len = sizeof(struct chfs_flash_data_node) -
|
||||
CHFS_NODE_HDR_SIZE;
|
||||
err = chfs_read_leb(chmp,
|
||||
lnr, buf + CHFS_NODE_HDR_SIZE,
|
||||
ofs, len, &retlen);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (retlen != len) {
|
||||
chfs_err("Error reading data node: read: %zu "
|
||||
"instead of: %zu\n", len, retlen);
|
||||
return EIO;
|
||||
}
|
||||
KASSERT(lnr == cheb->lnr);
|
||||
err = chfs_scan_check_data_node(chmp,
|
||||
cheb, buf, ofs - CHFS_NODE_HDR_SIZE);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
//dbg("XXX7end\n");
|
||||
break;
|
||||
case CHFS_NODETYPE_PADDING:
|
||||
//dbg("nodetype padding\n");
|
||||
//dbg("padding len: %d\n", le32toh(nhdr->length));
|
||||
//dbg("BEF: cheb->free_size: %d\n", cheb->free_size);
|
||||
nref = chfs_alloc_node_ref(cheb);
|
||||
nref->nref_offset = ofs - CHFS_NODE_HDR_SIZE;
|
||||
nref->nref_offset = CHFS_GET_OFS(nref->nref_offset) |
|
||||
CHFS_OBSOLETE_NODE_MASK;
|
||||
|
||||
err = chfs_update_eb_dirty(chmp, cheb,
|
||||
le32toh(nhdr->length));
|
||||
//dbg("AFT: cheb->free_size: %d\n", cheb->free_size);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
//dbg("XXX8end\n");
|
||||
break;
|
||||
default:
|
||||
//dbg("nodetype ? (default)\n");
|
||||
/* Unknown node type, update dirty and skip */
|
||||
err = chfs_update_eb_dirty(chmp, cheb,
|
||||
le32toh(nhdr->length));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
//dbg("XXX9end\n");
|
||||
break;
|
||||
}
|
||||
ofs += le32toh(nhdr->length) - CHFS_NODE_HDR_SIZE;
|
||||
}
|
||||
|
||||
KASSERT(cheb->used_size + cheb->free_size + cheb->dirty_size +
|
||||
cheb->unchecked_size + cheb->wasted_size == chmp->chm_ebh->eb_size);
|
||||
|
||||
//dbg("XXX10\n");
|
||||
return chfs_scan_classify_cheb(chmp, cheb);
|
||||
}
|
|
@ -0,0 +1,539 @@
|
|||
/* $NetBSD: chfs_subr.c,v 1.1 2011/11/24 15:51:31 ahoka Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2010 Department of Software Engineering,
|
||||
* University of Szeged, Hungary
|
||||
* Copyright (C) 2010 Tamas Toth <ttoth@inf.u-szeged.hu>
|
||||
* Copyright (C) 2010 Adam Hoka <ahoka@NetBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by the Department of Software Engineering, University of Szeged, Hungary
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Efficient memory file system supporting functions.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/dirent.h>
|
||||
#include <sys/event.h>
|
||||
#include <sys/kmem.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/namei.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/swap.h>
|
||||
#include <sys/vnode.h>
|
||||
#include <sys/kauth.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/atomic.h>
|
||||
|
||||
#include <uvm/uvm.h>
|
||||
|
||||
#include <miscfs/specfs/specdev.h>
|
||||
#include "chfs.h"
|
||||
//#include <fs/chfs/chfs_vnops.h>
|
||||
//#include </root/xipffs/netbsd.chfs/chfs.h>
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Returns information about the number of available memory pages,
|
||||
* including physical and virtual ones.
|
||||
*
|
||||
* If 'total' is true, the value returned is the total amount of memory
|
||||
* pages configured for the system (either in use or free).
|
||||
* If it is FALSE, the value returned is the amount of free memory pages.
|
||||
*
|
||||
* Remember to remove DUMMYFS_PAGES_RESERVED from the returned value to avoid
|
||||
* excessive memory usage.
|
||||
*
|
||||
*/
|
||||
size_t
|
||||
chfs_mem_info(bool total)
|
||||
{
|
||||
size_t size;
|
||||
|
||||
size = 0;
|
||||
size += uvmexp.swpgavail;
|
||||
if (!total) {
|
||||
size -= uvmexp.swpgonly;
|
||||
}
|
||||
size += uvmexp.free;
|
||||
size += uvmexp.filepages;
|
||||
if (size > uvmexp.wired) {
|
||||
size -= uvmexp.wired;
|
||||
} else {
|
||||
size = 0;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Looks for a directory entry in the directory represented by node.
|
||||
* 'cnp' describes the name of the entry to look for. Note that the .
|
||||
* and .. components are not allowed as they do not physically exist
|
||||
* within directories.
|
||||
*
|
||||
* Returns a pointer to the entry when found, otherwise NULL.
|
||||
*/
|
||||
struct chfs_dirent *
|
||||
chfs_dir_lookup(struct chfs_inode *ip, struct componentname *cnp)
|
||||
{
|
||||
bool found;
|
||||
struct chfs_dirent *fd;
|
||||
dbg("dir_lookup()\n");
|
||||
|
||||
KASSERT(IMPLIES(cnp->cn_namelen == 1, cnp->cn_nameptr[0] != '.'));
|
||||
KASSERT(IMPLIES(cnp->cn_namelen == 2, !(cnp->cn_nameptr[0] == '.' &&
|
||||
cnp->cn_nameptr[1] == '.')));
|
||||
//CHFS_VALIDATE_DIR(node);
|
||||
|
||||
//node->chn_status |= CHFS_NODE_ACCESSED;
|
||||
|
||||
found = false;
|
||||
// fd = ip->dents;
|
||||
// while(fd) {
|
||||
TAILQ_FOREACH(fd, &ip->dents, fds) {
|
||||
KASSERT(cnp->cn_namelen < 0xffff);
|
||||
if (fd->vno == 0)
|
||||
continue;
|
||||
/*dbg("dirent dump:\n");
|
||||
dbg(" ->vno: %d\n", fd->vno);
|
||||
dbg(" ->version: %ld\n", fd->version);
|
||||
dbg(" ->nhash: 0x%x\n", fd->nhash);
|
||||
dbg(" ->nsize: %d\n", fd->nsize);
|
||||
dbg(" ->name: %s\n", fd->name);
|
||||
dbg(" ->type: %d\n", fd->type);*/
|
||||
if (fd->nsize == (uint16_t)cnp->cn_namelen &&
|
||||
memcmp(fd->name, cnp->cn_nameptr, fd->nsize) == 0) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
// fd = fd->next;
|
||||
}
|
||||
|
||||
return found ? fd : NULL;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
int
|
||||
chfs_filldir(struct uio* uio, ino_t ino, const char *name,
|
||||
int namelen, enum vtype type)
|
||||
{
|
||||
struct dirent dent;
|
||||
int error;
|
||||
|
||||
memset(&dent, 0, sizeof(dent));
|
||||
|
||||
dent.d_fileno = ino;
|
||||
switch (type) {
|
||||
case VBLK:
|
||||
dent.d_type = DT_BLK;
|
||||
break;
|
||||
|
||||
case VCHR:
|
||||
dent.d_type = DT_CHR;
|
||||
break;
|
||||
|
||||
case VDIR:
|
||||
dent.d_type = DT_DIR;
|
||||
break;
|
||||
|
||||
case VFIFO:
|
||||
dent.d_type = DT_FIFO;
|
||||
break;
|
||||
|
||||
case VLNK:
|
||||
dent.d_type = DT_LNK;
|
||||
break;
|
||||
|
||||
case VREG:
|
||||
dent.d_type = DT_REG;
|
||||
break;
|
||||
|
||||
case VSOCK:
|
||||
dent.d_type = DT_SOCK;
|
||||
break;
|
||||
|
||||
default:
|
||||
KASSERT(0);
|
||||
}
|
||||
dent.d_namlen = namelen;
|
||||
(void)memcpy(dent.d_name, name, dent.d_namlen);
|
||||
dent.d_reclen = _DIRENT_SIZE(&dent);
|
||||
|
||||
if (dent.d_reclen > uio->uio_resid) {
|
||||
error = -1;
|
||||
} else {
|
||||
error = uiomove(&dent, dent.d_reclen, uio);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Change size of the given vnode.
|
||||
* Caller should execute chfs_update on vp after a successful execution.
|
||||
* The vnode must be locked on entry and remain locked on exit.
|
||||
*/
|
||||
int
|
||||
chfs_chsize(struct vnode *vp, u_quad_t size, kauth_cred_t cred)
|
||||
{
|
||||
struct chfs_mount *chmp;
|
||||
struct chfs_inode *ip;
|
||||
struct buf *bp;
|
||||
int blknum, append;
|
||||
int error = 0;
|
||||
char *buf = NULL;
|
||||
struct chfs_full_dnode *fd;
|
||||
|
||||
ip = VTOI(vp);
|
||||
chmp = ip->chmp;
|
||||
|
||||
dbg("chfs_chsize\n");
|
||||
|
||||
switch (vp->v_type) {
|
||||
case VDIR:
|
||||
return EISDIR;
|
||||
case VLNK:
|
||||
case VREG:
|
||||
if (vp->v_mount->mnt_flag & MNT_RDONLY)
|
||||
return EROFS;
|
||||
break;
|
||||
case VBLK:
|
||||
case VCHR:
|
||||
case VFIFO:
|
||||
return 0;
|
||||
default:
|
||||
return EOPNOTSUPP; /* XXX why not ENODEV? */
|
||||
}
|
||||
|
||||
vflushbuf(vp, 0);
|
||||
|
||||
mutex_enter(&chmp->chm_lock_mountfields);
|
||||
chfs_flush_pending_wbuf(chmp);
|
||||
|
||||
/* handle truncate to zero as a special case */
|
||||
if (size == 0) {
|
||||
dbg("truncate to zero");
|
||||
chfs_truncate_fragtree(ip->chmp,
|
||||
&ip->fragtree, size);
|
||||
chfs_set_vnode_size(vp, size);
|
||||
|
||||
mutex_exit(&chmp->chm_lock_mountfields);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* allocate zeros for the new data */
|
||||
buf = kmem_zalloc(size, KM_SLEEP);
|
||||
bp = getiobuf(vp, true);
|
||||
|
||||
if (ip->size != 0) {
|
||||
/* read the whole data */
|
||||
bp->b_blkno = 0;
|
||||
bp->b_bufsize = bp->b_resid = bp->b_bcount = ip->size;
|
||||
bp->b_data = kmem_alloc(ip->size, KM_SLEEP);
|
||||
|
||||
error = chfs_read_data(chmp, vp, bp);
|
||||
if (error) {
|
||||
mutex_exit(&chmp->chm_lock_mountfields);
|
||||
putiobuf(bp);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/* create the new data */
|
||||
dbg("create new data vap%llu ip%llu\n", size, ip->size);
|
||||
append = size - ip->size;
|
||||
if (append > 0) {
|
||||
memcpy(buf, bp->b_data, ip->size);
|
||||
} else {
|
||||
memcpy(buf, bp->b_data, size);
|
||||
chfs_truncate_fragtree(ip->chmp,
|
||||
&ip->fragtree, size);
|
||||
}
|
||||
|
||||
kmem_free(bp->b_data, ip->size);
|
||||
|
||||
struct chfs_node_frag *lastfrag = frag_last(&ip->fragtree);
|
||||
fd = lastfrag->node;
|
||||
chfs_mark_node_obsolete(chmp, fd->nref);
|
||||
|
||||
blknum = lastfrag->ofs / PAGE_SIZE;
|
||||
lastfrag->size = append > PAGE_SIZE ? PAGE_SIZE : size % PAGE_SIZE;
|
||||
} else {
|
||||
fd = chfs_alloc_full_dnode();
|
||||
blknum = 0;
|
||||
}
|
||||
|
||||
chfs_set_vnode_size(vp, size);
|
||||
|
||||
// write the new data
|
||||
for (bp->b_blkno = blknum; bp->b_blkno * PAGE_SIZE < size; bp->b_blkno++) {
|
||||
uint64_t writesize = MIN(size - bp->b_blkno * PAGE_SIZE, PAGE_SIZE);
|
||||
|
||||
bp->b_bufsize = bp->b_resid = bp->b_bcount = writesize;
|
||||
bp->b_data = kmem_alloc(writesize, KM_SLEEP);
|
||||
|
||||
memcpy(bp->b_data, buf + (bp->b_blkno * PAGE_SIZE), writesize);
|
||||
|
||||
if (bp->b_blkno != blknum) {
|
||||
fd = chfs_alloc_full_dnode();
|
||||
}
|
||||
|
||||
error = chfs_write_flash_dnode(chmp, vp, bp, fd);
|
||||
if (error) {
|
||||
mutex_exit(&chmp->chm_lock_mountfields);
|
||||
kmem_free(bp->b_data, writesize);
|
||||
putiobuf(bp);
|
||||
|
||||
return error;
|
||||
}
|
||||
if (bp->b_blkno != blknum) {
|
||||
chfs_add_full_dnode_to_inode(chmp, ip, fd);
|
||||
}
|
||||
kmem_free(bp->b_data, writesize);
|
||||
}
|
||||
|
||||
mutex_exit(&chmp->chm_lock_mountfields);
|
||||
|
||||
kmem_free(buf, size);
|
||||
putiobuf(bp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#if 0
|
||||
int error;
|
||||
struct chfs_node *node;
|
||||
|
||||
KASSERT(VOP_ISLOCKED(vp));
|
||||
|
||||
node = VP_TO_CHFS_NODE(vp);
|
||||
|
||||
// Decide whether this is a valid operation based on the file type.
|
||||
error = 0;
|
||||
switch (vp->v_type) {
|
||||
case VDIR:
|
||||
return EISDIR;
|
||||
|
||||
case VREG:
|
||||
if (vp->v_mount->mnt_flag & MNT_RDONLY)
|
||||
return EROFS;
|
||||
break;
|
||||
|
||||
case VBLK:
|
||||
case VCHR:
|
||||
case VFIFO:
|
||||
// Allow modifications of special files even if in the file
|
||||
// system is mounted read-only (we are not modifying the
|
||||
// files themselves, but the objects they represent).
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return ENODEV;
|
||||
}
|
||||
|
||||
// Immutable or append-only files cannot be modified, either.
|
||||
if (node->chn_flags & (IMMUTABLE | APPEND))
|
||||
return EPERM;
|
||||
|
||||
error = chfs_truncate(vp, size);
|
||||
// chfs_truncate will raise the NOTE_EXTEND and NOTE_ATTRIB kevents
|
||||
// for us, as will update dn_status; no need to do that here.
|
||||
|
||||
KASSERT(VOP_ISLOCKED(vp));
|
||||
|
||||
return error;
|
||||
#endif
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Change flags of the given vnode.
|
||||
* Caller should execute chfs_update on vp after a successful execution.
|
||||
* The vnode must be locked on entry and remain locked on exit.
|
||||
*/
|
||||
int
|
||||
chfs_chflags(struct vnode *vp, int flags, kauth_cred_t cred)
|
||||
{
|
||||
struct chfs_mount *chmp;
|
||||
struct chfs_inode *ip;
|
||||
int error = 0;
|
||||
|
||||
ip = VTOI(vp);
|
||||
chmp = ip->chmp;
|
||||
|
||||
if (vp->v_mount->mnt_flag & MNT_RDONLY)
|
||||
return EROFS;
|
||||
|
||||
if (kauth_cred_geteuid(cred) != ip->uid &&
|
||||
(error = kauth_authorize_generic(cred,
|
||||
KAUTH_GENERIC_ISSUSER, NULL)))
|
||||
return error;
|
||||
|
||||
if (kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER,
|
||||
NULL) == 0) {
|
||||
if ((ip->flags & (SF_IMMUTABLE | SF_APPEND)) &&
|
||||
kauth_authorize_system(curlwp->l_cred,
|
||||
KAUTH_SYSTEM_CHSYSFLAGS, 0, NULL, NULL, NULL))
|
||||
return EPERM;
|
||||
|
||||
if ((flags & SF_SNAPSHOT) !=
|
||||
(ip->flags & SF_SNAPSHOT))
|
||||
return EPERM;
|
||||
|
||||
ip->flags = flags;
|
||||
} else {
|
||||
if ((ip->flags & (SF_IMMUTABLE | SF_APPEND)) ||
|
||||
(flags & UF_SETTABLE) != flags)
|
||||
return EPERM;
|
||||
|
||||
if ((ip->flags & SF_SETTABLE) !=
|
||||
(flags & SF_SETTABLE))
|
||||
return EPERM;
|
||||
|
||||
ip->flags &= SF_SETTABLE;
|
||||
ip->flags |= (flags & UF_SETTABLE);
|
||||
}
|
||||
ip->iflag |= IN_CHANGE;
|
||||
error = chfs_update(vp, NULL, NULL, UPDATE_WAIT);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (flags & (IMMUTABLE | APPEND))
|
||||
return 0;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
void
|
||||
chfs_itimes(struct chfs_inode *ip, const struct timespec *acc,
|
||||
const struct timespec *mod, const struct timespec *cre)
|
||||
{
|
||||
//dbg("itimes\n");
|
||||
struct timespec now;
|
||||
|
||||
if (!(ip->iflag & (IN_ACCESS | IN_CHANGE | IN_UPDATE | IN_MODIFY))) {
|
||||
return;
|
||||
}
|
||||
|
||||
vfs_timestamp(&now);
|
||||
if (ip->iflag & IN_ACCESS) {
|
||||
if (acc == NULL)
|
||||
acc = &now;
|
||||
ip->atime = acc->tv_sec;
|
||||
}
|
||||
if (ip->iflag & (IN_UPDATE | IN_MODIFY)) {
|
||||
if (mod == NULL)
|
||||
mod = &now;
|
||||
ip->mtime = mod->tv_sec;
|
||||
//ip->i_modrev++;
|
||||
}
|
||||
if (ip->iflag & (IN_CHANGE | IN_MODIFY)) {
|
||||
if (cre == NULL)
|
||||
cre = &now;
|
||||
ip->ctime = cre->tv_sec;
|
||||
}
|
||||
if (ip->iflag & (IN_ACCESS | IN_MODIFY))
|
||||
ip->iflag |= IN_ACCESSED;
|
||||
if (ip->iflag & (IN_UPDATE | IN_CHANGE))
|
||||
ip->iflag |= IN_MODIFIED;
|
||||
ip->iflag &= ~(IN_ACCESS | IN_CHANGE | IN_UPDATE | IN_MODIFY);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
int
|
||||
chfs_update(struct vnode *vp, const struct timespec *acc,
|
||||
const struct timespec *mod, int flags)
|
||||
{
|
||||
|
||||
struct chfs_inode *ip;
|
||||
|
||||
/* XXX ufs_reclaim calls this function unlocked! */
|
||||
// KASSERT(VOP_ISLOCKED(vp));
|
||||
|
||||
#if 0
|
||||
if (flags & UPDATE_CLOSE)
|
||||
; /* XXX Need to do anything special? */
|
||||
#endif
|
||||
|
||||
ip = VTOI(vp);
|
||||
chfs_itimes(ip, acc, mod, NULL);
|
||||
|
||||
// KASSERT(VOP_ISLOCKED(vp));
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
/*
|
||||
int
|
||||
chfs_truncate(struct vnode *vp, off_t length)
|
||||
{
|
||||
bool extended;
|
||||
int error;
|
||||
struct chfs_node *node;
|
||||
printf("CHFS: truncate()\n");
|
||||
|
||||
node = VP_TO_CHFS_NODE(vp);
|
||||
extended = length > node->chn_size;
|
||||
|
||||
if (length < 0) {
|
||||
error = EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (node->chn_size == length) {
|
||||
error = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
error = chfs_reg_resize(vp, length);
|
||||
if (error == 0)
|
||||
node->chn_status |= CHFS_NODE_CHANGED | CHFS_NODE_MODIFIED;
|
||||
|
||||
out:
|
||||
chfs_update(vp, NULL, NULL, 0);
|
||||
|
||||
return error;
|
||||
}*/
|
||||
|
||||
|
|
@ -0,0 +1,844 @@
|
|||
/* $NetBSD: chfs_vfsops.c,v 1.1 2011/11/24 15:51:32 ahoka Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2010 Department of Software Engineering,
|
||||
* University of Szeged, Hungary
|
||||
* Copyright (C) 2010 Tamas Toth <ttoth@inf.u-szeged.hu>
|
||||
* Copyright (C) 2010 Adam Hoka <ahoka@NetBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by the Department of Software Engineering, University of Szeged, Hungary
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/kmem.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/namei.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/conf.h>
|
||||
#include <sys/buf.h>
|
||||
//XXX needed just for debugging
|
||||
#include <sys/fstrans.h>
|
||||
#include <sys/sleepq.h>
|
||||
#include <sys/lockdebug.h>
|
||||
#include <sys/ktrace.h>
|
||||
|
||||
#include <uvm/uvm.h>
|
||||
#include <uvm/uvm_pager.h>
|
||||
#include <ufs/ufs/dir.h>
|
||||
//#include <ufs/ufs/inode.h>
|
||||
#include <ufs/ufs/ufs_extern.h>
|
||||
#include <miscfs/genfs/genfs.h>
|
||||
#include <miscfs/genfs/genfs_node.h>
|
||||
#include <miscfs/specfs/specdev.h>
|
||||
//#include </root/xipffs/netbsd.chfs/chfs.h>
|
||||
//#include </root/xipffs/netbsd.chfs/chfs_args.h>
|
||||
#include "chfs.h"
|
||||
#include "chfs_args.h"
|
||||
|
||||
MODULE(MODULE_CLASS_VFS, chfs, "flash");
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
/* functions */
|
||||
|
||||
static int chfs_mount(struct mount *, const char *, void *, size_t *);
|
||||
static int chfs_unmount(struct mount *, int);
|
||||
static int chfs_root(struct mount *, struct vnode **);
|
||||
static int chfs_vget(struct mount *, ino_t, struct vnode **);
|
||||
static int chfs_fhtovp(struct mount *, struct fid *, struct vnode **);
|
||||
static int chfs_vptofh(struct vnode *, struct fid *, size_t *);
|
||||
static int chfs_start(struct mount *, int);
|
||||
static int chfs_statvfs(struct mount *, struct statvfs *);
|
||||
static int chfs_sync(struct mount *, int, kauth_cred_t);
|
||||
static void chfs_init(void);
|
||||
static void chfs_reinit(void);
|
||||
static void chfs_done(void);
|
||||
static int chfs_snapshot(struct mount *, struct vnode *,
|
||||
struct timespec *);
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
/* structures */
|
||||
|
||||
int
|
||||
chfs_gop_alloc(struct vnode *vp, off_t off, off_t len, int flags,
|
||||
kauth_cred_t cred)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
||||
const struct genfs_ops chfs_genfsops = {
|
||||
.gop_size = genfs_size,
|
||||
.gop_alloc = chfs_gop_alloc,
|
||||
.gop_write = genfs_gop_write,
|
||||
.gop_markupdate = ufs_gop_markupdate,
|
||||
};
|
||||
|
||||
/*
|
||||
static const struct ufs_ops chfs_ufsops = {
|
||||
.uo_itimes = chfs_itimes,
|
||||
.uo_update = chfs_update,
|
||||
};
|
||||
*/
|
||||
|
||||
struct pool chfs_inode_pool;
|
||||
|
||||
/* for looking up the major for flash */
|
||||
extern const struct cdevsw flash_cdevsw;
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static int
|
||||
chfs_mount(struct mount *mp,
|
||||
const char *path, void *data, size_t *data_len)
|
||||
{
|
||||
struct lwp *l = curlwp;
|
||||
struct nameidata nd;
|
||||
struct pathbuf *pb;
|
||||
struct vnode *devvp = NULL;
|
||||
struct ufs_args *args = data;
|
||||
struct ufsmount *ump = NULL;
|
||||
struct chfs_mount *chmp;
|
||||
int err = 0;
|
||||
int xflags;
|
||||
|
||||
dbg("mount()\n");
|
||||
|
||||
if (*data_len < sizeof *args)
|
||||
return EINVAL;
|
||||
|
||||
if (mp->mnt_flag & MNT_GETARGS) {
|
||||
ump = VFSTOUFS(mp);
|
||||
if (ump == NULL)
|
||||
return EIO;
|
||||
memset(args, 0, sizeof *args);
|
||||
args->fspec = NULL;
|
||||
*data_len = sizeof *args;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mp->mnt_flag & MNT_UPDATE) {
|
||||
/* XXX: There is no support yet to update file system
|
||||
* settings. Should be added. */
|
||||
|
||||
return ENODEV;
|
||||
}
|
||||
|
||||
if (args->fspec != NULL) {
|
||||
err = pathbuf_copyin(args->fspec, &pb);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
/*
|
||||
* Look up the name and verify that it's sane.
|
||||
*/
|
||||
NDINIT(&nd, LOOKUP, FOLLOW, pb);
|
||||
if ((err = namei(&nd)) != 0 )
|
||||
return (err);
|
||||
devvp = nd.ni_vp;
|
||||
|
||||
/*
|
||||
* Be sure this is a valid block device
|
||||
*/
|
||||
if (devvp->v_type != VBLK)
|
||||
err = ENOTBLK;
|
||||
else if (bdevsw_lookup(devvp->v_rdev) == NULL)
|
||||
err = ENXIO;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
vrele(devvp);
|
||||
return (err);
|
||||
}
|
||||
|
||||
if (mp->mnt_flag & MNT_RDONLY)
|
||||
xflags = FREAD;
|
||||
else
|
||||
xflags = FREAD|FWRITE;
|
||||
|
||||
err = VOP_OPEN(devvp, xflags, FSCRED);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
|
||||
err = chfs_mountfs(devvp, mp);
|
||||
if (err) {
|
||||
vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
|
||||
(void)VOP_CLOSE(devvp, xflags, NOCRED);
|
||||
VOP_UNLOCK(devvp);
|
||||
goto fail;
|
||||
}
|
||||
ump = VFSTOUFS(mp);
|
||||
chmp = ump->um_chfs;
|
||||
|
||||
vfs_getnewfsid(mp);
|
||||
chmp->chm_fsmp = mp;
|
||||
|
||||
return set_statvfs_info(path,
|
||||
UIO_USERSPACE, args->fspec,
|
||||
UIO_USERSPACE, mp->mnt_op->vfs_name, mp, l);
|
||||
|
||||
fail:
|
||||
vrele(devvp);
|
||||
return (err);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
chfs_mountfs(struct vnode *devvp, struct mount *mp)
|
||||
{
|
||||
struct lwp *l = curlwp;
|
||||
struct proc *p;
|
||||
kauth_cred_t cred;
|
||||
devmajor_t flash_major;
|
||||
dev_t dev;
|
||||
struct ufsmount* ump = NULL;
|
||||
struct chfs_mount* chmp;
|
||||
struct vnode *vp;
|
||||
int err = 0;
|
||||
|
||||
dbg("mountfs()\n");
|
||||
|
||||
dev = devvp->v_rdev;
|
||||
p = l ? l->l_proc : NULL;
|
||||
cred = l ? l->l_cred : NOCRED;
|
||||
|
||||
/* Flush out any old buffers remaining from a previous use. */
|
||||
vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
|
||||
err = vinvalbuf(devvp, V_SAVE, cred, l, 0, 0);
|
||||
VOP_UNLOCK(devvp);
|
||||
if (err)
|
||||
return (err);
|
||||
|
||||
flash_major = cdevsw_lookup_major(&flash_cdevsw);
|
||||
|
||||
if (devvp->v_type != VBLK)
|
||||
err = ENOTBLK;
|
||||
else if (bdevsw_lookup(dev) == NULL)
|
||||
err = ENXIO;
|
||||
else if (major(dev) != flash_major) {
|
||||
dbg("major(dev): %d, flash_major: %d\n",
|
||||
major(dev), flash_major);
|
||||
err = ENODEV;
|
||||
}
|
||||
if (err) {
|
||||
vrele(devvp);
|
||||
return (err);
|
||||
}
|
||||
|
||||
ump = malloc(sizeof(*ump), M_UFSMNT, M_WAITOK);
|
||||
memset(ump, 0, sizeof(*ump));
|
||||
ump->um_fstype = UFS1;
|
||||
//ump->um_ops = &chfs_ufsops;
|
||||
ump->um_chfs = malloc(sizeof(struct chfs_mount),
|
||||
M_UFSMNT, M_WAITOK);
|
||||
memset(ump->um_chfs, 0, sizeof(struct chfs_mount));
|
||||
|
||||
mutex_init(&ump->um_lock, MUTEX_DEFAULT, IPL_NONE);
|
||||
|
||||
/* Get superblock and set flash device number */
|
||||
chmp = ump->um_chfs;
|
||||
if (!chmp)
|
||||
return ENOMEM;
|
||||
|
||||
chmp->chm_ebh = kmem_alloc(sizeof(struct chfs_ebh), KM_SLEEP);
|
||||
|
||||
dbg("[]opening flash: %u\n", (unsigned int)devvp->v_rdev);
|
||||
err = ebh_open(chmp->chm_ebh, devvp->v_rdev);
|
||||
if (err) {
|
||||
dbg("error while opening flash\n");
|
||||
kmem_free(chmp->chm_ebh, sizeof(struct chfs_ebh));
|
||||
free(chmp, M_UFSMNT);
|
||||
return err;
|
||||
}
|
||||
|
||||
//TODO check flash sizes
|
||||
|
||||
chmp->chm_gbl_version = 0;
|
||||
chmp->chm_vnocache_hash = chfs_vnocache_hash_init();
|
||||
|
||||
chmp->chm_blocks = kmem_zalloc(chmp->chm_ebh->peb_nr *
|
||||
sizeof(struct chfs_eraseblock), KM_SLEEP);
|
||||
|
||||
if (!chmp->chm_blocks) {
|
||||
kmem_free(chmp->chm_ebh, chmp->chm_ebh->peb_nr *
|
||||
sizeof(struct chfs_eraseblock));
|
||||
ebh_close(chmp->chm_ebh);
|
||||
free(chmp, M_UFSMNT);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
mutex_init(&chmp->chm_lock_mountfields, MUTEX_DEFAULT, IPL_NONE);
|
||||
mutex_init(&chmp->chm_lock_sizes, MUTEX_DEFAULT, IPL_NONE);
|
||||
mutex_init(&chmp->chm_lock_vnocache, MUTEX_DEFAULT, IPL_NONE);
|
||||
|
||||
//XXX
|
||||
chmp->chm_fs_bmask = -4096;
|
||||
chmp->chm_fs_bsize = 4096;
|
||||
chmp->chm_fs_qbmask = 4095;
|
||||
chmp->chm_fs_bshift = 12;
|
||||
chmp->chm_fs_fmask = -2048;
|
||||
chmp->chm_fs_qfmask = 2047;
|
||||
|
||||
chmp->chm_wbuf_pagesize = chmp->chm_ebh->flash_if->page_size;
|
||||
dbg("wbuf size: %zu\n", chmp->chm_wbuf_pagesize);
|
||||
chmp->chm_wbuf = kmem_alloc(chmp->chm_wbuf_pagesize, KM_SLEEP);
|
||||
rw_init(&chmp->chm_lock_wbuf);
|
||||
|
||||
//init queues
|
||||
TAILQ_INIT(&chmp->chm_free_queue);
|
||||
TAILQ_INIT(&chmp->chm_clean_queue);
|
||||
TAILQ_INIT(&chmp->chm_dirty_queue);
|
||||
TAILQ_INIT(&chmp->chm_very_dirty_queue);
|
||||
TAILQ_INIT(&chmp->chm_erasable_pending_wbuf_queue);
|
||||
TAILQ_INIT(&chmp->chm_erase_pending_queue);
|
||||
|
||||
chfs_calc_trigger_levels(chmp);
|
||||
|
||||
chmp->chm_nr_free_blocks = 0;
|
||||
chmp->chm_nr_erasable_blocks = 0;
|
||||
chmp->chm_max_vno = 2;
|
||||
chmp->chm_checked_vno = 2;
|
||||
chmp->chm_unchecked_size = 0;
|
||||
chmp->chm_used_size = 0;
|
||||
chmp->chm_dirty_size = 0;
|
||||
chmp->chm_wasted_size = 0;
|
||||
chmp->chm_free_size = chmp->chm_ebh->eb_size * chmp->chm_ebh->peb_nr;
|
||||
err = chfs_build_filesystem(chmp);
|
||||
|
||||
if (err) {
|
||||
chfs_vnocache_hash_destroy(chmp->chm_vnocache_hash);
|
||||
kmem_free(chmp->chm_ebh, chmp->chm_ebh->peb_nr *
|
||||
sizeof(struct chfs_eraseblock));
|
||||
ebh_close(chmp->chm_ebh);
|
||||
free(chmp, M_UFSMNT);
|
||||
return EIO;
|
||||
}
|
||||
|
||||
mp->mnt_data = ump;
|
||||
mp->mnt_stat.f_fsidx.__fsid_val[0] = (long)dev;
|
||||
mp->mnt_stat.f_fsidx.__fsid_val[1] = makefstype(MOUNT_CHFS);
|
||||
mp->mnt_stat.f_fsid = mp->mnt_stat.f_fsidx.__fsid_val[0];
|
||||
mp->mnt_stat.f_namemax = MAXNAMLEN;
|
||||
mp->mnt_flag |= MNT_LOCAL;
|
||||
mp->mnt_fs_bshift = PAGE_SHIFT;
|
||||
mp->mnt_dev_bshift = DEV_BSHIFT;
|
||||
mp->mnt_iflag |= IMNT_MPSAFE;
|
||||
ump->um_flags = 0;
|
||||
ump->um_mountp = mp;
|
||||
ump->um_dev = dev;
|
||||
ump->um_devvp = devvp;
|
||||
ump->um_maxfilesize = 1048512 * 1024;
|
||||
/*TODO fill these fields
|
||||
ump->um_nindir =
|
||||
ump->um_lognindir =
|
||||
ump->um_bptrtodb =
|
||||
ump->um_seqinc =
|
||||
ump->um_maxsymlinklen =
|
||||
ump->um_dirblksiz =
|
||||
ump->um_maxfilesize =
|
||||
*/
|
||||
|
||||
/*
|
||||
* Allocate the root vnode.
|
||||
*/
|
||||
err = VFS_VGET(mp, CHFS_ROOTINO, &vp);
|
||||
if (err) {
|
||||
dbg("error: %d while allocating root node\n", err);
|
||||
return err;
|
||||
}
|
||||
vput(vp);
|
||||
|
||||
chfs_gc_thread_start(chmp);
|
||||
mutex_enter(&chmp->chm_lock_mountfields);
|
||||
chfs_gc_trigger(chmp);
|
||||
mutex_exit(&chmp->chm_lock_mountfields);
|
||||
|
||||
devvp->v_specmountpoint = mp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/* ARGSUSED2 */
|
||||
static int
|
||||
chfs_unmount(struct mount *mp, int mntflags)
|
||||
{
|
||||
int flags = 0, i = 0;
|
||||
struct ufsmount *ump;
|
||||
struct chfs_mount *chmp;
|
||||
// struct chfs_vnode_cache *vc, *next;
|
||||
|
||||
if (mntflags & MNT_FORCE)
|
||||
flags |= FORCECLOSE;
|
||||
|
||||
dbg("[START]\n");
|
||||
|
||||
ump = VFSTOUFS(mp);
|
||||
chmp = ump->um_chfs;
|
||||
|
||||
chfs_gc_thread_stop(chmp);
|
||||
|
||||
(void)vflush(mp, NULLVP, flags);
|
||||
|
||||
if (chmp->chm_wbuf_len) {
|
||||
mutex_enter(&chmp->chm_lock_mountfields);
|
||||
chfs_flush_pending_wbuf(chmp);
|
||||
mutex_exit(&chmp->chm_lock_mountfields);
|
||||
}
|
||||
|
||||
for (i = 0; i < chmp->chm_ebh->peb_nr; i++) {
|
||||
chfs_free_node_refs(&chmp->chm_blocks[i]);
|
||||
}
|
||||
|
||||
chfs_vnocache_hash_destroy(chmp->chm_vnocache_hash);
|
||||
|
||||
ebh_close(chmp->chm_ebh);
|
||||
|
||||
rw_destroy(&chmp->chm_lock_wbuf);
|
||||
mutex_destroy(&chmp->chm_lock_vnocache);
|
||||
mutex_destroy(&chmp->chm_lock_sizes);
|
||||
mutex_destroy(&chmp->chm_lock_mountfields);
|
||||
|
||||
if (ump->um_devvp->v_type != VBAD) {
|
||||
ump->um_devvp->v_specmountpoint = NULL;
|
||||
}
|
||||
vn_lock(ump->um_devvp, LK_EXCLUSIVE | LK_RETRY);
|
||||
(void)VOP_CLOSE(ump->um_devvp, FREAD|FWRITE, NOCRED);
|
||||
vput(ump->um_devvp);
|
||||
|
||||
mutex_destroy(&ump->um_lock);
|
||||
|
||||
//free(ump->um_chfs, M_UFSMNT);
|
||||
free(ump, M_UFSMNT);
|
||||
mp->mnt_data = NULL;
|
||||
mp->mnt_flag &= ~MNT_LOCAL;
|
||||
dbg("[END]\n");
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static int
|
||||
chfs_root(struct mount *mp, struct vnode **vpp)
|
||||
{
|
||||
struct vnode *vp;
|
||||
int error;
|
||||
|
||||
if ((error = VFS_VGET(mp, (ino_t)ROOTINO, &vp)) != 0)
|
||||
return error;
|
||||
*vpp = vp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
extern rb_tree_ops_t frag_rbtree_ops;
|
||||
|
||||
static int
|
||||
chfs_vget(struct mount *mp, ino_t ino, struct vnode **vpp)
|
||||
{
|
||||
struct chfs_mount *chmp;
|
||||
struct chfs_inode *ip;
|
||||
struct ufsmount *ump;
|
||||
struct vnode *vp;
|
||||
dev_t dev;
|
||||
int error;
|
||||
struct chfs_vnode_cache* chvc = NULL;
|
||||
struct chfs_node_ref* nref = NULL;
|
||||
struct buf *bp;
|
||||
|
||||
dbg("vget() | ino: %llu\n", ino);
|
||||
|
||||
ump = VFSTOUFS(mp);
|
||||
dev = ump->um_dev;
|
||||
retry:
|
||||
if (!vpp) {
|
||||
vpp = kmem_alloc(sizeof(struct vnode*), KM_SLEEP);
|
||||
}
|
||||
|
||||
if ((*vpp = chfs_ihashget(dev, ino, LK_EXCLUSIVE)) != NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Allocate a new vnode/inode. */
|
||||
if ((error = getnewvnode(VT_CHFS,
|
||||
mp, chfs_vnodeop_p, NULL, &vp)) != 0) {
|
||||
*vpp = NULL;
|
||||
return (error);
|
||||
}
|
||||
ip = pool_get(&chfs_inode_pool, PR_WAITOK);
|
||||
|
||||
mutex_enter(&chfs_hashlock);
|
||||
if ((*vpp = chfs_ihashget(dev, ino, LK_EXCLUSIVE)) != NULL) {
|
||||
mutex_exit(&chfs_hashlock);
|
||||
ungetnewvnode(vp);
|
||||
pool_put(&chfs_inode_pool, ip);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
vp->v_vflag |= VV_LOCKSWORK;
|
||||
|
||||
memset(ip, 0, sizeof(*ip));
|
||||
vp->v_data = ip;
|
||||
ip->vp = vp;
|
||||
ip->ump = ump;
|
||||
ip->chmp = chmp = ump->um_chfs;
|
||||
ip->dev = dev;
|
||||
ip->ino = ino;
|
||||
vp->v_mount = mp;
|
||||
genfs_node_init(vp, &chfs_genfsops);
|
||||
|
||||
rb_tree_init(&ip->fragtree, &frag_rbtree_ops);
|
||||
//mutex_init(&ip->inode_lock, MUTEX_DEFAULT, IPL_NONE);
|
||||
|
||||
chfs_ihashins(ip);
|
||||
mutex_exit(&chfs_hashlock);
|
||||
|
||||
// set root inode
|
||||
if (ino == CHFS_ROOTINO) {
|
||||
dbg("SETROOT\n");
|
||||
vp->v_vflag |= VV_ROOT;
|
||||
vp->v_type = VDIR;
|
||||
ip->mode = IFMT | IEXEC | IWRITE | IREAD;
|
||||
ip->iflag |= (IN_ACCESS | IN_CHANGE | IN_UPDATE);
|
||||
chfs_update(vp, NULL, NULL, UPDATE_WAIT);
|
||||
// ip->dents = NULL; XXXTAILQ
|
||||
TAILQ_INIT(&ip->dents);
|
||||
chfs_set_vnode_size(vp, 512);
|
||||
}
|
||||
|
||||
// set vnode cache
|
||||
mutex_enter(&chmp->chm_lock_vnocache);
|
||||
chvc = chfs_vnode_cache_get(chmp, ino);
|
||||
mutex_exit(&chmp->chm_lock_vnocache);
|
||||
if (!chvc) {
|
||||
dbg("!chvc\n");
|
||||
/* XXX, we cant alloc under a lock, refactor this! */
|
||||
chvc = chfs_vnode_cache_alloc(ino);
|
||||
mutex_enter(&chmp->chm_lock_vnocache);
|
||||
if (ino == CHFS_ROOTINO) {
|
||||
chvc->nlink = 2;
|
||||
chvc->pvno = CHFS_ROOTINO;
|
||||
chfs_vnode_cache_set_state(chmp,
|
||||
chvc, VNO_STATE_CHECKEDABSENT);
|
||||
}
|
||||
chfs_vnode_cache_add(chmp, chvc);
|
||||
mutex_exit(&chmp->chm_lock_vnocache);
|
||||
|
||||
ip->chvc = chvc;
|
||||
TAILQ_INIT(&ip->dents);
|
||||
} else {
|
||||
dbg("chvc\n");
|
||||
ip->chvc = chvc;
|
||||
// if we have a vnode cache, the node is already on flash, so read it
|
||||
if (ino == CHFS_ROOTINO) {
|
||||
chvc->pvno = CHFS_ROOTINO;
|
||||
TAILQ_INIT(&chvc->scan_dirents);
|
||||
} else {
|
||||
chfs_readvnode(mp, ino, &vp);
|
||||
}
|
||||
|
||||
mutex_enter(&chmp->chm_lock_mountfields);
|
||||
// init type specific things
|
||||
switch (vp->v_type) {
|
||||
case VDIR:
|
||||
nref = chvc->dirents;
|
||||
while (nref &&
|
||||
(struct chfs_vnode_cache *)nref != chvc) {
|
||||
chfs_readdirent(mp, nref, ip);
|
||||
nref = nref->nref_next;
|
||||
}
|
||||
chfs_set_vnode_size(vp, 512);
|
||||
break;
|
||||
case VREG:
|
||||
case VSOCK:
|
||||
//build the fragtree of the vnode
|
||||
dbg("read_inode_internal | ino: %llu\n", ip->ino);
|
||||
error = chfs_read_inode(chmp, ip);
|
||||
if (error) {
|
||||
vput(vp);
|
||||
*vpp = NULL;
|
||||
mutex_exit(&chmp->chm_lock_mountfields);
|
||||
return (error);
|
||||
}
|
||||
break;
|
||||
case VLNK:
|
||||
//build the fragtree of the vnode
|
||||
dbg("read_inode_internal | ino: %llu\n", ip->ino);
|
||||
error = chfs_read_inode_internal(chmp, ip);
|
||||
if (error) {
|
||||
vput(vp);
|
||||
*vpp = NULL;
|
||||
mutex_exit(&chmp->chm_lock_mountfields);
|
||||
return (error);
|
||||
}
|
||||
|
||||
dbg("size: %llu\n", ip->size);
|
||||
bp = getiobuf(vp, true);
|
||||
bp->b_blkno = 0;
|
||||
bp->b_bufsize = bp->b_resid =
|
||||
bp->b_bcount = ip->size;
|
||||
bp->b_data = kmem_alloc(ip->size, KM_SLEEP);
|
||||
chfs_read_data(chmp, vp, bp);
|
||||
if (!ip->target)
|
||||
ip->target = kmem_alloc(ip->size,
|
||||
KM_SLEEP);
|
||||
memcpy(ip->target, bp->b_data, ip->size);
|
||||
kmem_free(bp->b_data, ip->size);
|
||||
putiobuf(bp);
|
||||
|
||||
break;
|
||||
case VCHR:
|
||||
case VBLK:
|
||||
case VFIFO:
|
||||
//build the fragtree of the vnode
|
||||
dbg("read_inode_internal | ino: %llu\n", ip->ino);
|
||||
error = chfs_read_inode_internal(chmp, ip);
|
||||
if (error) {
|
||||
vput(vp);
|
||||
*vpp = NULL;
|
||||
mutex_exit(&chmp->chm_lock_mountfields);
|
||||
return (error);
|
||||
}
|
||||
|
||||
bp = getiobuf(vp, true);
|
||||
bp->b_blkno = 0;
|
||||
bp->b_bufsize = bp->b_resid =
|
||||
bp->b_bcount = sizeof(dev_t);
|
||||
bp->b_data = kmem_alloc(sizeof(dev_t), KM_SLEEP);
|
||||
chfs_read_data(chmp, vp, bp);
|
||||
memcpy(&ip->rdev,
|
||||
bp->b_data, sizeof(dev_t));
|
||||
kmem_free(bp->b_data, sizeof(dev_t));
|
||||
putiobuf(bp);
|
||||
if (vp->v_type == VFIFO)
|
||||
vp->v_op = chfs_fifoop_p;
|
||||
else {
|
||||
vp->v_op = chfs_specop_p;
|
||||
spec_node_init(vp, ip->rdev);
|
||||
}
|
||||
|
||||
break;
|
||||
case VNON:
|
||||
case VBAD:
|
||||
break;
|
||||
}
|
||||
mutex_exit(&chmp->chm_lock_mountfields);
|
||||
|
||||
}
|
||||
|
||||
/* finish inode initalization */
|
||||
ip->devvp = ump->um_devvp;
|
||||
vref(ip->devvp);
|
||||
|
||||
uvm_vnp_setsize(vp, ip->size);
|
||||
*vpp = vp;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static int
|
||||
chfs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp)
|
||||
{
|
||||
return ENODEV;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static int
|
||||
chfs_vptofh(struct vnode *vp, struct fid *fhp, size_t *fh_size)
|
||||
{
|
||||
return ENODEV;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static int
|
||||
chfs_start(struct mount *mp, int flags)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/* ARGSUSED2 */
|
||||
static int
|
||||
chfs_statvfs(struct mount *mp, struct statvfs *sbp)
|
||||
{
|
||||
struct chfs_mount *chmp;
|
||||
struct ufsmount *ump;
|
||||
dbg("statvfs\n");
|
||||
|
||||
ump = VFSTOUFS(mp);
|
||||
chmp = ump->um_chfs;
|
||||
|
||||
sbp->f_flag = mp->mnt_flag;
|
||||
sbp->f_bsize = chmp->chm_ebh->eb_size;
|
||||
sbp->f_frsize = chmp->chm_ebh->eb_size;
|
||||
sbp->f_iosize = chmp->chm_ebh->eb_size;
|
||||
|
||||
sbp->f_blocks = chmp->chm_ebh->peb_nr;
|
||||
sbp->f_files = 0;
|
||||
sbp->f_bavail = chmp->chm_nr_free_blocks - chmp->chm_resv_blocks_write;
|
||||
#if 0
|
||||
printf("chmp->chm_nr_free_blocks: %jd\n",
|
||||
(intmax_t )chmp->chm_nr_free_blocks);
|
||||
printf("chmp->chm_resv_blocks_write: %jd\n",
|
||||
(intmax_t) chmp->chm_resv_blocks_write);
|
||||
printf("chmp->chm_ebh->peb_nr: %jd\n",
|
||||
(intmax_t) chmp->chm_ebh->peb_nr);
|
||||
#endif
|
||||
|
||||
sbp->f_bfree = chmp->chm_nr_free_blocks;
|
||||
sbp->f_bresvd = chmp->chm_resv_blocks_write;
|
||||
|
||||
/* FFS specific */
|
||||
sbp->f_ffree = 0;
|
||||
sbp->f_favail = 0;
|
||||
sbp->f_fresvd = 0;
|
||||
|
||||
copy_statvfs_info(sbp, mp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/* ARGSUSED0 */
|
||||
static int
|
||||
chfs_sync(struct mount *mp, int waitfor,
|
||||
kauth_cred_t uc)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static void
|
||||
chfs_init(void)
|
||||
{
|
||||
chfs_alloc_pool_caches();
|
||||
chfs_ihashinit();
|
||||
pool_init(&chfs_inode_pool, sizeof(struct chfs_inode), 0, 0, 0,
|
||||
"chfsinopl", &pool_allocator_nointr, IPL_NONE);
|
||||
ufs_init();
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static void
|
||||
chfs_reinit(void)
|
||||
{
|
||||
chfs_ihashreinit();
|
||||
ufs_reinit();
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static void
|
||||
chfs_done(void)
|
||||
{
|
||||
ufs_done();
|
||||
chfs_ihashdone();
|
||||
pool_destroy(&chfs_inode_pool);
|
||||
chfs_destroy_pool_caches();
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static int
|
||||
chfs_snapshot(struct mount *mp, struct vnode *vp,
|
||||
struct timespec *ctime)
|
||||
{
|
||||
return ENODEV;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* chfs vfs operations.
|
||||
*/
|
||||
|
||||
extern const struct vnodeopv_desc chfs_fifoop_opv_desc;
|
||||
extern const struct vnodeopv_desc chfs_specop_opv_desc;
|
||||
extern const struct vnodeopv_desc chfs_vnodeop_opv_desc;
|
||||
|
||||
const struct vnodeopv_desc * const chfs_vnodeopv_descs[] = {
|
||||
&chfs_fifoop_opv_desc,
|
||||
&chfs_specop_opv_desc,
|
||||
&chfs_vnodeop_opv_desc,
|
||||
NULL,
|
||||
};
|
||||
|
||||
struct vfsops chfs_vfsops = {
|
||||
MOUNT_CHFS, /* vfs_name */
|
||||
sizeof (struct chfs_args),
|
||||
chfs_mount, /* vfs_mount */
|
||||
chfs_start, /* vfs_start */
|
||||
chfs_unmount, /* vfs_unmount */
|
||||
chfs_root, /* vfs_root */
|
||||
ufs_quotactl, /* vfs_quotactl */
|
||||
chfs_statvfs, /* vfs_statvfs */
|
||||
chfs_sync, /* vfs_sync */
|
||||
chfs_vget, /* vfs_vget */
|
||||
chfs_fhtovp, /* vfs_fhtovp */
|
||||
chfs_vptofh, /* vfs_vptofh */
|
||||
chfs_init, /* vfs_init */
|
||||
chfs_reinit, /* vfs_reinit */
|
||||
chfs_done, /* vfs_done */
|
||||
NULL, /* vfs_mountroot */
|
||||
chfs_snapshot, /* vfs_snapshot */
|
||||
vfs_stdextattrctl, /* vfs_extattrctl */
|
||||
(void *)eopnotsupp, /* vfs_suspendctl */
|
||||
genfs_renamelock_enter,
|
||||
genfs_renamelock_exit,
|
||||
(void *)eopnotsupp,
|
||||
chfs_vnodeopv_descs,
|
||||
0, /* vfs_refcount */
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
static int
|
||||
chfs_modcmd(modcmd_t cmd, void *arg)
|
||||
{
|
||||
switch (cmd) {
|
||||
case MODULE_CMD_INIT:
|
||||
return vfs_attach(&chfs_vfsops);
|
||||
case MODULE_CMD_FINI:
|
||||
return vfs_detach(&chfs_vfsops);
|
||||
default:
|
||||
return ENOTTY;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,393 @@
|
|||
/* $NetBSD: chfs_vnode.c,v 1.1 2011/11/24 15:51:32 ahoka Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2010 Department of Software Engineering,
|
||||
* University of Szeged, Hungary
|
||||
* Copyright (C) 2010 Tamas Toth <ttoth@inf.u-szeged.hu>
|
||||
* Copyright (C) 2010 Adam Hoka <ahoka@NetBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by the Department of Software Engineering, University of Szeged, Hungary
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "chfs.h"
|
||||
#include "chfs_inode.h"
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/kauth.h>
|
||||
#include <sys/namei.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/buf.h>
|
||||
|
||||
struct vnode *
|
||||
chfs_vnode_lookup(struct chfs_mount *chmp, ino_t vno)
|
||||
{
|
||||
struct vnode *vp;
|
||||
struct chfs_inode *ip;
|
||||
|
||||
TAILQ_FOREACH(vp, &chmp->chm_fsmp->mnt_vnodelist, v_mntvnodes) {
|
||||
ip = VTOI(vp);
|
||||
if (ip && ip->ino == vno)
|
||||
return vp;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
chfs_readvnode(struct mount* mp, ino_t ino, struct vnode** vpp)
|
||||
{
|
||||
struct ufsmount* ump = VFSTOUFS(mp);
|
||||
struct chfs_mount *chmp = ump->um_chfs;
|
||||
struct chfs_vnode_cache *chvc;
|
||||
struct chfs_flash_vnode *chfvn;
|
||||
struct chfs_inode *ip;
|
||||
int err;
|
||||
char* buf;
|
||||
size_t retlen, len;
|
||||
struct vnode* vp = NULL;
|
||||
dbg("readvnode | ino: %llu\n", ino);
|
||||
|
||||
len = sizeof(struct chfs_flash_vnode);
|
||||
|
||||
KASSERT(vpp != NULL);
|
||||
|
||||
if (vpp != NULL) {
|
||||
vp = *vpp;
|
||||
}
|
||||
|
||||
ip = VTOI(vp);
|
||||
chvc = ip->chvc;
|
||||
|
||||
if (chvc && ino != CHFS_ROOTINO) {
|
||||
/* debug... */
|
||||
printf("readvnode; offset: %" PRIu32 ", lnr: %d\n",
|
||||
CHFS_GET_OFS(chvc->v->nref_offset), chvc->v->nref_lnr);
|
||||
|
||||
KASSERT((void *)chvc != (void *)chvc->v);
|
||||
|
||||
buf = kmem_alloc(len, KM_SLEEP);
|
||||
err = chfs_read_leb(chmp, chvc->v->nref_lnr, buf,
|
||||
CHFS_GET_OFS(chvc->v->nref_offset), len, &retlen);
|
||||
if (err)
|
||||
return err;
|
||||
if (retlen != len) {
|
||||
chfs_err("Error reading vnode: read: %zu insted of: %zu\n",
|
||||
len, retlen);
|
||||
return EIO;
|
||||
}
|
||||
chfvn = (struct chfs_flash_vnode*)buf;
|
||||
chfs_set_vnode_size(vp, chfvn->dn_size);
|
||||
ip->mode = chfvn->mode;
|
||||
vp->v_type = IFTOVT(ip->mode);
|
||||
ip->version = chfvn->version;
|
||||
//ip->chvc->highest_version = ip->version;
|
||||
ip->uid = chfvn->uid;
|
||||
ip->gid = chfvn->gid;
|
||||
ip->atime = chfvn->atime;
|
||||
ip->mtime = chfvn->mtime;
|
||||
ip->ctime = chfvn->ctime;
|
||||
kmem_free(buf, len);
|
||||
}
|
||||
|
||||
|
||||
*vpp = vp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
chfs_readdirent(struct mount *mp, struct chfs_node_ref *chnr, struct chfs_inode *pdir)
|
||||
{
|
||||
struct ufsmount *ump = VFSTOUFS(mp);
|
||||
struct chfs_mount *chmp = ump->um_chfs;
|
||||
struct chfs_flash_dirent_node chfdn;
|
||||
struct chfs_dirent *fd;//, *pdents;
|
||||
size_t len = sizeof(struct chfs_flash_dirent_node);
|
||||
// struct chfs_vnode_cache* parent;
|
||||
size_t retlen;
|
||||
int err = 0;
|
||||
|
||||
// parent = chfs_get_vnode_cache(chmp, pdir->ino);
|
||||
|
||||
//read flash_dirent_node
|
||||
err = chfs_read_leb(chmp, chnr->nref_lnr, (char *)&chfdn,
|
||||
CHFS_GET_OFS(chnr->nref_offset), len, &retlen);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
if (retlen != len) {
|
||||
chfs_err("Error reading vnode: read: %zu insted of: %zu\n",
|
||||
retlen, len);
|
||||
return EIO;
|
||||
}
|
||||
|
||||
//set fields of dirent
|
||||
fd = chfs_alloc_dirent(chfdn.nsize + 1);
|
||||
fd->version = chfdn.version;
|
||||
fd->vno = chfdn.vno;
|
||||
fd->type = chfdn.dtype;
|
||||
fd->nsize = chfdn.nsize;
|
||||
// fd->next = NULL;
|
||||
|
||||
err = chfs_read_leb(chmp, chnr->nref_lnr, fd->name,
|
||||
CHFS_GET_OFS(chnr->nref_offset) + len, chfdn.nsize, &retlen);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (retlen != chfdn.nsize) {
|
||||
chfs_err("Error reading vnode: read: %zu insted of: %zu\n",
|
||||
len, retlen);
|
||||
return EIO;
|
||||
}
|
||||
|
||||
fd->name[fd->nsize] = 0;
|
||||
fd->nref = chnr;
|
||||
|
||||
chfs_add_fd_to_inode(chmp, pdir, fd);
|
||||
/*
|
||||
pdents = pdir->i_chfs_ext.dents;
|
||||
if (!pdents)
|
||||
pdir->i_chfs_ext.dents = fd;
|
||||
else {
|
||||
while (pdents->next != NULL) {
|
||||
pdents = pdents->next;
|
||||
}
|
||||
pdents->next = fd;
|
||||
}
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate a new inode.
|
||||
*/
|
||||
int
|
||||
chfs_makeinode(int mode, struct vnode *dvp, struct vnode **vpp,
|
||||
struct componentname *cnp, int type)
|
||||
{
|
||||
struct chfs_inode *ip, *pdir;
|
||||
struct vnode *vp;
|
||||
struct ufsmount* ump = VFSTOUFS(dvp->v_mount);
|
||||
struct chfs_mount* chmp = ump->um_chfs;
|
||||
struct chfs_vnode_cache* chvc;
|
||||
int error, ismember = 0;
|
||||
ino_t vno;
|
||||
struct chfs_dirent *nfd;//, *fd;
|
||||
|
||||
dbg("makeinode\n");
|
||||
pdir = VTOI(dvp);
|
||||
|
||||
*vpp = NULL;
|
||||
|
||||
vno = ++(chmp->chm_max_vno);
|
||||
|
||||
error = VFS_VGET(dvp->v_mount, vno, &vp);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
mutex_enter(&chmp->chm_lock_vnocache);
|
||||
chvc = chfs_vnode_cache_get(chmp, vno);
|
||||
mutex_exit(&chmp->chm_lock_vnocache);
|
||||
|
||||
chvc->pvno = pdir->ino;
|
||||
chvc->vno_version = kmem_alloc(sizeof(uint64_t), KM_SLEEP);
|
||||
*(chvc->vno_version) = 1;
|
||||
if (type != VDIR)
|
||||
chvc->nlink = 1;
|
||||
else
|
||||
chvc->nlink = 2;
|
||||
// chfs_vnode_cache_set_state(chmp, chvc, VNO_STATE_CHECKEDABSENT);
|
||||
chvc->state = VNO_STATE_CHECKEDABSENT;
|
||||
|
||||
ip = VTOI(vp);
|
||||
ip->ino = vno;
|
||||
|
||||
if (type == VDIR)
|
||||
chfs_set_vnode_size(vp, 512);
|
||||
else
|
||||
chfs_set_vnode_size(vp, 0);
|
||||
|
||||
ip->uid = kauth_cred_geteuid(cnp->cn_cred);
|
||||
ip->gid = kauth_cred_getegid(cnp->cn_cred);
|
||||
ip->version = 1;
|
||||
ip->iflag |= (IN_ACCESS | IN_CHANGE | IN_UPDATE);
|
||||
|
||||
ip->chvc = chvc;
|
||||
//ip->chvc->highest_version = 1;
|
||||
ip->target = NULL;
|
||||
|
||||
ip->mode = mode;
|
||||
vp->v_type = type; /* Rest init'd in getnewvnode(). */
|
||||
if ((ip->mode & ISGID) && (kauth_cred_ismember_gid(cnp->cn_cred,
|
||||
ip->gid, &ismember) != 0 || !ismember) &&
|
||||
kauth_authorize_generic(cnp->cn_cred, KAUTH_GENERIC_ISSUSER, NULL))
|
||||
ip->mode &= ~ISGID;
|
||||
|
||||
chfs_update(vp, NULL, NULL, UPDATE_WAIT);
|
||||
|
||||
mutex_enter(&chmp->chm_lock_mountfields);
|
||||
|
||||
//write inode to flash
|
||||
error = chfs_write_flash_vnode(chmp, ip, ALLOC_NORMAL);
|
||||
if (error) {
|
||||
mutex_exit(&chmp->chm_lock_mountfields);
|
||||
vput(vp);
|
||||
vput(dvp);
|
||||
return error;
|
||||
}
|
||||
//update parent directory and write it to the flash
|
||||
pdir->iflag |= (IN_ACCESS | IN_CHANGE | IN_MODIFY | IN_UPDATE);
|
||||
chfs_update(dvp, NULL, NULL, UPDATE_WAIT);
|
||||
|
||||
error = chfs_write_flash_vnode(chmp, pdir, ALLOC_NORMAL);
|
||||
if (error) {
|
||||
mutex_exit(&chmp->chm_lock_mountfields);
|
||||
vput(vp);
|
||||
vput(dvp);
|
||||
return error;
|
||||
}
|
||||
vput(dvp);
|
||||
|
||||
//set up node's full dirent
|
||||
nfd = chfs_alloc_dirent(cnp->cn_namelen + 1);
|
||||
nfd->vno = ip->ino;
|
||||
nfd->version = (++pdir->chvc->highest_version);
|
||||
nfd->type = type;
|
||||
// nfd->next = NULL;
|
||||
nfd->nsize = cnp->cn_namelen;
|
||||
memcpy(&(nfd->name), cnp->cn_nameptr, cnp->cn_namelen);
|
||||
nfd->name[nfd->nsize] = 0;
|
||||
nfd->nhash = hash32_buf(nfd->name, cnp->cn_namelen, HASH32_BUF_INIT);
|
||||
|
||||
// write out direntry
|
||||
error = chfs_write_flash_dirent(chmp, pdir, ip, nfd, ip->ino, ALLOC_NORMAL);
|
||||
if (error) {
|
||||
mutex_exit(&chmp->chm_lock_mountfields);
|
||||
vput(vp);
|
||||
return error;
|
||||
}
|
||||
|
||||
//TODO set parent's dir times
|
||||
|
||||
chfs_add_fd_to_inode(chmp, pdir, nfd);
|
||||
/*
|
||||
fd = pdir->i_chfs_ext.dents;
|
||||
if (!fd)
|
||||
pdir->i_chfs_ext.dents = nfd;
|
||||
else {
|
||||
while (fd->next != NULL) {
|
||||
fd = fd->next;
|
||||
}
|
||||
fd->next = nfd;
|
||||
}
|
||||
*/
|
||||
//pdir->i_nlink++;
|
||||
pdir->chvc->nlink++;
|
||||
|
||||
mutex_exit(&chmp->chm_lock_mountfields);
|
||||
|
||||
*vpp = vp;
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
chfs_set_vnode_size(struct vnode *vp, size_t size)
|
||||
{
|
||||
struct chfs_inode *ip;
|
||||
|
||||
KASSERT(vp != NULL);
|
||||
|
||||
ip = VTOI(vp);
|
||||
KASSERT(ip != NULL);
|
||||
|
||||
ip->size = size;
|
||||
vp->v_size = vp->v_writesize = size;
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
chfs_change_size_free(struct chfs_mount *chmp,
|
||||
struct chfs_eraseblock *cheb, int change)
|
||||
{
|
||||
KASSERT(mutex_owned(&chmp->chm_lock_sizes));
|
||||
KASSERT((int)(chmp->chm_free_size + change) >= 0);
|
||||
KASSERT((int)(cheb->free_size + change) >= 0);
|
||||
KASSERT((int)(cheb->free_size + change) <= chmp->chm_ebh->eb_size);
|
||||
chmp->chm_free_size += change;
|
||||
cheb->free_size += change;
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
chfs_change_size_dirty(struct chfs_mount *chmp,
|
||||
struct chfs_eraseblock *cheb, int change)
|
||||
{
|
||||
KASSERT(mutex_owned(&chmp->chm_lock_sizes));
|
||||
KASSERT((int)(chmp->chm_dirty_size + change) >= 0);
|
||||
KASSERT((int)(cheb->dirty_size + change) >= 0);
|
||||
KASSERT((int)(cheb->dirty_size + change) <= chmp->chm_ebh->eb_size);
|
||||
chmp->chm_dirty_size += change;
|
||||
cheb->dirty_size += change;
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
chfs_change_size_unchecked(struct chfs_mount *chmp,
|
||||
struct chfs_eraseblock *cheb, int change)
|
||||
{
|
||||
KASSERT(mutex_owned(&chmp->chm_lock_sizes));
|
||||
KASSERT((int)(chmp->chm_unchecked_size + change) >= 0);
|
||||
KASSERT((int)(cheb->unchecked_size + change) >= 0);
|
||||
KASSERT((int)(cheb->unchecked_size + change) <= chmp->chm_ebh->eb_size);
|
||||
chmp->chm_unchecked_size += change;
|
||||
cheb->unchecked_size += change;
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
chfs_change_size_used(struct chfs_mount *chmp,
|
||||
struct chfs_eraseblock *cheb, int change)
|
||||
{
|
||||
KASSERT(mutex_owned(&chmp->chm_lock_sizes));
|
||||
KASSERT((int)(chmp->chm_used_size + change) >= 0);
|
||||
KASSERT((int)(cheb->used_size + change) >= 0);
|
||||
KASSERT((int)(cheb->used_size + change) <= chmp->chm_ebh->eb_size);
|
||||
chmp->chm_used_size += change;
|
||||
cheb->used_size += change;
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
chfs_change_size_wasted(struct chfs_mount *chmp,
|
||||
struct chfs_eraseblock *cheb, int change)
|
||||
{
|
||||
KASSERT(mutex_owned(&chmp->chm_lock_sizes));
|
||||
KASSERT((int)(chmp->chm_wasted_size + change) >= 0);
|
||||
KASSERT((int)(cheb->wasted_size + change) >= 0);
|
||||
KASSERT((int)(cheb->wasted_size + change) <= chmp->chm_ebh->eb_size);
|
||||
chmp->chm_wasted_size += change;
|
||||
cheb->wasted_size += change;
|
||||
return;
|
||||
}
|
||||
|
|
@ -0,0 +1,165 @@
|
|||
/* $NetBSD: chfs_vnode_cache.c,v 1.1 2011/11/24 15:51:32 ahoka Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2010 Department of Software Engineering,
|
||||
* University of Szeged, Hungary
|
||||
* Copyright (C) 2010 Tamas Toth <ttoth@inf.u-szeged.hu>
|
||||
* Copyright (C) 2010 Adam Hoka <ahoka@NetBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by the Department of Software Engineering, University of Szeged, Hungary
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "chfs.h"
|
||||
#include <sys/pool.h>
|
||||
|
||||
struct chfs_vnode_cache **
|
||||
chfs_vnocache_hash_init(void)
|
||||
{
|
||||
return kmem_zalloc(VNODECACHE_SIZE *
|
||||
sizeof(struct chfs_vnode_cache *), KM_SLEEP);
|
||||
}
|
||||
|
||||
/**
|
||||
* chfs_set_vnode_cache_state - set state of a vnode_cache
|
||||
* @chmp: fs super block info
|
||||
* @vc: vnode_cache
|
||||
* @state: new state
|
||||
*/
|
||||
void
|
||||
chfs_vnode_cache_set_state(struct chfs_mount *chmp,
|
||||
struct chfs_vnode_cache* vc, int state)
|
||||
{
|
||||
/* XXX do we really need locking here? */
|
||||
KASSERT(mutex_owned(&chmp->chm_lock_vnocache));
|
||||
vc->state = state;
|
||||
}
|
||||
|
||||
/**
|
||||
* chfs_get_vnode_cache - get a vnode_cache from the vnocache_hash
|
||||
* @chmp: fs super block info
|
||||
* @ino: inode for search
|
||||
* Returns the vnode_cache.
|
||||
*/
|
||||
struct chfs_vnode_cache *
|
||||
chfs_vnode_cache_get(struct chfs_mount *chmp, ino_t vno)
|
||||
{
|
||||
struct chfs_vnode_cache* ret;
|
||||
|
||||
KASSERT(mutex_owned(&chmp->chm_lock_vnocache));
|
||||
|
||||
ret = chmp->chm_vnocache_hash[vno % VNODECACHE_SIZE];
|
||||
|
||||
if (ret == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (ret && ret->vno < vno) {
|
||||
ret = ret->next;
|
||||
}
|
||||
|
||||
if (ret && ret->vno != vno) {
|
||||
ret = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* chfs_add_vnode_cache - add a vnode_cache to the vnocache_hash
|
||||
* @chmp: fs super block info
|
||||
* @new: new vnode_cache
|
||||
*/
|
||||
void
|
||||
chfs_vnode_cache_add(struct chfs_mount *chmp,
|
||||
struct chfs_vnode_cache* new)
|
||||
{
|
||||
struct chfs_vnode_cache** prev;
|
||||
|
||||
KASSERT(mutex_owned(&chmp->chm_lock_vnocache));
|
||||
|
||||
if (!new->vno) {
|
||||
new->vno = ++chmp->chm_max_vno;
|
||||
}
|
||||
|
||||
prev = &chmp->chm_vnocache_hash[new->vno % VNODECACHE_SIZE];
|
||||
|
||||
while ((*prev) && (*prev)->vno < new->vno) {
|
||||
prev = &((*prev)->next);
|
||||
}
|
||||
new->next = *prev;
|
||||
*prev = new;
|
||||
}
|
||||
|
||||
/**
|
||||
* chfs_del_vnode_cache - del a vnode_cache from the vnocache_hash
|
||||
* @chmp: fs super block info
|
||||
* @old: old vnode_cache
|
||||
*/
|
||||
void
|
||||
chfs_vnode_cache_remove(struct chfs_mount *chmp,
|
||||
struct chfs_vnode_cache* old)
|
||||
{
|
||||
struct chfs_vnode_cache** prev;
|
||||
|
||||
KASSERT(mutex_owned(&chmp->chm_lock_vnocache));
|
||||
|
||||
prev = &chmp->chm_vnocache_hash[old->vno % VNODECACHE_SIZE];
|
||||
while ((*prev) && (*prev)->vno < old->vno) {
|
||||
prev = &(*prev)->next;
|
||||
}
|
||||
|
||||
if ((*prev) == old) {
|
||||
*prev = old->next;
|
||||
}
|
||||
|
||||
if (old->state != VNO_STATE_READING &&
|
||||
old->state != VNO_STATE_CLEARING) {
|
||||
chfs_vnode_cache_free(old);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* chfs_free_vnode_caches - free the vnocache_hash
|
||||
* @chmp: fs super block info
|
||||
*/
|
||||
void
|
||||
chfs_vnocache_hash_destroy(struct chfs_vnode_cache **hash)
|
||||
{
|
||||
struct chfs_vnode_cache *this, *next;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < VNODECACHE_SIZE; i++) {
|
||||
this = hash[i];
|
||||
while (this) {
|
||||
next = this->next;
|
||||
chfs_vnode_cache_free(this);
|
||||
this = next;
|
||||
}
|
||||
hash[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,258 @@
|
|||
/* $NetBSD: chfs_wbuf.c,v 1.1 2011/11/24 15:51:32 ahoka Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2010 Department of Software Engineering,
|
||||
* University of Szeged, Hungary
|
||||
* Copyright (C) 2010 Tamas Toth <ttoth@inf.u-szeged.hu>
|
||||
* Copyright (C) 2010 Adam Hoka <ahoka@NetBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by the Department of Software Engineering, University of Szeged, Hungary
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <dev/flash/flash.h>
|
||||
#include <sys/uio.h>
|
||||
#include "chfs.h"
|
||||
//#include </root/xipffs/netbsd.chfs/chfs.h>
|
||||
|
||||
#define DBG_WBUF 1
|
||||
|
||||
#define PAD(x) (((x)+3)&~3)
|
||||
|
||||
#define EB_ADDRESS(x) ( ((unsigned long)(x) / chmp->chm_ebh->eb_size) * chmp->chm_ebh->eb_size )
|
||||
|
||||
#define PAGE_DIV(x) ( ((unsigned long)(x) / (unsigned long)(chmp->chm_wbuf_pagesize)) * (unsigned long)(chmp->chm_wbuf_pagesize) )
|
||||
#define PAGE_MOD(x) ( (unsigned long)(x) % (unsigned long)(chmp->chm_wbuf_pagesize) )
|
||||
|
||||
/*
|
||||
// test functions
|
||||
int wbuf_test(void);
|
||||
void wbuf_test_erase_flash(struct chfs_mount*);
|
||||
void wbuf_test_callback(struct erase_instruction*);
|
||||
*/
|
||||
|
||||
#define NOPAD 0
|
||||
#define SETPAD 1
|
||||
|
||||
|
||||
/**
|
||||
* chfs_flush_wbuf - write wbuf to the flash
|
||||
* @chmp: super block info
|
||||
* @pad: padding (NOPAD / SETPAD)
|
||||
* Returns zero in case of success.
|
||||
*/
|
||||
static int
|
||||
chfs_flush_wbuf(struct chfs_mount *chmp, int pad)
|
||||
{
|
||||
int ret=0;
|
||||
size_t retlen = 0;
|
||||
|
||||
KASSERT(mutex_owned(&chmp->chm_lock_mountfields));
|
||||
KASSERT(mutex_owned(&chmp->chm_lock_sizes));
|
||||
KASSERT(rw_write_held(&chmp->chm_lock_wbuf));
|
||||
|
||||
if (pad) {
|
||||
chmp->chm_wbuf_len = PAD(chmp->chm_wbuf_len);
|
||||
memset(chmp->chm_wbuf + chmp->chm_wbuf_len, 0, chmp->chm_wbuf_pagesize - chmp->chm_wbuf_len);
|
||||
|
||||
struct chfs_flash_padding_node* padnode = (void*)(chmp->chm_wbuf + chmp->chm_wbuf_len);
|
||||
padnode->magic = htole16(CHFS_FS_MAGIC_BITMASK);
|
||||
padnode->type = htole16(CHFS_NODETYPE_PADDING);
|
||||
padnode->length = htole32(chmp->chm_wbuf_pagesize - chmp->chm_wbuf_len);
|
||||
padnode->hdr_crc = htole32(crc32(0, (uint8_t *)padnode, sizeof(*padnode)-4));
|
||||
|
||||
struct chfs_node_ref *nref;
|
||||
nref = chfs_alloc_node_ref(chmp->chm_nextblock);
|
||||
nref->nref_offset = chmp->chm_wbuf_ofs + chmp->chm_wbuf_len;
|
||||
nref->nref_offset = CHFS_GET_OFS(nref->nref_offset) |
|
||||
CHFS_OBSOLETE_NODE_MASK;
|
||||
chmp->chm_wbuf_len = chmp->chm_wbuf_pagesize;
|
||||
|
||||
chfs_change_size_free(chmp, chmp->chm_nextblock, -padnode->length);
|
||||
chfs_change_size_wasted(chmp, chmp->chm_nextblock, padnode->length);
|
||||
}
|
||||
|
||||
ret = chfs_write_leb(chmp, chmp->chm_nextblock->lnr, chmp->chm_wbuf, chmp->chm_wbuf_ofs, chmp->chm_wbuf_len, &retlen);
|
||||
if(ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
memset(chmp->chm_wbuf,0xff,chmp->chm_wbuf_pagesize);
|
||||
chmp->chm_wbuf_ofs += chmp->chm_wbuf_pagesize;
|
||||
chmp->chm_wbuf_len = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* chfs_fill_wbuf - write to wbuf
|
||||
* @chmp: super block info
|
||||
* @buf: buffer
|
||||
* @len: buffer length
|
||||
* Return the len of the buf what we didn't write to the wbuf.
|
||||
*/
|
||||
static size_t
|
||||
chfs_fill_wbuf(struct chfs_mount *chmp, const u_char *buf, size_t len)
|
||||
{
|
||||
if (len && !chmp->chm_wbuf_len && (len >= chmp->chm_wbuf_pagesize)) {
|
||||
return 0;
|
||||
}
|
||||
if (len > (chmp->chm_wbuf_pagesize - chmp->chm_wbuf_len)) {
|
||||
len = chmp->chm_wbuf_pagesize - chmp->chm_wbuf_len;
|
||||
}
|
||||
memcpy(chmp->chm_wbuf + chmp->chm_wbuf_len, buf, len);
|
||||
|
||||
chmp->chm_wbuf_len += (int) len;
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* chfs_write_wbuf - write to wbuf and then the flash
|
||||
* @chmp: super block info
|
||||
* @invecs: io vectors
|
||||
* @count: num of vectors
|
||||
* @to: offset of target
|
||||
* @retlen: writed bytes
|
||||
* Returns zero in case of success.
|
||||
*/
|
||||
int
|
||||
chfs_write_wbuf(struct chfs_mount* chmp, const struct iovec *invecs, long count,
|
||||
off_t to, size_t *retlen)
|
||||
{
|
||||
int invec, ret = 0;
|
||||
size_t wbuf_retlen, donelen = 0;
|
||||
int outvec_to = to;
|
||||
|
||||
int lnr = chmp->chm_nextblock->lnr;
|
||||
|
||||
KASSERT(mutex_owned(&chmp->chm_lock_mountfields));
|
||||
KASSERT(mutex_owned(&chmp->chm_lock_sizes));
|
||||
KASSERT(!rw_write_held(&chmp->chm_lock_wbuf));
|
||||
|
||||
rw_enter(&chmp->chm_lock_wbuf, RW_WRITER);
|
||||
|
||||
//dbg("1. wbuf ofs: %zu, len: %zu\n", chmp->chm_wbuf_ofs, chmp->chm_wbuf_len);
|
||||
|
||||
if (chmp->chm_wbuf_ofs == 0xffffffff) {
|
||||
chmp->chm_wbuf_ofs = PAGE_DIV(to);
|
||||
chmp->chm_wbuf_len = PAGE_MOD(to);
|
||||
memset(chmp->chm_wbuf, 0xff, chmp->chm_wbuf_pagesize);
|
||||
}
|
||||
|
||||
//dbg("2. wbuf ofs: %zu, len: %zu\n", chmp->chm_wbuf_ofs, chmp->chm_wbuf_len);
|
||||
|
||||
if (EB_ADDRESS(to) != EB_ADDRESS(chmp->chm_wbuf_ofs)) {
|
||||
if (chmp->chm_wbuf_len) {
|
||||
ret = chfs_flush_wbuf(chmp, SETPAD);
|
||||
if (ret)
|
||||
goto outerr;
|
||||
}
|
||||
chmp->chm_wbuf_ofs = PAGE_DIV(to);
|
||||
chmp->chm_wbuf_len = PAGE_MOD(to);
|
||||
}
|
||||
|
||||
//dbg("3. wbuf ofs: %zu, len: %zu\n", chmp->chm_wbuf_ofs, chmp->chm_wbuf_len);
|
||||
|
||||
if (to != PAD(chmp->chm_wbuf_ofs + chmp->chm_wbuf_len)) {
|
||||
dbg("to: %llu != %zu\n", to, PAD(chmp->chm_wbuf_ofs + chmp->chm_wbuf_len));
|
||||
dbg("Non-contiguous write\n");
|
||||
panic("BUG\n");
|
||||
}
|
||||
|
||||
/* adjust alignment offset */
|
||||
if (chmp->chm_wbuf_len != PAGE_MOD(to)) {
|
||||
chmp->chm_wbuf_len = PAGE_MOD(to);
|
||||
/* take care of alignement to next page*/
|
||||
if (!chmp->chm_wbuf_len) {
|
||||
chmp->chm_wbuf_len += chmp->chm_wbuf_pagesize;
|
||||
ret = chfs_flush_wbuf(chmp, NOPAD);
|
||||
if (ret)
|
||||
goto outerr;
|
||||
}
|
||||
}
|
||||
|
||||
for (invec = 0; invec < count; invec++) {
|
||||
int vlen = invecs[invec].iov_len;
|
||||
u_char* v = invecs[invec].iov_base;
|
||||
|
||||
//dbg("invec:%d len:%d\n", invec, vlen);
|
||||
|
||||
wbuf_retlen = chfs_fill_wbuf(chmp, v, vlen);
|
||||
if (chmp->chm_wbuf_len == chmp->chm_wbuf_pagesize) {
|
||||
ret = chfs_flush_wbuf(chmp, NOPAD);
|
||||
if (ret) {
|
||||
goto outerr;
|
||||
}
|
||||
}
|
||||
vlen -= wbuf_retlen;
|
||||
outvec_to += wbuf_retlen;
|
||||
v += wbuf_retlen;
|
||||
donelen += wbuf_retlen;
|
||||
if (vlen >= chmp->chm_wbuf_pagesize) {
|
||||
ret = chfs_write_leb(chmp, lnr, v, outvec_to, PAGE_DIV(vlen), &wbuf_retlen);
|
||||
//dbg("fd->write: %zu\n", wbuf_retlen);
|
||||
vlen -= wbuf_retlen;
|
||||
outvec_to += wbuf_retlen;
|
||||
chmp->chm_wbuf_ofs = outvec_to;
|
||||
v += wbuf_retlen;
|
||||
donelen += wbuf_retlen;
|
||||
}
|
||||
wbuf_retlen = chfs_fill_wbuf(chmp, v, vlen);
|
||||
if (chmp->chm_wbuf_len == chmp->chm_wbuf_pagesize) {
|
||||
ret = chfs_flush_wbuf(chmp, NOPAD);
|
||||
if (ret)
|
||||
goto outerr;
|
||||
}
|
||||
|
||||
// if we write the last vector, we flush with padding
|
||||
/*if (invec == count-1) {
|
||||
ret = chfs_flush_wbuf(chmp, SETPAD);
|
||||
if (ret)
|
||||
goto outerr;
|
||||
}*/
|
||||
outvec_to += wbuf_retlen;
|
||||
donelen += wbuf_retlen;
|
||||
}
|
||||
*retlen = donelen;
|
||||
rw_exit(&chmp->chm_lock_wbuf);
|
||||
return ret;
|
||||
|
||||
outerr:
|
||||
*retlen = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int chfs_flush_pending_wbuf(struct chfs_mount *chmp)
|
||||
{
|
||||
//dbg("flush pending wbuf\n");
|
||||
int err;
|
||||
KASSERT(mutex_owned(&chmp->chm_lock_mountfields));
|
||||
mutex_enter(&chmp->chm_lock_sizes);
|
||||
rw_enter(&chmp->chm_lock_wbuf, RW_WRITER);
|
||||
err = chfs_flush_wbuf(chmp, SETPAD);
|
||||
rw_exit(&chmp->chm_lock_wbuf);
|
||||
mutex_exit(&chmp->chm_lock_sizes);
|
||||
return err;
|
||||
}
|
|
@ -0,0 +1,544 @@
|
|||
/* $NetBSD: chfs_write.c,v 1.1 2011/11/24 15:51:32 ahoka Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2010 Department of Software Engineering,
|
||||
* University of Szeged, Hungary
|
||||
* Copyright (C) 2010 David Tengeri <dtengeri@inf.u-szeged.hu>
|
||||
* Copyright (C) 2010 Tamas Toth <ttoth@inf.u-szeged.hu>
|
||||
* Copyright (C) 2010 Adam Hoka <ahoka@NetBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by the Department of Software Engineering, University of Szeged, Hungary
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* chfs_write.c
|
||||
*
|
||||
* Created on: 2010.02.17.
|
||||
* Author: dtengeri
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/buf.h>
|
||||
|
||||
#include "chfs.h"
|
||||
|
||||
int
|
||||
chfs_write_flash_vnode(struct chfs_mount *chmp,
|
||||
struct chfs_inode *ip, int prio)
|
||||
{
|
||||
KASSERT(mutex_owned(&chmp->chm_lock_mountfields));
|
||||
|
||||
struct chfs_flash_vnode *fvnode;
|
||||
struct chfs_vnode_cache* chvc;
|
||||
struct chfs_node_ref *nref;
|
||||
struct iovec vec;
|
||||
size_t size, retlen;
|
||||
int err = 0, retries = 0;
|
||||
|
||||
if (ip->ino == CHFS_ROOTINO)
|
||||
return 0;
|
||||
|
||||
fvnode = chfs_alloc_flash_vnode();
|
||||
if (!fvnode)
|
||||
return ENOMEM;
|
||||
|
||||
chvc = ip->chvc;
|
||||
|
||||
/* setting up flash_vnode members */
|
||||
size = sizeof(*fvnode);
|
||||
//dbg("size: %zu | PADDED: %zu\n", size, CHFS_PAD(size));
|
||||
fvnode->magic = htole16(CHFS_FS_MAGIC_BITMASK);
|
||||
fvnode->type = htole16(CHFS_NODETYPE_VNODE);
|
||||
fvnode->length = htole32(CHFS_PAD(size));
|
||||
fvnode->hdr_crc = htole32(crc32(0, (uint8_t *)fvnode,
|
||||
CHFS_NODE_HDR_SIZE - 4));
|
||||
fvnode->vno = htole64(ip->ino);
|
||||
fvnode->version = htole64(++ip->chvc->highest_version);
|
||||
fvnode->mode = htole32(ip->mode);
|
||||
fvnode->dn_size = htole32(ip->size);
|
||||
fvnode->atime = htole32(ip->atime);
|
||||
fvnode->ctime = htole32(ip->ctime);
|
||||
fvnode->mtime = htole32(ip->mtime);
|
||||
fvnode->gid = htole32(ip->gid);
|
||||
fvnode->uid = htole32(ip->uid);
|
||||
fvnode->node_crc = htole32(crc32(0, (uint8_t *)fvnode, size - 4));
|
||||
|
||||
/* write out flash_vnode */
|
||||
retry:
|
||||
if (prio == ALLOC_GC) {
|
||||
/* the GC calls this function */
|
||||
err = chfs_reserve_space_gc(chmp, CHFS_PAD(size));
|
||||
if (err)
|
||||
goto out;
|
||||
} else {
|
||||
chfs_gc_trigger(chmp);
|
||||
if (prio == ALLOC_NORMAL)
|
||||
err = chfs_reserve_space_normal(chmp,
|
||||
CHFS_PAD(size), ALLOC_NORMAL);
|
||||
else
|
||||
err = chfs_reserve_space_normal(chmp,
|
||||
CHFS_PAD(size), ALLOC_DELETION);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
nref = chfs_alloc_node_ref(chmp->chm_nextblock);
|
||||
if (!nref) {
|
||||
err = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mutex_enter(&chmp->chm_lock_sizes);
|
||||
|
||||
nref->nref_offset = chmp->chm_ebh->eb_size - chmp->chm_nextblock->free_size;
|
||||
chfs_change_size_free(chmp, chmp->chm_nextblock, -CHFS_PAD(size));
|
||||
vec.iov_base = fvnode;
|
||||
vec.iov_len = CHFS_PAD(size);
|
||||
err = chfs_write_wbuf(chmp, &vec, 1, nref->nref_offset, &retlen);
|
||||
if (err || retlen != CHFS_PAD(size)) {
|
||||
chfs_err("error while writing out flash vnode to the media\n");
|
||||
chfs_err("err: %d | size: %zu | retlen : %zu\n",
|
||||
err, CHFS_PAD(size), retlen);
|
||||
chfs_change_size_dirty(chmp,
|
||||
chmp->chm_nextblock, CHFS_PAD(size));
|
||||
if (retries) {
|
||||
err = EIO;
|
||||
mutex_exit(&chmp->chm_lock_sizes);
|
||||
goto out;
|
||||
}
|
||||
|
||||
retries++;
|
||||
mutex_exit(&chmp->chm_lock_sizes);
|
||||
goto retry;
|
||||
}
|
||||
//Everything went well
|
||||
chfs_change_size_used(chmp,
|
||||
&chmp->chm_blocks[nref->nref_lnr], CHFS_PAD(size));
|
||||
mutex_exit(&chmp->chm_lock_sizes);
|
||||
|
||||
chfs_add_vnode_ref_to_vc(chmp, chvc, nref);
|
||||
KASSERT(chmp->chm_blocks[nref->nref_lnr].used_size <= chmp->chm_ebh->eb_size);
|
||||
out:
|
||||
chfs_free_flash_vnode(fvnode);
|
||||
return err;
|
||||
}
|
||||
|
||||
int
|
||||
chfs_write_flash_dirent(struct chfs_mount *chmp, struct chfs_inode *pdir,
|
||||
struct chfs_inode *ip, struct chfs_dirent *fd,
|
||||
ino_t ino, int prio)
|
||||
{
|
||||
KASSERT(mutex_owned(&chmp->chm_lock_mountfields));
|
||||
|
||||
struct chfs_flash_dirent_node *fdirent;
|
||||
struct chfs_node_ref *nref;
|
||||
struct iovec vec[2];
|
||||
size_t size, retlen;
|
||||
int err = 0, retries = 0;
|
||||
uint8_t *name;
|
||||
size_t namelen;
|
||||
|
||||
KASSERT(fd->vno != CHFS_ROOTINO);
|
||||
|
||||
fdirent = chfs_alloc_flash_dirent();
|
||||
if (!fdirent)
|
||||
return ENOMEM;
|
||||
|
||||
size = sizeof(*fdirent) + fd->nsize;
|
||||
namelen = CHFS_PAD(size) - sizeof(*fdirent);
|
||||
|
||||
name = kmem_zalloc(namelen, KM_SLEEP);
|
||||
memcpy(name, fd->name, fd->nsize);
|
||||
//dbg("namelen: %zu | nsize: %hhu\n", namelen, fd->nsize);
|
||||
|
||||
|
||||
//dbg("size: %zu | PADDED: %zu\n", size, CHFS_PAD(size));
|
||||
fdirent->magic = htole16(CHFS_FS_MAGIC_BITMASK);
|
||||
fdirent->type = htole16(CHFS_NODETYPE_DIRENT);
|
||||
fdirent->length = htole32(CHFS_PAD(size));
|
||||
fdirent->hdr_crc = htole32(crc32(0, (uint8_t *)fdirent,
|
||||
CHFS_NODE_HDR_SIZE - 4));
|
||||
fdirent->vno = htole64(ino);
|
||||
fdirent->pvno = htole64(pdir->ino);
|
||||
fdirent->version = htole64(++pdir->chvc->highest_version);
|
||||
fdirent->mctime = ip?ip->ctime:0;
|
||||
fdirent->nsize = fd->nsize;
|
||||
fdirent->dtype = fd->type;
|
||||
fdirent->name_crc = crc32(0, (uint8_t *)&(fd->name), fd->nsize);
|
||||
fdirent->node_crc = crc32(0, (uint8_t *)fdirent, sizeof(*fdirent) - 4);
|
||||
|
||||
vec[0].iov_base = fdirent;
|
||||
vec[0].iov_len = sizeof(*fdirent);
|
||||
vec[1].iov_base = name;
|
||||
vec[1].iov_len = namelen;
|
||||
|
||||
retry:
|
||||
if (prio == ALLOC_GC) {
|
||||
/* the GC calls this function */
|
||||
err = chfs_reserve_space_gc(chmp, CHFS_PAD(size));
|
||||
if (err)
|
||||
goto out;
|
||||
} else {
|
||||
chfs_gc_trigger(chmp);
|
||||
if (prio == ALLOC_NORMAL)
|
||||
err = chfs_reserve_space_normal(chmp,
|
||||
CHFS_PAD(size), ALLOC_NORMAL);
|
||||
else
|
||||
err = chfs_reserve_space_normal(chmp,
|
||||
CHFS_PAD(size), ALLOC_DELETION);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
nref = chfs_alloc_node_ref(chmp->chm_nextblock);
|
||||
if (!nref) {
|
||||
err = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mutex_enter(&chmp->chm_lock_sizes);
|
||||
|
||||
nref->nref_offset = chmp->chm_ebh->eb_size - chmp->chm_nextblock->free_size;
|
||||
chfs_change_size_free(chmp, chmp->chm_nextblock, -CHFS_PAD(size));
|
||||
|
||||
err = chfs_write_wbuf(chmp, vec, 2, nref->nref_offset, &retlen);
|
||||
if (err || retlen != CHFS_PAD(size)) {
|
||||
chfs_err("error while writing out flash dirent node to the media\n");
|
||||
chfs_err("err: %d | size: %zu | retlen : %zu\n",
|
||||
err, CHFS_PAD(size), retlen);
|
||||
chfs_change_size_dirty(chmp,
|
||||
chmp->chm_nextblock, CHFS_PAD(size));
|
||||
if (retries) {
|
||||
err = EIO;
|
||||
mutex_exit(&chmp->chm_lock_sizes);
|
||||
goto out;
|
||||
}
|
||||
|
||||
retries++;
|
||||
mutex_exit(&chmp->chm_lock_sizes);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
|
||||
// Everything went well
|
||||
chfs_change_size_used(chmp,
|
||||
&chmp->chm_blocks[nref->nref_lnr], CHFS_PAD(size));
|
||||
mutex_exit(&chmp->chm_lock_sizes);
|
||||
KASSERT(chmp->chm_blocks[nref->nref_lnr].used_size <= chmp->chm_ebh->eb_size);
|
||||
fd->nref = nref;
|
||||
if (prio != ALLOC_DELETION) {
|
||||
chfs_add_node_to_list(chmp,
|
||||
pdir->chvc, nref, &pdir->chvc->dirents);
|
||||
}
|
||||
out:
|
||||
chfs_free_flash_dirent(fdirent);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* chfs_write_flash_dnode - write out a data node to flash
|
||||
* @chmp: chfs mount structure
|
||||
* @vp: vnode where the data belongs to
|
||||
* @bp: buffer contains data
|
||||
*/
|
||||
int
|
||||
chfs_write_flash_dnode(struct chfs_mount *chmp, struct vnode *vp,
|
||||
struct buf *bp, struct chfs_full_dnode *fd)
|
||||
{
|
||||
KASSERT(mutex_owned(&chmp->chm_lock_mountfields));
|
||||
|
||||
int err = 0, retries = 0;
|
||||
size_t size, retlen;
|
||||
off_t ofs;
|
||||
struct chfs_flash_data_node *dnode;
|
||||
struct chfs_node_ref *nref;
|
||||
struct chfs_inode *ip = VTOI(vp);
|
||||
struct iovec vec[2];
|
||||
uint32_t len;
|
||||
void *tmpbuf = NULL;
|
||||
|
||||
KASSERT(ip->ino != CHFS_ROOTINO);
|
||||
|
||||
dnode = chfs_alloc_flash_dnode();
|
||||
if (!dnode)
|
||||
return ENOMEM;
|
||||
|
||||
/* initialize flash data node */
|
||||
ofs = bp->b_blkno * PAGE_SIZE;
|
||||
//dbg("vp->v_size: %ju, bp->b_blkno: %ju, bp-b_data: %p,"
|
||||
// " bp->b_resid: %ju\n",
|
||||
// (uintmax_t )vp->v_size, (uintmax_t )bp->b_blkno,
|
||||
// bp->b_data, (uintmax_t )bp->b_resid);
|
||||
//dbg("[XXX]vp->v_size - ofs: %llu\n", (vp->v_size - ofs));
|
||||
len = MIN((vp->v_size - ofs), bp->b_resid);
|
||||
size = sizeof(*dnode) + len;
|
||||
|
||||
dnode->magic = htole16(CHFS_FS_MAGIC_BITMASK);
|
||||
dnode->type = htole16(CHFS_NODETYPE_DATA);
|
||||
dnode->length = htole32(CHFS_PAD(size));
|
||||
dnode->hdr_crc = htole32(crc32(0, (uint8_t *)dnode,
|
||||
CHFS_NODE_HDR_SIZE - 4));
|
||||
dnode->vno = htole64(ip->ino);
|
||||
dnode->version = htole64(++ip->chvc->highest_version);
|
||||
dnode->offset = htole64(ofs);
|
||||
dnode->data_length = htole32(len);
|
||||
dnode->data_crc = htole32(crc32(0, (uint8_t *)bp->b_data, len));
|
||||
dnode->node_crc = htole32(crc32(0, (uint8_t *)dnode,
|
||||
sizeof(*dnode) - 4));
|
||||
|
||||
dbg("dnode @%llu %ub v%llu\n", dnode->offset, dnode->data_length, dnode->version);
|
||||
|
||||
if (CHFS_PAD(size) - sizeof(*dnode)) {
|
||||
tmpbuf = kmem_zalloc(CHFS_PAD(size)
|
||||
- sizeof(*dnode), KM_SLEEP);
|
||||
memcpy(tmpbuf, bp->b_data, len);
|
||||
}
|
||||
|
||||
/* creating iovecs for wbuf */
|
||||
vec[0].iov_base = dnode;
|
||||
vec[0].iov_len = sizeof(*dnode);
|
||||
vec[1].iov_base = tmpbuf;
|
||||
vec[1].iov_len = CHFS_PAD(size) - sizeof(*dnode);
|
||||
|
||||
fd->frags = 0;
|
||||
fd->ofs = ofs;
|
||||
fd->size = len;
|
||||
|
||||
retry:
|
||||
|
||||
/* Reserve space for data node. This will set up the next eraseblock
|
||||
* where to we will write.
|
||||
*/
|
||||
|
||||
chfs_gc_trigger(chmp);
|
||||
err = chfs_reserve_space_normal(chmp,
|
||||
CHFS_PAD(size), ALLOC_NORMAL);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
nref = chfs_alloc_node_ref(chmp->chm_nextblock);
|
||||
if (!nref) {
|
||||
err = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
nref->nref_offset =
|
||||
chmp->chm_ebh->eb_size - chmp->chm_nextblock->free_size;
|
||||
|
||||
KASSERT(nref->nref_offset < chmp->chm_ebh->eb_size);
|
||||
|
||||
mutex_enter(&chmp->chm_lock_sizes);
|
||||
|
||||
chfs_change_size_free(chmp,
|
||||
chmp->chm_nextblock, -CHFS_PAD(size));
|
||||
|
||||
//dbg("vno: %llu nref lnr: %u offset: %u\n",
|
||||
// dnode->vno, nref->nref_lnr, nref->nref_offset);
|
||||
|
||||
err = chfs_write_wbuf(chmp, vec, 2, nref->nref_offset, &retlen);
|
||||
if (err || retlen != CHFS_PAD(size)) {
|
||||
chfs_err("error while writing out flash data node to the media\n");
|
||||
chfs_err("err: %d | size: %zu | retlen : %zu\n",
|
||||
err, size, retlen);
|
||||
chfs_change_size_dirty(chmp,
|
||||
chmp->chm_nextblock, CHFS_PAD(size));
|
||||
if (retries) {
|
||||
err = EIO;
|
||||
mutex_exit(&chmp->chm_lock_sizes);
|
||||
goto out;
|
||||
}
|
||||
|
||||
retries++;
|
||||
mutex_exit(&chmp->chm_lock_sizes);
|
||||
goto retry;
|
||||
}
|
||||
/* Everything went well */
|
||||
ip->write_size += fd->size;
|
||||
chfs_change_size_used(chmp,
|
||||
&chmp->chm_blocks[nref->nref_lnr], CHFS_PAD(size));
|
||||
mutex_exit(&chmp->chm_lock_sizes);
|
||||
|
||||
KASSERT(chmp->chm_blocks[nref->nref_lnr].used_size <= chmp->chm_ebh->eb_size);
|
||||
fd->nref = nref;
|
||||
chfs_add_node_to_list(chmp, ip->chvc, nref, &ip->chvc->dnode);
|
||||
out:
|
||||
chfs_free_flash_dnode(dnode);
|
||||
if (CHFS_PAD(size) - sizeof(*dnode)) {
|
||||
kmem_free(tmpbuf, CHFS_PAD(size) - sizeof(*dnode));
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* chfs_do_link - makes a copy from a node
|
||||
* @old: old node
|
||||
* @oldfd: dirent of old node
|
||||
* @parent: parent of new node
|
||||
* @name: name of new node
|
||||
* @namelen: length of name
|
||||
* This function writes the dirent of the new node to the media.
|
||||
*/
|
||||
int
|
||||
chfs_do_link(struct chfs_inode *ip, struct chfs_inode *parent, const char *name, int namelen, enum vtype type)
|
||||
{
|
||||
int error = 0;
|
||||
struct vnode *vp = ITOV(ip);
|
||||
struct ufsmount *ump = VFSTOUFS(vp->v_mount);
|
||||
struct chfs_mount *chmp = ump->um_chfs;
|
||||
struct chfs_dirent *newfd = NULL;
|
||||
// struct chfs_dirent *fd = NULL;
|
||||
|
||||
//dbg("link vno: %llu\n", ip->ino);
|
||||
|
||||
newfd = chfs_alloc_dirent(namelen + 1);
|
||||
|
||||
newfd->vno = ip->ino;
|
||||
newfd->type = type;
|
||||
newfd->nsize = namelen;
|
||||
memcpy(newfd->name, name, namelen);
|
||||
newfd->name[newfd->nsize] = 0;
|
||||
// newfd->next = NULL;
|
||||
|
||||
ip->chvc->nlink++;
|
||||
parent->chvc->nlink++;
|
||||
ip->iflag |= IN_CHANGE;
|
||||
chfs_update(vp, NULL, NULL, UPDATE_WAIT);
|
||||
|
||||
mutex_enter(&chmp->chm_lock_mountfields);
|
||||
|
||||
error = chfs_write_flash_vnode(chmp, ip, ALLOC_NORMAL);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = chfs_write_flash_dirent(chmp,
|
||||
parent, ip, newfd, ip->ino, ALLOC_NORMAL);
|
||||
/* TODO: what should we do if error isn't zero? */
|
||||
|
||||
mutex_exit(&chmp->chm_lock_mountfields);
|
||||
|
||||
/* add fd to the fd list */
|
||||
TAILQ_INSERT_TAIL(&parent->dents, newfd, fds);
|
||||
#if 0
|
||||
fd = parent->dents;
|
||||
if (!fd) {
|
||||
parent->dents = newfd;
|
||||
} else {
|
||||
while (fd->next)
|
||||
fd = fd->next;
|
||||
fd->next = newfd;
|
||||
}
|
||||
#endif
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* chfs_do_unlink - delete a node
|
||||
* @ip: node what we'd like to delete
|
||||
* @parent: parent of the node
|
||||
* @name: name of the node
|
||||
* @namelen: length of name
|
||||
* This function set the nlink and vno of the node zero and write its dirent to the media.
|
||||
*/
|
||||
int
|
||||
chfs_do_unlink(struct chfs_inode *ip,
|
||||
struct chfs_inode *parent, const char *name, int namelen)
|
||||
{
|
||||
struct chfs_dirent *fd, *tmpfd;
|
||||
int error = 0;
|
||||
struct vnode *vp = ITOV(ip);
|
||||
struct ufsmount *ump = VFSTOUFS(vp->v_mount);
|
||||
struct chfs_mount *chmp = ump->um_chfs;
|
||||
struct chfs_node_ref *nref;
|
||||
|
||||
//dbg("unlink vno: %llu\n", ip->ino);
|
||||
|
||||
vflushbuf(vp, 0);
|
||||
|
||||
mutex_enter(&chmp->chm_lock_mountfields);
|
||||
|
||||
/* remove the full direntry from the parent dents list */
|
||||
TAILQ_FOREACH_SAFE(fd, &parent->dents, fds, tmpfd) {
|
||||
if (fd->vno == ip->ino &&
|
||||
fd->nsize == namelen &&
|
||||
!memcmp(fd->name, name, fd->nsize)) {
|
||||
if (fd->type == VDIR && ip->chvc->nlink == 2)
|
||||
ip->chvc->nlink = 0;
|
||||
else
|
||||
ip->chvc->nlink--;
|
||||
|
||||
fd->type = VNON;
|
||||
|
||||
TAILQ_REMOVE(&parent->dents, fd, fds);
|
||||
|
||||
/* remove nref from dirents list */
|
||||
nref = parent->chvc->dirents;
|
||||
if (nref == fd->nref) {
|
||||
nref->nref_next = fd->nref->nref_next;
|
||||
} else {
|
||||
while (nref->nref_next && nref->nref_next != fd->nref)
|
||||
nref = nref->nref_next;
|
||||
if (nref->nref_next)
|
||||
nref->nref_next = fd->nref->nref_next;
|
||||
}
|
||||
|
||||
//dbg("FD->NREF vno: %llu, lnr: %u, ofs: %u\n",
|
||||
// fd->vno, fd->nref->nref_lnr, fd->nref->nref_offset);
|
||||
chfs_mark_node_obsolete(chmp, fd->nref);
|
||||
|
||||
error = chfs_write_flash_dirent(chmp,
|
||||
parent, ip, fd, 0, ALLOC_DELETION);
|
||||
|
||||
//dbg("FD->NREF vno: %llu, lnr: %u, ofs: %u\n",
|
||||
// fd->vno, fd->nref->nref_lnr, fd->nref->nref_offset);
|
||||
chfs_mark_node_obsolete(chmp, fd->nref);
|
||||
|
||||
nref = ip->chvc->dnode;
|
||||
while (nref != (struct chfs_node_ref *)ip->chvc) {
|
||||
//dbg("DATA NREF\n");
|
||||
chfs_mark_node_obsolete(chmp, nref);
|
||||
nref = nref->nref_next;
|
||||
}
|
||||
ip->chvc->dnode = (struct chfs_node_ref *)ip->chvc;
|
||||
|
||||
nref = ip->chvc->v;
|
||||
while (nref != (struct chfs_node_ref *)ip->chvc) {
|
||||
//dbg("V NREF\n");
|
||||
chfs_mark_node_obsolete(chmp, nref);
|
||||
nref = nref->nref_next;
|
||||
}
|
||||
ip->chvc->v = ip->chvc->v->nref_next;
|
||||
|
||||
parent->chvc->nlink--;
|
||||
//TODO: if error
|
||||
}
|
||||
}
|
||||
mutex_exit(&chmp->chm_lock_mountfields);
|
||||
|
||||
return error;
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/* $NetBSD: debug.c,v 1.1 2011/11/24 15:51:32 ahoka Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2010 Department of Software Engineering,
|
||||
* University of Szeged, Hungary
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by the Department of Software Engineering, University of Szeged, Hungary
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* XipFFS -- Xip Flash File System
|
||||
*
|
||||
* Copyright (C) 2009 Ferenc Havasi <havasi@inf.u-szeged.hu>,
|
||||
* Zoltan Sogor <weth@inf.u-szeged.hu>,
|
||||
* ...
|
||||
* University of Szeged, Hungary
|
||||
*
|
||||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "chfs.h"
|
||||
//#include </root/xipffs/netbsd.chfs/chfs.h>
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
/* $NetBSD: debug.h,v 1.1 2011/11/24 15:51:32 ahoka Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2010 Department of Software Engineering,
|
||||
* University of Szeged, Hungary
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by the Department of Software Engineering, University of Szeged, Hungary
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* XipFFS -- Xip Flash File System
|
||||
*
|
||||
* Copyright (C) 2009 Ferenc Havasi <havasi@inf.u-szeged.hu>,
|
||||
* Zoltan Sogor <weth@inf.u-szeged.hu>,
|
||||
* ...
|
||||
* University of Szeged, Hungary
|
||||
*
|
||||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __CHFS_DEBUG_H__
|
||||
#define __CHFS_DEBUG_H__
|
||||
|
||||
#define CHFS_ERROR_PREFIX "[CHFS ERROR]"
|
||||
#define CHFS_WARNING_PREFIX "[CHFS WARNING]"
|
||||
#define CHFS_NOTICE_PREFIX "[CHFS NOTICE]"
|
||||
#define CHFS_DBG_PREFIX "[CHFS DBG]"
|
||||
#define CHFS_DBG2_PREFIX "[CHFS DBG2]"
|
||||
#define CHFS_DBG_EBH_PREFIX "[CHFS DBG EBH]"
|
||||
#define CHFS_DBG_GC_PREFIX "[CHFS_GC DBG]"
|
||||
|
||||
#define unlikely(x) __builtin_expect ((x), 0)
|
||||
|
||||
|
||||
|
||||
#define debug_msg(pref, fmt, ...) \
|
||||
do { \
|
||||
printf(pref \
|
||||
" %s: " fmt, __FUNCTION__ , ##__VA_ARGS__); \
|
||||
} while(0)
|
||||
|
||||
#define chfs_assert(expr) do { \
|
||||
if (unlikely(!(expr))) { \
|
||||
printf("CHFS assert failed in %s at %u\n", \
|
||||
__func__, __LINE__); \
|
||||
/*dump_stack();*/ \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#ifdef DBG_MSG
|
||||
#define chfs_err(fmt, ...) debug_msg(CHFS_ERROR_PREFIX, fmt, ##__VA_ARGS__)
|
||||
#define chfs_warn(fmt, ...) debug_msg(CHFS_WARNING_PREFIX, fmt, ##__VA_ARGS__)
|
||||
#define chfs_noti(fmt, ...) debug_msg(CHFS_NOTICE_PREFIX, fmt, ##__VA_ARGS__)
|
||||
#define dbg(fmt, ...) debug_msg(CHFS_DBG_PREFIX, fmt, ##__VA_ARGS__)
|
||||
#define dbg2(fmt, ...) debug_msg(CHFS_DBG2_PREFIX(fmt, ##__VA_ARGS__)
|
||||
#define dbg_ebh(fmt, ...) debug_msg(CHFS_DBG_EBH_PREFIX, fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define chfs_err(fmt, ...) debug_msg(CHFS_ERROR_PREFIX, fmt, ##__VA_ARGS__)
|
||||
#define chfs_warn(fmt, ...) debug_msg(CHFS_WARNING_PREFIX, fmt, ##__VA_ARGS__)
|
||||
#define chfs_noti(fmt, ...) debug_msg(CHFS_NOTICE_PREFIX, fmt, ##__VA_ARGS__)
|
||||
#define dbg(fmt, ...)
|
||||
#define dbg2(fmt, ...)
|
||||
#define dbg_ebh(fmt, ...)
|
||||
#endif
|
||||
|
||||
#ifdef DBG_MSG_GC
|
||||
#define dbg_gc(fmt, ...) debug_msg(CHFS_DBG_GC_PREFIX, fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define dbg_gc(fmt, ...)
|
||||
#endif
|
||||
|
||||
#endif /* __CHFS_DEBUG_H__ */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,318 @@
|
|||
/* $NetBSD: ebh.h,v 1.1 2011/11/24 15:51:32 ahoka Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2010 Department of Software Engineering,
|
||||
* University of Szeged, Hungary
|
||||
* Copyright (c) 2010 David Tengeri <dtengeri@inf.u-szeged.hu>
|
||||
* Copyright (c) 2010 Adam Hoka <ahoka@NetBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by the Department of Software Engineering, University of Szeged, Hungary
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* ebh.h
|
||||
*
|
||||
* Created on: 2009.11.03.
|
||||
* Author: dtengeri
|
||||
*/
|
||||
|
||||
#ifndef EBH_H_
|
||||
#define EBH_H_
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/tree.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/kmem.h>
|
||||
#include <sys/endian.h>
|
||||
#include <sys/rwlock.h>
|
||||
#include <sys/condvar.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/kthread.h>
|
||||
|
||||
#include <dev/flash/flash.h>
|
||||
#include <ufs/chfs/ebh_media.h>
|
||||
#include <ufs/chfs/debug.h>
|
||||
#include <ufs/chfs/ebh_misc.h>
|
||||
|
||||
/* Maximum retries when getting new PEB before exit with failure */
|
||||
#define CHFS_MAX_GET_PEB_RETRIES 2
|
||||
|
||||
/**
|
||||
* LEB status
|
||||
*
|
||||
*/
|
||||
enum {
|
||||
EBH_LEB_UNMAPPED = -1,
|
||||
EBH_LEB_MAPPED,
|
||||
EBH_LEB_DIRTY,
|
||||
EBH_LEB_INVALID,
|
||||
EBH_LEB_ERASE,
|
||||
EBH_LEB_ERASED,
|
||||
EBH_LEB_FREE,
|
||||
};
|
||||
|
||||
/**
|
||||
* EB header status
|
||||
*/
|
||||
enum {
|
||||
EBHDR_LEB_OK = 0,
|
||||
EBHDR_LEB_DIRTY,
|
||||
EBHDR_LEB_INVALIDATED,
|
||||
EBHDR_LEB_BADMAGIC,
|
||||
EBHDR_LEB_BADCRC,
|
||||
EBHDR_LEB_FREE,
|
||||
EBHDR_LEB_NO_HDR,
|
||||
};
|
||||
|
||||
struct chfs_ebh;
|
||||
|
||||
/**
|
||||
* struct chfs_ltree_entry - an netry in the lock tree
|
||||
* @rb: RB-node of the tree
|
||||
* @lnr: logical eraseblock number
|
||||
* @users: counts the tasks that are using or want to use the eraseblock
|
||||
* @mutex: read/write mutex to lock the eraseblock
|
||||
*/
|
||||
struct chfs_ltree_entry {
|
||||
RB_ENTRY(chfs_ltree_entry) rb;
|
||||
int lnr;
|
||||
int users;
|
||||
krwlock_t mutex;
|
||||
};
|
||||
|
||||
/* Generate structure for Lock tree's red-black tree */
|
||||
RB_HEAD(ltree_rbtree, chfs_ltree_entry);
|
||||
|
||||
|
||||
/**
|
||||
* struct chfs_scan_leb - scanning infomration about a physical eraseblock
|
||||
* @erase_cnt: erase counter
|
||||
* @pebnr: physical eraseblock number
|
||||
* @info: the status of the PEB's eraseblock header when NOR serial when NAND
|
||||
* @u.list: link in one of the eraseblock list
|
||||
* @u.rb: link in the used RB-tree of chfs_scan_info
|
||||
*/
|
||||
struct chfs_scan_leb {
|
||||
int erase_cnt;
|
||||
int pebnr;
|
||||
int lnr;
|
||||
uint64_t info;
|
||||
union {
|
||||
TAILQ_ENTRY(chfs_scan_leb) queue;
|
||||
RB_ENTRY(chfs_scan_leb) rb;
|
||||
} u;
|
||||
};
|
||||
|
||||
TAILQ_HEAD(scan_leb_queue, chfs_scan_leb);
|
||||
RB_HEAD(scan_leb_used_rbtree, chfs_scan_leb);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* struct chfs_scan_info - chfs scanning information
|
||||
* @corrupted: queue of corrupted physical eraseblocks
|
||||
* @free: queue of free physical eraseblocks
|
||||
* @erase: queue of the physical eraseblocks signed to erase
|
||||
* @erased: queue of physical eraseblocks that contain no header
|
||||
* @used: RB-tree of used PEBs describing by chfs_scan_leb
|
||||
* @sum_of_ec: summary of erase counters
|
||||
* @num_of_eb: number of free and used eraseblocks
|
||||
* @bad_peb_cnt: counter of bad eraseblocks
|
||||
*
|
||||
* This structure contains information about the scanning for further
|
||||
* processing.
|
||||
*/
|
||||
struct chfs_scan_info {
|
||||
struct scan_leb_queue corrupted;
|
||||
struct scan_leb_queue free;
|
||||
struct scan_leb_queue erase;
|
||||
struct scan_leb_queue erased;
|
||||
struct scan_leb_used_rbtree used;
|
||||
uint64_t sum_of_ec;
|
||||
int num_of_eb;
|
||||
int bad_peb_cnt;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct chfs_peb - PEB information for erasing and wear leveling
|
||||
* @erase_cnt: erase counter of the physical eraseblock
|
||||
* @pebnr: physical eraseblock number
|
||||
* @u.queue: link to the queue of the PEBs waiting for erase
|
||||
* @u.rb: link to the RB-tree to the free PEBs
|
||||
*/
|
||||
struct chfs_peb {
|
||||
int erase_cnt;
|
||||
int pebnr;
|
||||
union {
|
||||
TAILQ_ENTRY(chfs_peb) queue;
|
||||
RB_ENTRY(chfs_peb) rb;
|
||||
} u;
|
||||
};
|
||||
|
||||
/* Generate queue and rb-tree structures. */
|
||||
TAILQ_HEAD(peb_queue, chfs_peb);
|
||||
RB_HEAD(peb_free_rbtree, chfs_peb);
|
||||
RB_HEAD(peb_in_use_rbtree, chfs_peb);
|
||||
|
||||
/**
|
||||
* struct chfs_eb_hdr - in-memory representation of eraseblock headers
|
||||
* @ec_hdr: erase counter header ob eraseblock
|
||||
* @u.nor_hdr: eraseblock header on NOR flash
|
||||
* @u.nand_hdr: eraseblock header on NAND flash
|
||||
*/
|
||||
struct chfs_eb_hdr {
|
||||
struct chfs_eb_ec_hdr ec_hdr;
|
||||
union {
|
||||
struct chfs_nor_eb_hdr nor_hdr;
|
||||
struct chfs_nand_eb_hdr nand_hdr;
|
||||
} u;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct chfs_ebh_ops - collection of operations which
|
||||
* depends on flash type
|
||||
* *************************************************************************** *
|
||||
* Direct flash operations:
|
||||
*
|
||||
* @read_eb_hdr: read eraseblock header from media
|
||||
* @write_eb_hdr: write eraseblock header to media
|
||||
* @check_eb_hdr: validates eraseblock header
|
||||
* @mark_eb_hdr_dirty_flash: marks eraseblock dirty on flash
|
||||
* @invalidate_eb_hdr: invalidates eraseblock header
|
||||
* @mark_eb_hdr_free: marks eraseblock header free (after erase)
|
||||
* *************************************************************************** *
|
||||
* Scanning operations:
|
||||
*
|
||||
* @process_eb: process an eraseblock information at scan
|
||||
* *************************************************************************** *
|
||||
* Misc operations:
|
||||
*
|
||||
* @create_eb_hdr: creates an eraseblock header based on flash type
|
||||
* @calc_data_offs: calculates where the data starts
|
||||
*/
|
||||
struct chfs_ebh_ops {
|
||||
int (*read_eb_hdr)(struct chfs_ebh *ebh, int pebnr,
|
||||
struct chfs_eb_hdr *ebhdr);
|
||||
int (*write_eb_hdr)(struct chfs_ebh *ebh, int pebnr,
|
||||
struct chfs_eb_hdr *ebhdr);
|
||||
int (*check_eb_hdr)(struct chfs_ebh *ebh, void *buf);
|
||||
int (*mark_eb_hdr_dirty_flash)(struct chfs_ebh *ebh, int pebnr, int lid);
|
||||
int (*invalidate_eb_hdr)(struct chfs_ebh *ebh, int pebnr);
|
||||
int (*mark_eb_hdr_free)(struct chfs_ebh *ebh, int pebnr, int ec);
|
||||
|
||||
int (*process_eb)(struct chfs_ebh *ebh, struct chfs_scan_info *si,
|
||||
int pebnr, struct chfs_eb_hdr *ebhdr);
|
||||
|
||||
int (*create_eb_hdr)(struct chfs_eb_hdr *ebhdr, int lnr);
|
||||
int (*calc_data_offs)(struct chfs_ebh *ebh, int pebnr, int offset);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct erase_thread - background thread for erasing
|
||||
* @thread: pointer to thread structure
|
||||
* @wakeup: conditional variable for sleeping if there isn't any job to do
|
||||
* @running: flag to signal a thread shutdown
|
||||
*/
|
||||
struct erase_thread {
|
||||
lwp_t *eth_thread;
|
||||
kcondvar_t eth_wakeup;
|
||||
bool eth_running;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* struct chfs_ebh - eraseblock handler descriptor
|
||||
* @mtd: mtd device descriptor
|
||||
* @eb_size: eraseblock size
|
||||
* @peb_nr: number of PEBs
|
||||
* @lmap: LEB to PEB mapping
|
||||
* @layout_map: the LEBs layout (NOT USED YET)
|
||||
* @ltree: the lock tree
|
||||
* @ltree_lock: protects the tree
|
||||
* @alc_mutex: serializes "atomic LEB change" operation
|
||||
* @free: RB-tree of the free easeblocks
|
||||
* @in_use: RB-tree of PEBs are in use
|
||||
* @to_erase: list of the PEBs waiting for erase
|
||||
* @fully_erased: list of PEBs that have been erased but don't have header
|
||||
* @erase_lock: list and tree lock for fully_erased and to_erase lists and
|
||||
* for the free RB-tree
|
||||
* @bg_erase: background thread for eraseing PEBs.
|
||||
* @ops: collection of operations which depends on flash type
|
||||
* @max_serial: max serial number of eraseblocks, only used on NAND
|
||||
*/
|
||||
struct chfs_ebh {
|
||||
struct peb_free_rbtree free;
|
||||
struct peb_in_use_rbtree in_use;
|
||||
struct peb_queue to_erase;
|
||||
struct peb_queue fully_erased;
|
||||
struct erase_thread bg_erase;
|
||||
device_t flash_dev;
|
||||
const struct flash_interface *flash_if;
|
||||
struct chfs_ebh_ops *ops;
|
||||
uint64_t *max_serial;
|
||||
int *lmap;
|
||||
//int *layout_map;
|
||||
struct ltree_rbtree ltree;
|
||||
//struct mutex alc_mutex;
|
||||
kmutex_t ltree_lock;
|
||||
kmutex_t alc_mutex;
|
||||
kmutex_t erase_lock;
|
||||
size_t eb_size;
|
||||
size_t peb_nr;
|
||||
flash_size_t flash_size;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct chfs_erase_info_priv - private information for erase
|
||||
* @ebh: eraseblock handler
|
||||
* @peb: physical eraseblock information
|
||||
*/
|
||||
struct chfs_erase_info_priv {
|
||||
struct chfs_ebh *ebh;
|
||||
struct chfs_peb *peb;
|
||||
};
|
||||
|
||||
/* ebh.c */
|
||||
|
||||
int ebh_open(struct chfs_ebh *ebh, dev_t dev);
|
||||
int ebh_close(struct chfs_ebh *ebh);
|
||||
int ebh_read_leb(struct chfs_ebh *ebh, int lnr, char *buf,
|
||||
uint32_t offset, size_t len, size_t *retlen);
|
||||
int ebh_write_leb(struct chfs_ebh *ebh, int lnr, char *buf,
|
||||
uint32_t offset, size_t len, size_t *retlen);
|
||||
int ebh_erase_leb(struct chfs_ebh *ebh, int lnr);
|
||||
int ebh_map_leb(struct chfs_ebh *ebh, int lnr);
|
||||
int ebh_unmap_leb(struct chfs_ebh *ebh, int lnr);
|
||||
int ebh_is_mapped(struct chfs_ebh *ebh, int lnr);
|
||||
int ebh_change_leb(struct chfs_ebh *ebh, int lnr, char *buf,
|
||||
size_t len, size_t *retlen);
|
||||
|
||||
|
||||
#endif /* EBH_H_ */
|
|
@ -0,0 +1,116 @@
|
|||
/* $NetBSD: ebh_media.h,v 1.1 2011/11/24 15:51:32 ahoka Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2010 Department of Software Engineering,
|
||||
* University of Szeged, Hungary
|
||||
* Copyright (C) 2009 Ferenc Havasi <havasi@inf.u-szeged.hu>
|
||||
* Copyright (C) 2009 Zoltan Sogor <weth@inf.u-szeged.hu>
|
||||
* Copyright (C) 2009 David Tengeri <dtengeri@inf.u-szeged.hu>
|
||||
* Copyright (C) 2010 Adam Hoka <ahoka@NetBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by the Department of Software Engineering, University of Szeged, Hungary
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef EBH_MEDIA_H_
|
||||
#define EBH_MEDIA_H_
|
||||
|
||||
#ifndef _LE_TYPES
|
||||
#define _LE_TYPES
|
||||
typedef uint16_t le16;
|
||||
typedef uint32_t le32;
|
||||
typedef uint64_t le64;
|
||||
#endif
|
||||
|
||||
/*****************************************************************************/
|
||||
/* EBH specific structures */
|
||||
/*****************************************************************************/
|
||||
#define CHFS_MAGIC_BITMASK 0x53454452
|
||||
|
||||
#define CHFS_LID_NOT_DIRTY_BIT 0x80000000
|
||||
#define CHFS_LID_DIRTY_BIT_MASK 0x7fffffff
|
||||
|
||||
/* sizeof(crc) + sizeof(lid) */
|
||||
#define CHFS_INVALIDATE_SIZE 8
|
||||
|
||||
/* Size of magic + crc_ec + erase_cnt */
|
||||
#define CHFS_EB_EC_HDR_SIZE sizeof(struct chfs_eb_ec_hdr)
|
||||
/* Size of NOR eraseblock header */
|
||||
#define CHFS_EB_HDR_NOR_SIZE sizeof(struct chfs_nor_eb_hdr)
|
||||
/* Size of NAND eraseblock header */
|
||||
#define CHFS_EB_HDR_NAND_SIZE sizeof(struct chfs_nand_eb_hdr)
|
||||
|
||||
/*
|
||||
* chfs_eb_ec_hdr - erase counter header of eraseblock
|
||||
* @magic: filesystem magic
|
||||
* @crc_ec: CRC32 sum of erase counter
|
||||
* @erase_cnt: erase counter
|
||||
*
|
||||
* This structure holds the erasablock description information.
|
||||
* This will be written to the beginning of the eraseblock.
|
||||
*
|
||||
*/
|
||||
struct chfs_eb_ec_hdr {
|
||||
le32 magic;
|
||||
le32 crc_ec;
|
||||
le32 erase_cnt;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct chfs_nor_eb_hdr - eraseblock header on NOR flash
|
||||
* @crc: CRC32 sum
|
||||
* @lid: logical identifier
|
||||
*
|
||||
* @lid contains the logical block reference but only the first 31 bit (0-30) is
|
||||
* used. The 32th bit is for marking a lid dirty (marked for recovery purposes).
|
||||
* If a new eraseblock is succesfully assigned with the same lid then the lid of
|
||||
* the old one is zeroed. If power failure happened during this operation then
|
||||
* the recovery detects that there is two eraseblock with the same lid, but one
|
||||
* of them is marked (the old one).
|
||||
*
|
||||
* Invalidated eraseblock header means that the @crc and @lid is set to 0.
|
||||
*/
|
||||
struct chfs_nor_eb_hdr {
|
||||
le32 crc;
|
||||
le32 lid;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct chfs_nand_eb_hdr - eraseblock header on NAND flash
|
||||
* @crc: CRC32 sum
|
||||
* @lid: logical identifier
|
||||
* @serial: layout of the lid
|
||||
*
|
||||
* @serial is an unique number. Every eraseblock header on NAND flash has its
|
||||
* own serial. If there are two eraseblock on the flash referencing to the same
|
||||
* logical eraseblock, the one with bigger serial is the newer.
|
||||
*/
|
||||
struct chfs_nand_eb_hdr {
|
||||
le32 crc;
|
||||
le32 lid;
|
||||
le64 serial;
|
||||
} __packed;
|
||||
|
||||
#endif /* EBH_MEDIA_H_ */
|
|
@ -0,0 +1,88 @@
|
|||
/* $NetBSD: ebh_misc.h,v 1.1 2011/11/24 15:51:32 ahoka Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2010 Department of Software Engineering,
|
||||
* University of Szeged, Hungary
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by the Department of Software Engineering, University of Szeged, Hungary
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef EBH_MISC_H_
|
||||
#define EBH_MISC_H_
|
||||
|
||||
/******************************************************************************/
|
||||
/* EBH specific functions */
|
||||
/******************************************************************************/
|
||||
|
||||
#define CHFS_GET_MEMBER_POS(type, member) \
|
||||
((unsigned long)(&((type *)0)->member))
|
||||
|
||||
#define CHFS_GET_LID(lid) (le32toh(lid) & CHFS_LID_DIRTY_BIT_MASK)
|
||||
|
||||
/**
|
||||
* EBH_TREE_DESTROY - destroys an RB-tree and frees the memory of its elements.
|
||||
* @name - the RB-tree structure's name
|
||||
* @head - pointer to the RB-tree's head
|
||||
* @type - type of the elements
|
||||
*/
|
||||
#define EBH_TREE_DESTROY(name, head, type) \
|
||||
{ \
|
||||
type *var, *nxt; \
|
||||
for (var = RB_MIN(name, head); var != NULL; var = nxt) { \
|
||||
nxt = RB_NEXT(name, head, var); \
|
||||
RB_REMOVE(name, head, var); \
|
||||
kmem_free(var, sizeof(type)); \
|
||||
} \
|
||||
}
|
||||
|
||||
/* XXX HACK! we need a clean solution for destroying mutexes in trees */
|
||||
#define EBH_TREE_DESTROY_MUTEX(name, head, type) \
|
||||
{ \
|
||||
type *var, *nxt; \
|
||||
for (var = RB_MIN(name, head); var != NULL; var = nxt) { \
|
||||
nxt = RB_NEXT(name, head, var); \
|
||||
RB_REMOVE(name, head, var); \
|
||||
rw_destroy(&var->mutex); \
|
||||
kmem_free(var, sizeof(type)); \
|
||||
} \
|
||||
}
|
||||
|
||||
/**
|
||||
* EBH_QUEUE_DESTROY - destroys a TAILQ and frees the memory of its elements.
|
||||
* @head: pointer to the head of the queue
|
||||
* @type: type of the elements
|
||||
* @entry: name of TAILQ_ENTRY
|
||||
*/
|
||||
#define EBH_QUEUE_DESTROY(head, type, entry) \
|
||||
{ \
|
||||
type *var; \
|
||||
while ((var = TAILQ_FIRST(head))) { \
|
||||
TAILQ_REMOVE(head, var, entry); \
|
||||
kmem_free(var, sizeof(type)); \
|
||||
} \
|
||||
}
|
||||
|
||||
#endif /* EBH_MISC_H_ */
|
|
@ -0,0 +1,200 @@
|
|||
/*-
|
||||
* Copyright (c) 2010 Department of Software Engineering,
|
||||
* University of Szeged, Hungary
|
||||
* Copyright (C) 2009 Ferenc Havasi <havasi@inf.u-szeged.hu>
|
||||
* Copyright (C) 2009 Zoltan Sogor <weth@inf.u-szeged.hu>
|
||||
* Copyright (C) 2009 David Tengeri <dtengeri@inf.u-szeged.hu>
|
||||
* Copyright (C) 2010 Adam Hoka <ahoka@NetBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by the Department of Software Engineering, University of Szeged, Hungary
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __CHFS_MEDIA_H__
|
||||
#define __CHFS_MEDIA_H__
|
||||
|
||||
#ifndef _LE_TYPES
|
||||
#define _LE_TYPES
|
||||
typedef uint16_t le16;
|
||||
typedef uint32_t le32;
|
||||
typedef uint64_t le64;
|
||||
#endif
|
||||
|
||||
/*****************************************************************************/
|
||||
/* File system specific structures */
|
||||
/*****************************************************************************/
|
||||
|
||||
enum {
|
||||
CHFS_NODETYPE_VNODE = 1,
|
||||
CHFS_NODETYPE_DATA,
|
||||
CHFS_NODETYPE_DIRENT,
|
||||
CHFS_NODETYPE_PADDING,
|
||||
};
|
||||
|
||||
//#define CHFS_NODE_HDR_SIZE 12 /* magic + type + length + hdr_crc */
|
||||
#define CHFS_NODE_HDR_SIZE sizeof(struct chfs_flash_node_hdr)
|
||||
|
||||
/* Max size we have to read to get all info.
|
||||
* It is max size of chfs_flash_dirent_node with max name length.
|
||||
*/
|
||||
#define CHFS_MAX_NODE_SIZE 299
|
||||
|
||||
/* This will identify CHfs nodes */
|
||||
#define CHFS_FS_MAGIC_BITMASK 0x4AF1
|
||||
|
||||
/**
|
||||
* struct chfs_flash_node_hdr - node header, its members are same for
|
||||
* all nodes, used at scan
|
||||
* @magic: filesystem magic
|
||||
* @type: node type
|
||||
* @length: length of node
|
||||
* @hdr_crc: crc of the first 3 members
|
||||
*/
|
||||
struct chfs_flash_node_hdr
|
||||
{
|
||||
le16 magic;
|
||||
le16 type;
|
||||
le32 length;
|
||||
le32 hdr_crc;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct chfs_flash_vnode - vnode informations stored on flash
|
||||
* @magic: filesystem magic
|
||||
* @type: node type (CHFS_NODETYPE_VNODE)
|
||||
* @length: length of node
|
||||
* @hdr_crc: crc of the first 3 members
|
||||
* @vno: vnode identifier id
|
||||
* @version: vnode's version number
|
||||
* @uid: owner of the file
|
||||
* @gid: group of file
|
||||
* @mode: permissions for vnode
|
||||
* @dn_size: size of written out data nodes
|
||||
* @atime: last access times
|
||||
* @mtime: last modification time
|
||||
* @ctime: change time
|
||||
* @dsize: size of the node's data
|
||||
* @node_crc: crc of full node
|
||||
*/
|
||||
struct chfs_flash_vnode
|
||||
{
|
||||
le16 magic; /*0 */
|
||||
le16 type; /*2 */
|
||||
le32 length; /*4 */
|
||||
le32 hdr_crc; /*8 */
|
||||
le64 vno; /*12*/
|
||||
le64 version; /*20*/
|
||||
le32 uid; /*28*/
|
||||
le32 gid; /*32*/
|
||||
le32 mode; /*36*/
|
||||
le32 dn_size; /*40*/
|
||||
le32 atime; /*44*/
|
||||
le32 mtime; /*48*/
|
||||
le32 ctime; /*52*/
|
||||
le32 dsize; /*56*/
|
||||
le32 node_crc; /*60*/
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct chfs_flash_data_node - node informations of data stored on flash
|
||||
* @magic: filesystem magic
|
||||
* @type: node type (CHFS_NODETYPE_DATA)
|
||||
* @length: length of node with data
|
||||
* @hdr_crc: crc of the first 3 members
|
||||
* @vno: vnode identifier id
|
||||
* @version: vnode's version number
|
||||
* @offset: offset in the file where write begins
|
||||
* @data_length: length of data
|
||||
* @data_crc: crc of data
|
||||
* @node_crc: crc of full node
|
||||
* @data: array of data
|
||||
*/
|
||||
struct chfs_flash_data_node
|
||||
{
|
||||
le16 magic;
|
||||
le16 type;
|
||||
le32 length;
|
||||
le32 hdr_crc;
|
||||
le64 vno;
|
||||
le64 version;
|
||||
le64 offset;
|
||||
le32 data_length;
|
||||
le32 data_crc;
|
||||
le32 node_crc;
|
||||
uint8_t data[0];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct chfs_flash_dirent_node - vnode informations stored on flash
|
||||
* @magic: filesystem magic
|
||||
* @type: node type (CHFS_NODETYPE_DIRENT)
|
||||
* @length: length of node
|
||||
* @hdr_crc: crc of the first 3 members
|
||||
* @vno: vnode identifier id
|
||||
* @pvno: vnode identifier id of parent vnode
|
||||
* @version: vnode's version number
|
||||
* @mctime:
|
||||
* @nsize: length of name
|
||||
* @dtype: file type
|
||||
* @unused: just for padding
|
||||
* @name_crc: crc of name
|
||||
* @node_crc: crc of full node
|
||||
* @name: name of the directory entry
|
||||
*/
|
||||
struct chfs_flash_dirent_node
|
||||
{
|
||||
le16 magic;
|
||||
le16 type;
|
||||
le32 length;
|
||||
le32 hdr_crc;
|
||||
le64 vno;
|
||||
le64 pvno;
|
||||
le64 version;
|
||||
le32 mctime;
|
||||
uint8_t nsize;
|
||||
uint8_t dtype;
|
||||
uint8_t unused[2];
|
||||
le32 name_crc;
|
||||
le32 node_crc;
|
||||
uint8_t name[0];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct chfs_flash_padding_node - node informations of data stored on
|
||||
* flash
|
||||
* @magic: filesystem magic
|
||||
* @type: node type (CHFS_NODETYPE_PADDING)
|
||||
* @length: length of node
|
||||
* @hdr_crc: crc of the first 3 members
|
||||
*/
|
||||
struct chfs_flash_padding_node
|
||||
{
|
||||
le16 magic;
|
||||
le16 type;
|
||||
le32 length;
|
||||
le32 hdr_crc;
|
||||
} __packed;
|
||||
|
||||
#endif /* __CHFS_MEDIA_H__ */
|
|
@ -1,9 +1,10 @@
|
|||
# $NetBSD: files.ufs,v 1.26 2011/03/24 17:05:45 bouyer Exp $
|
||||
# $NetBSD: files.ufs,v 1.27 2011/11/24 15:51:31 ahoka Exp $
|
||||
|
||||
deffs FFS
|
||||
deffs EXT2FS
|
||||
deffs MFS
|
||||
deffs LFS
|
||||
deffs CHFS
|
||||
|
||||
defflag opt_ffs.h FFS_EI FFS_NO_SNAPSHOT APPLE_UFS
|
||||
UFS_DIRHASH
|
||||
|
@ -22,18 +23,37 @@ file ufs/ext2fs/ext2fs_subr.c ext2fs
|
|||
file ufs/ext2fs/ext2fs_vfsops.c ext2fs
|
||||
file ufs/ext2fs/ext2fs_vnops.c ext2fs
|
||||
|
||||
file ufs/ffs/ffs_alloc.c ffs | lfs | mfs | ext2fs
|
||||
file ufs/ffs/ffs_balloc.c ffs | lfs | mfs | ext2fs
|
||||
file ufs/chfs/ebh.c chfs
|
||||
file ufs/chfs/chfs_ihash.c chfs
|
||||
file ufs/chfs/chfs_scan.c chfs
|
||||
file ufs/chfs/chfs_write.c chfs
|
||||
file ufs/chfs/chfs_vnode_cache.c chfs
|
||||
file ufs/chfs/chfs_erase.c chfs
|
||||
file ufs/chfs/chfs_build.c chfs
|
||||
file ufs/chfs/chfs_wbuf.c chfs
|
||||
file ufs/chfs/chfs_vnops.c chfs
|
||||
file ufs/chfs/chfs_gc.c chfs
|
||||
file ufs/chfs/chfs_nodeops.c chfs
|
||||
file ufs/chfs/chfs_malloc.c chfs
|
||||
file ufs/chfs/chfs_pool.c chfs
|
||||
file ufs/chfs/debug.c chfs
|
||||
file ufs/chfs/chfs_vnode.c chfs
|
||||
file ufs/chfs/chfs_subr.c chfs
|
||||
file ufs/chfs/chfs_vfsops.c chfs
|
||||
file ufs/chfs/chfs_readinode.c chfs
|
||||
|
||||
file ufs/ffs/ffs_alloc.c ffs | lfs | mfs | ext2fs | chfs
|
||||
file ufs/ffs/ffs_balloc.c ffs | lfs | mfs | ext2fs | chfs
|
||||
file ufs/ffs/ffs_bswap.c (ffs | mfs) & ffs_ei
|
||||
file ufs/ffs/ffs_inode.c ffs | lfs | mfs | ext2fs
|
||||
file ufs/ffs/ffs_snapshot.c ffs | lfs | mfs | ext2fs
|
||||
file ufs/ffs/ffs_subr.c ffs | lfs | mfs | ext2fs
|
||||
file ufs/ffs/ffs_tables.c ffs | lfs | mfs | ext2fs
|
||||
file ufs/ffs/ffs_vfsops.c ffs | lfs | mfs | ext2fs
|
||||
file ufs/ffs/ffs_vnops.c ffs | lfs | mfs | ext2fs
|
||||
file ufs/ffs/ffs_inode.c ffs | lfs | mfs | ext2fs | chfs
|
||||
file ufs/ffs/ffs_snapshot.c ffs | lfs | mfs | ext2fs | chfs
|
||||
file ufs/ffs/ffs_subr.c ffs | lfs | mfs | ext2fs | chfs
|
||||
file ufs/ffs/ffs_tables.c ffs | lfs | mfs | ext2fs | chfs
|
||||
file ufs/ffs/ffs_vfsops.c ffs | lfs | mfs | ext2fs | chfs
|
||||
file ufs/ffs/ffs_vnops.c ffs | lfs | mfs | ext2fs | chfs
|
||||
file ufs/ffs/ffs_wapbl.c ffs & wapbl
|
||||
file ufs/ffs/ffs_appleufs.c ffs & apple_ufs
|
||||
file ufs/ffs/ffs_quota2.c quota2 & (ffs | lfs | mfs | ext2fs)
|
||||
file ufs/ffs/ffs_quota2.c quota2 & (ffs | lfs | mfs | ext2fs | chfs)
|
||||
|
||||
file ufs/lfs/lfs_alloc.c lfs
|
||||
file ufs/lfs/lfs_balloc.c lfs
|
||||
|
@ -53,17 +73,17 @@ file ufs/mfs/mfs_vfsops.c mfs
|
|||
file ufs/mfs/mfs_vnops.c mfs
|
||||
file ufs/mfs/mfs_miniroot.c
|
||||
|
||||
file ufs/ufs/ufs_bmap.c ffs | lfs | mfs | ext2fs
|
||||
file ufs/ufs/ufs_dirhash.c (ffs | lfs | mfs | ext2fs) & ufs_dirhash
|
||||
file ufs/ufs/ufs_bmap.c ffs | lfs | mfs | ext2fs | chfs
|
||||
file ufs/ufs/ufs_dirhash.c (ffs | lfs | mfs | ext2fs | chfs) & ufs_dirhash
|
||||
file ufs/ufs/ufs_extattr.c (ffs | mfs) & ufs_extattr
|
||||
file ufs/ufs/ufs_ihash.c ffs | lfs | mfs | ext2fs
|
||||
file ufs/ufs/ufs_inode.c ffs | lfs | mfs | ext2fs
|
||||
file ufs/ufs/ufs_lookup.c ffs | lfs | mfs | ext2fs
|
||||
file ufs/ufs/ufs_quota.c (quota | quota2) & (ffs | lfs | mfs | ext2fs)
|
||||
file ufs/ufs/ufs_quota1.c quota & (ffs | lfs | mfs | ext2fs)
|
||||
file ufs/ufs/ufs_quota2.c quota2 & (ffs | lfs | mfs | ext2fs)
|
||||
file ufs/ufs/ufs_lookup.c ffs | lfs | mfs | ext2fs | chfs
|
||||
file ufs/ufs/ufs_quota.c (quota | quota2) & (ffs | lfs | mfs | ext2fs | chfs)
|
||||
file ufs/ufs/ufs_quota1.c quota & (ffs | lfs | mfs | ext2fs | chfs)
|
||||
file ufs/ufs/ufs_quota2.c quota2 & (ffs | lfs | mfs | ext2fs | chfs)
|
||||
file ufs/ufs/quota1_subr.c
|
||||
file ufs/ufs/quota2_subr.c quota2 & (ffs | lfs | mfs | ext2fs)
|
||||
file ufs/ufs/ufs_vfsops.c ffs | lfs | mfs | ext2fs
|
||||
file ufs/ufs/ufs_vnops.c ffs | lfs | mfs | ext2fs
|
||||
file ufs/ufs/quota2_subr.c quota2 & (ffs | lfs | mfs | ext2fs | chfs)
|
||||
file ufs/ufs/ufs_vfsops.c ffs | lfs | mfs | ext2fs | chfs
|
||||
file ufs/ufs/ufs_vnops.c ffs | lfs | mfs | ext2fs | chfs
|
||||
file ufs/ufs/ufs_wapbl.c ffs & wapbl
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: ufsmount.h,v 1.36 2011/03/06 17:08:39 bouyer Exp $ */
|
||||
/* $NetBSD: ufsmount.h,v 1.37 2011/11/24 15:51:32 ahoka Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1982, 1986, 1989, 1993
|
||||
|
@ -81,12 +81,14 @@ struct ufsmount {
|
|||
union { /* pointer to superblock */
|
||||
struct fs *fs; /* FFS */
|
||||
struct lfs *lfs; /* LFS */
|
||||
struct m_ext2fs *e2fs; /* EXT2FS */
|
||||
struct m_ext2fs *e2fs; /* EXT2FS */
|
||||
struct chfs_mount *chfs; /* CHFS */
|
||||
} ufsmount_u;
|
||||
#define um_fs ufsmount_u.fs
|
||||
#define um_lfs ufsmount_u.lfs
|
||||
#define um_e2fs ufsmount_u.e2fs
|
||||
#define um_e2fsb ufsmount_u.e2fs->s_es
|
||||
#define um_chfs ufsmount_u.chfs
|
||||
|
||||
/* Extended attribute information. */
|
||||
struct ufs_extattr_per_mount um_extattr;
|
||||
|
|
Loading…
Reference in New Issue