blob: ceec511eee8b95dfee3ac070f44ac5331bb06b85 [file] [edit]
/*
* Copyright 2016, NICTA
*
* This software may be distributed and modified according to the terms of
* the GNU General Public License version 2. Note that NO WARRANTY is provided.
* See "LICENSE_GPLv2.txt" for details.
*
* @TAG(NICTA_GPL)
*/
#ifndef __BILBYFS_H__
#define __BILBYFS_H__
#include <wrapper.h>
#define bilbyfs_assert(v) BUG_ON(!(v))
#define bilbyfs_err(...) \
printk(KERN_ERR __VA_ARGS__)
#define bilbyfs_msg(...) \
printk(KERN_INFO __VA_ARGS__)
#ifndef BILBYFS_DEBUG
#define bilbyfs_debug(...) \
do {} while (0)
#else
#define bilbyfs_debug(...) \
printk(KERN_ERR __VA_ARGS__)
#endif /* !BILBYFS_DEBUG */
#define BILBYFS_SUPER_MAGIC 0x0b17b9f5
#define BILBYFS_ROOT_INO 24
#define BILBYFS_FIRST_SQNUM 1 /* Must be greater than 0 */
#define BILBYFS_FIRST_INO (BILBYFS_ROOT_INO + 1)
#define BILBYFS_SUP_LNUM 0
#define BILBYFS_LOG_FST_LNUM 3
#define BILBYFS_MIN_USABLE_LOG_SZ 2
#define BILBYFS_MAX_READDIR_DATA_SIZE 64
#define BILBYFS_BD_MAX_NLEN 16 /* maximum length of block device */
#define BILBYFS_MAX_NLEN 255 /* maximum length of a file name */
#define BILBYFS_DEFAULT_NB_RESERVED_GC 3
#define BILBYFS_DEFAULT_NB_RESERVED_DEL 3
#define BILBYFS_OBJ_MAGIC 0xb17b9f50
#define BILBYFS_PAD_BYTE 0x42
#define BILBYFS_OBJ_PADDING 8
#define BILBYFS_CRC32_INIT 0xFFFFFFFFU
#define BILBYFS_TRUE (1)
#define BILBYFS_FALSE (0)
/*
* BILBYFS inode types.
*
* BILBYFS_ITYPE_REG: regular file
* BILBYFS_ITYPE_DIR: directory
* BILBYFS_ITYPE_LNK: soft link
* BILBYFS_ITYPE_BLK: block device node
* BILBYFS_ITYPE_CHR: character device node
* BILBYFS_ITYPE_FIFO: fifo
* BILBYFS_ITYPE_SOCK: socket
* BILBYFS_ITYPES_CNT: count of supported file types
*/
enum {
BILBYFS_ITYPE_REG,
BILBYFS_ITYPE_DIR,
BILBYFS_ITYPE_LNK,
BILBYFS_ITYPE_BLK,
BILBYFS_ITYPE_CHR,
BILBYFS_ITYPE_FIFO,
BILBYFS_ITYPE_SOCK,
BILBYFS_ITYPES_CNT,
};
/*
* Object types.
*
* BILBYFS_INODE_OBJ: inode object
* BILBYFS_DATA_OBJ: data object
* BILBYFS_DENTARR_OBJ: directory entry array object
* BILBYFS_PAD_OBJ: padding object
* BILBYFS_OBJ_TYPES_CNT: count of supported object types
*
* Note: we use these constants for id types as well.
* Directory entry array (dentarr) is a collection of obj_dentry
* see the definition of obj_dentry.
*/
enum {
BILBYFS_INODE_OBJ,
BILBYFS_DATA_OBJ,
BILBYFS_DENTARR_OBJ,
BILBYFS_SUP_OBJ,
BILBYFS_PAD_OBJ,
BILBYFS_DEL_OBJ,
BILBYFS_SUM_OBJ,
BILBYFS_OBJ_TYPES_CNT,
};
/* Object inode flags (on-flash inode flags)
* BILBYFS_ORPHAN_INODE: orhpan inode flag
*/
enum {
BILBYFS_ORPHAN_FL = 0x01,
BILBYFS_APPEND_FL = 0x02,
BILBYFS_IMMUTABLE_FL = 0x04,
};
/* Super object flags
* BILBYFS_SUP_ISDIRTY: was the file system unmount cleanly?
*/
enum {
BILBYFS_SUP_ISDIRTY = 0x01,
};
/* obj_id: id to retrieve an object from the index.
* This id is 64 bits long organised differently for each
* type of object:
* - inode: inode number (32 bits), zero (32 bits)
* - dentarr: inode number (32 bits), type dentry (3 bits), name hash (29 bits)
* - data: inode number (32 bits), type data (3 bits), block index (29 bits)
*/
typedef u64 obj_id;
typedef __le64 __le_obj_id; /* little endian version: as we store it on the media */
/* NIL_ID: Key returned by next_obj_id() when the id passed as argument is the
* highest id present in the index: there is no next id.
*/
#define NIL_ID ((u64)~0)
/* Number of bits available to store extra information in an id */
#define BILBYFS_ID_XINFO_BITS 29
#define BILBYFS_ID_XINFO_MASK 0x1FFFFFF
#define BILBYFS_MAX_FILE_SZ (1ULL << (BILBYFS_ID_XINFO_BITS + BILBYFS_BLOCK_SHIFT))
/* inum_from_id: extract the inode number from an id
* @id: id to analyse
* Returns the inode number of the id.
*/
static inline ino_t inum_from_id(obj_id id)
{
return id >> 32;
}
/* type_from_id: extract the type of an id
* @id: id to analyse
* Returns the type of the id (BILBYFS_INODE_OBJ, BILBYFS_DATA_OBJ...).
*/
static inline u32 type_from_id(obj_id id)
{
return (id >> BILBYFS_ID_XINFO_BITS) & 7;
}
static inline u32 xinfo_from_id(obj_id id)
{
return (id & BILBYFS_ID_XINFO_MASK);
}
/* inode_id_init: Initialise an inode id.
* ino: inode number of the id
* returns the id
*/
static inline obj_id inode_id_init(ino_t ino)
{
obj_id id = (u64)ino << 32;
return id;
}
/**
* id_hash_nm - R5 hash function (borrowed from reiserfs).
* @s: dentry name
* @len: name length
*/
static inline uint32_t id_hash_nm(const char *s, int len)
{
uint32_t a = 0;
const signed char *str = (const signed char *)s;
while (*str) {
a += *str << 4;
a += *str >> 4;
a *= 11;
str++;
}
return (a & BILBYFS_ID_XINFO_MASK);
}
/* dentarr_id_init: Initialise an `dentry array' id.
* ino: inode number of the id
* returns the id
*/
static inline obj_id dentarr_id_init(ino_t ino, const char *name)
{
obj_id id = (u64)ino << 32 |
(BILBYFS_DENTARR_OBJ << BILBYFS_ID_XINFO_BITS) |
id_hash_nm(name, strlen(name));
return id;
}
/* data_id_init: Initialise an data id.
* ino: inode number of the id
* block: block index number
* returns the id
*/
static inline obj_id data_id_init(ino_t ino, u32 block)
{
obj_id id = (u64)ino << 32 |
(BILBYFS_DATA_OBJ << BILBYFS_ID_XINFO_BITS) |
block;
return id;
}
/* is_inode_id: assess if the id is a inode id
* @id: id to assess
* Returns 1 if id is a inode id 0 otherwise
*/
static inline int is_inode_id(obj_id id)
{
return (type_from_id(id) == BILBYFS_INODE_OBJ);
}
/* is_dentarr_id: assess if the id is a `dentry array' id
* @id: id to assess
* Returns 1 if id is a `dentry array' id 0 otherwise
*/
static inline int is_dentarr_id(obj_id id)
{
return (type_from_id(id) == BILBYFS_DENTARR_OBJ);
}
/* is_data_id: assess if the id is a data id
* @id: id to assess
* Returns 1 if id is a data id 0 otherwise
*/
static inline int is_data_id(obj_id id)
{
return (type_from_id(id) == BILBYFS_DATA_OBJ);
}
/* struct obj_addr: Address of an object on-disk
* @lnum: logical-erase block number
* @offs: offset inside the logical-erase block
* @len: length of the object
*/
struct obj_addr {
u32 lnum;
u32 offs;
u32 len;
u64 sqnum;
};
/*
* struct bilbyfs_wbuf - BilbyFs write-buffer.
* @buf: write-buffer (of min. flash I/O unit size)
* @size: write-buffer size (in [@c->min_io_size, @c->max_write_size] range)
* @avail: number of bytes available in the write-buffer
* @used: number of used bytes in the write-buffer
* @sync_offs: offset to which data was synchronised to disk
*
*/
struct bilbyfs_wbuf {
void *buf;
int size;
int avail;
int used;
int sync_offs;
};
/*
* struct bilbyfs_rbuf - BilbyFs read-buffer.
* @buf: buffer of leb_size
* @offs: read-buffer offset in this logical eraseblock
* @size: read-buffer size (usually leb_size)
*/
struct bilbyfs_rbuf {
void *buf;
int size;
int offs;
};
/* struct bilbyfs_inode: in-memory inode representation
* @vfs_inode: VFS inode is embedded and MUST be the first field for
* container_of to work.
* @creat_sqnum: sequence number generated when the inode was created
* @flags: flags that will be stored on flash
*/
struct bilbyfs_inode {
struct inode vfs_inode;
u64 creat_sqnum;
int flags;
};
/* inode_to_binode: obtain a bilbyfs_inode from a VFS inode.
* @inode: inode to introspect
* Returns the bilbyfs_inode pointer attached to @inode.
*/
static inline struct bilbyfs_inode *inode_to_binode(struct inode *inode)
{
return (void *)inode;
}
static inline void inode_init_perm(struct inode *inode, const struct inode *dir,
umode_t mode)
{
inode->i_uid = current_fsuid();
if (dir && dir->i_mode & S_ISGID) {
inode->i_gid = dir->i_gid;
if (S_ISDIR(mode))
mode |= S_ISGID;
} else
inode->i_gid = current_fsgid();
inode->i_mode = mode;
}
/* struct bilbyfs_dirent: Generic directory entry structure used for readdir
* The structure allows to abstract away the Linux VFS filldir callback.
* @d_ino: inode number the directory entry is pointing to
* @d_off: offset of the directory entry in the directory
* @d_reclen: length of the name
* @d_type: type of inode (DT_REG, DT_DIR, DT_LNK...)
* @d_name: name of the (file/directory/link...)
*/
struct bilbyfs_dirent {
unsigned long d_ino;
unsigned long d_off;
unsigned short d_reclen;
unsigned int d_type;
char d_name[BILBYFS_MAX_NLEN];
};
/* struct bilbyfs_super: in-memory superblock object representation
* @uuid: UUID from superblock
* @flags: (%BILBYFS_SUP_ISDIRTY, ...)
* @sqnum: sequence number of the super object, is the highest one if the FS was
* cleanly unmounted.
* @leb_size: size of a LEB for the media.
* @leb_cnt: number of LEB for the fs.
* @nb_reserved_gc: number of LEB reserved for GC
* @nb_reserved_del: number of LEB reserved for deletion
* @min_io_size: minimum amount of data we can write in a LEB in one go
* @max_io_size: maximum amount of data we can write in a LEB in one go
* @log_lnum: last erase-block number in the log (likely partially written)
* @log_offs: offset in the last erase-block of the log.
* @lowest_sqnum: lowest sqnum of a valid object on-flash (therefore indexed)
* @last_inum: last inode number used when unmount (next one is +1).
*/
struct bilbyfs_super {
u8 uuid[16];
u32 flags;
u64 sqnum;
int leb_cnt;
int leb_size;
int nb_reserved_gc;
int nb_reserved_del;
int min_io_size;
int max_io_size;
u32 log_lnum;
u32 log_offs;
u64 lowest_sqnum;
u64 last_inum;
};
/* Red-Black tree node */
struct rbt_node {
unsigned long rbt_parent_color;
#define RBT_RED 0
#define RBT_BLACK 1
struct rbt_node *rbt_left;
struct rbt_node *rbt_right;
};
struct rbt_root {
struct rbt_node *rbt_node;
};
/* idx_node: Node index
* node: red-black tree node
* addr: address and sqnum of the object stored in the index
*/
struct idx_node {
struct rbt_node node;
struct obj_addr addr;
obj_id id;
};
/* gim_node: Node for GIM tree
* @node: red-black tree node
* @id: object id
* @sqnum: sequence number of the newest garbage object
* @count: number of objects with the same id
*/
struct gim_node {
struct rbt_node node;
obj_id id;
u64 sqnum;
u32 count;
};
#define NODE_SIZE max(sizeof(struct idx_node), sizeof(struct gim_node))
/* alloc_pool: Pool of pre-allocated content
* @arr: array of content
* @len: length of array of content
*/
struct alloc_pool {
int tot_nodes_needed;
void **arr;
int len;
int i;
int sz_obj;
};
/*
* Transactional object store flags
* BILBYFS_TRANS_ST: first object of a transaction with multiple objects
* BILBYFS_TRANS_IN: object in a transaction that is not the first nor the last
* one
* BILBYFS_TRANS_END: last object of a transaction
* BILBYFS_TRANS_ATOM: transaction of a single object
*
* Valid sequences pattern: (ST IN* END | ATOM)
*/
enum {
BILBYFS_TRANS_ST = 0x1,
BILBYFS_TRANS_IN = 0x2,
BILBYFS_TRANS_END = 0x4,
BILBYFS_TRANS_ATOM = 0x1 | 0x4,
};
/**
* struct obj_ch - object common header.
* @magic: object magic number (%BILBYFS_NODE_MAGIC)
* @crc: CRC-32 checksum of the object header
* @sqnum: sequence number
* @len: full object length (including this header)
* @type: object type (%BILBYFS_INODE_OBJ, %BILBYFS_EXPR_OBJ...)
* @trans: position in transaction (%BILBYFS_TRANS_*)
* @padding: reserved for future, zeroes
*
* Every object on-flash starts with this common part. If the object has an id, the
* id always goes next.
*/
struct obj_ch {
__le32 magic;
__le32 crc;
__le64 sqnum;
__le32 len;
__u8 type;
__u8 trans;
__u8 padding[2];
} __packed;
static inline void zero_obj_ch_unused(struct obj_ch *ch)
{
memset(ch->padding, 0, 2);
}
/**
* struct obj_inode - inode object.
* @ch: common header
* @id: object id
* @creat_sqnum: sequence number at time of creation
* @size: inode size in bytes
* @atime_sec: access time seconds
* @ctime_sec: creation time seconds
* @mtime_sec: modification time seconds
* @nlink: number of hard links
* @uid: owner ID
* @gid: group ID
* @mode: access flags, inode type...
* @flags: BILBYFS_ORPHAN_FL, BILBYFS_APPEND_FL...
*/
struct obj_inode {
struct obj_ch ch;
__le_obj_id id;
__le64 creat_sqnum;
__le64 size;
__le64 atime_sec;
__le64 ctime_sec;
__le64 mtime_sec;
__le32 nlink;
__le32 uid;
__le32 gid;
__le32 mode;
__le32 flags;
__le32 padding;
} __packed;
static inline void zero_obj_inode_unused(struct obj_inode *ino)
{
ino->padding = 0;
}
/**
* struct obj_dentarr - `directory entry array' object.
* @ch: common header
* @id: object id
* @nb_dentry: number of dentry object in the array (i.e. hash collisions)
* @size: size in bytes of the obj_dentarr including the header (ch) but as
* opposed to ch.len, @size is not aligned.
*/
struct obj_dentarr {
struct obj_ch ch;
__le_obj_id id;
__le32 nb_dentry;
__le32 size;
} __packed;
static inline void zero_obj_dentarr_unused(struct obj_dentarr *dentarr)
{
}
/* struct obj_dentry - directory entry object.
* @ino: target inode number
* @type: type of the target inode (%BILBYFS_ITYPE_REG, %BILBYFS_ITYPE_DIR, etc)
* @nlen: name length
* @name: zero-terminated name
*
* No padding here for simplicity.
*/
struct obj_dentry {
__le32 ino;
__u8 type;
__le16 nlen;
__u8 name[];
} __packed;
static inline void zero_obj_dentry_unused(struct obj_dentry *de)
{
}
/**
* struct obj_data - data object.
* @ch: common header
* @id: object id
* @size: data size in bytes
* @padding: reserved for future, zeroes
* @data: data
*/
struct obj_data {
struct obj_ch ch;
__le_obj_id id;
__le32 size;
__u8 padding[4];
__u8 data[];
} __packed;
static inline void zero_obj_data_unused(struct obj_data *data)
{
memset(data->padding, 0, 4);
}
/**
* struct obj_del - deletion object
* @ch: common header
* @id: id of the object which is deleted
*/
struct obj_del {
struct obj_ch ch;
__le_obj_id id;
} __packed;
static inline void zero_obj_del_unused(struct obj_del *del)
{
}
/**
* struct obj_sum_entry - summary object
* @id: obj id
* @sqnum: sequence number of the object
* @len: length of the object
* @del_flag_and_offs: the most significant bit indicates whether this
* is a deletion entry, the rest of bits are the offset of the object
* within the erase-block.
* @count: number of objects covered by deletion object (can't we use len
* for this for deletion objects? Their size is fixed isn't it?
*/
struct obj_sum_entry {
__le_obj_id id;
__le64 sqnum;
__le32 len;
__le32 del_flag_and_offs;
#define BILBYFS_SUM_ENTRY_DEL_FLAG_MASK 0x80000000
__le16 count;
} __packed;
static inline void zero_obj_sum_entry_unused(struct obj_sum_entry *sum_entry)
{
}
static inline u32 obj_sum_entry_offs(struct obj_sum_entry *sum_entry)
{
return (le32_to_cpu(sum_entry->del_flag_and_offs) & ~ BILBYFS_SUM_ENTRY_DEL_FLAG_MASK);
}
static inline u32 obj_sum_entry_is_del(struct obj_sum_entry *sum_entry)
{
return !!(le32_to_cpu(sum_entry->del_flag_and_offs) & BILBYFS_SUM_ENTRY_DEL_FLAG_MASK);
}
/**
* struct obj_sum - summary object
* @ch: common header
* @nb_sum_entry: number of entries in the summary
* @entries: summary entries
*/
struct obj_sum {
struct obj_ch ch;
__le32 nb_sum_entry;
struct obj_sum_entry entries[];
/* __le32 offs; The very last word32 is an offset to the summary object */
#define BILBYFS_OBJ_SUM_OFFS_SZ 4
} __packed;
static inline void zero_obj_sum_unused(struct obj_sum *sum)
{}
static inline u32 *obj_sum_offs(struct obj_sum *sum)
{
return (u32 *)((void *)sum + le32_to_cpu(sum->ch.len) - BILBYFS_OBJ_SUM_OFFS_SZ);
}
/* get_obj_id: Get the obj_id of an object
* @obj: pointer to the object
*
* Note that only indexed objects have an ID.
* Object ID has to be positioned right after the common header.
*/
static inline obj_id get_obj_id(void *obj)
{
struct obj_ch *ch = obj;
__le_obj_id *le_id = (__le_obj_id *)(ch + 1);
bilbyfs_assert(ch->type == BILBYFS_INODE_OBJ ||
ch->type == BILBYFS_DENTARR_OBJ ||
ch->type == BILBYFS_DATA_OBJ ||
ch->type == BILBYFS_DEL_OBJ );
return le64_to_cpu(*le_id);
}
/**
* struct obj_super - super object.
* @ch: common header
* @flags: various flags (%BILBYFS_FS_DIRTY, etc)
* @gc_lnum: LEB reserved for garbage collection
* @total_free: total free space in bytes
* @total_dirty: total dirty space in bytes
* @total_used: total used space in bytes (includes only data LEBs)
* @total_dead: total dead space in bytes (includes only data LEBs)
* @total_dark: total dark space in bytes (includes only data LEBs)
* @empty_lebs: number of empty logical eraseblocks
* @leb_cnt: count of LEBs used by file-system
* @nb_reserved_gc: Number of LEBs reserved for garbage collection
* @log_head_leb: where was the head of log at last unmount
* @log_head_offs: and its offset in the leb
* @lowest_sqnum: Lowest valid sqnum on FS?
* @last_inum: Last inode number generated
*/
struct obj_super {
struct obj_ch ch;
#define BILBYFS_FS_DIRTY 0x1
__le32 flags;
__le32 gc_lnum;
__le64 total_free;
__le64 total_dirty;
__le64 total_used;
__le64 total_dead;
__le64 total_dark;
__le32 empty_lebs;
__le32 leb_size;
__le32 leb_cnt;
__le32 nb_reserved_gc;
__le32 nb_reserved_del;
__le32 log_head_leb;
__le32 log_head_offs;
__le64 lowest_sqnum;
__le64 last_inum;
__u8 padding[4];
} __packed;
static inline void zero_obj_super_unused(struct obj_super *o)
{
memset(o->padding, 0, 4);
}
/* Size of objects */
#define BILBYFS_CH_SZ sizeof(struct obj_ch)
#define BILBYFS_INODE_SZ sizeof(struct obj_inode)
#define BILBYFS_DATA_SZ sizeof(struct obj_data)
#define BILBYFS_DENTARR_SZ sizeof(struct obj_dentarr)
#define BILBYFS_SUPER_SZ sizeof(struct obj_super)
/* Note: obj_dentry objects do NOT have a common header */
#define BILBYFS_DENTRY_SZ sizeof(struct obj_dentry)
#define BILBYFS_DEL_SZ sizeof(struct obj_del)
#define BILBYFS_SUM_SZ sizeof(struct obj_sum)
#define BILBYFS_SUM_ENTRY_SZ sizeof(struct obj_sum_entry)
/* BILBYFS_BLOCK_SIZE: Block granularity of the index and maximum amount
* of data attached to a obj_data.
*/
#define BILBYFS_BLOCK_SIZE 4096
#define BILBYFS_BLOCK_SHIFT 12
/* Maximum object size (must all be aligned on 8) */
#define BILBYFS_MAX_DATA_SZ (BILBYFS_DATA_SZ + BILBYFS_BLOCK_SIZE)
#define BILBYFS_MAX_DENTRY_SZ (BILBYFS_DENTRY_SZ + BILBYFS_MAX_NLEN + 1)
/* Maximum number of entries stored in a dentarr.
* We put this limitation to ensure that all transactions fits in a leb even
* in the worst case scenario.
*/
#define BILBYFS_MAX_DENTARR_ENTRIES 16
#define BILBYFS_MAX_DENTARR_SZ (BILBYFS_DENTARR_SZ + \
BILBYFS_MAX_DENTARR_ENTRIES * BILBYFS_MAX_DENTRY_SZ)
/* dentarr clearly has the biggest obj size */
#define BILBYFS_MAX_OBJ_SZ BILBYFS_MAX_DENTARR_SZ
#define BILBYFS_MIN_OBJ_SZ BILBYFS_CH_SZ
/* Maximum number of object in one transaction
* A transaction can span a whole erase-block, hence this number should
* be large enough to commodate this
*/
#define BILBYFS_MAX_OBJ_PER_TRANS 2048
#define BILBYFS_LAST_INUM 0xFFFFFF00 /* max inode number */
static inline int obj_dentry_size(struct obj_dentry *de)
{
return BILBYFS_DENTRY_SZ + le16_to_cpu(de->nlen) + 1;
}
static inline int obj_dentarr_size(struct obj_dentarr *dearr)
{
return (!le32_to_cpu(dearr->size) ? BILBYFS_DENTARR_SZ : le32_to_cpu(dearr->size));
}
static inline int obj_dentry_size_from_nm(const char *name)
{
return BILBYFS_DENTRY_SZ + strlen(name) + 1;
}
static inline int obj_dentarr_size_with_nm(struct obj_dentarr *dentarr,
const char *name)
{
return obj_dentarr_size(dentarr) + obj_dentry_size_from_nm(name);
}
static inline int obj_data_size_with_data(int data_size)
{
return BILBYFS_DATA_SZ + data_size;
}
static inline int obj_sum_size(struct obj_sum *sum)
{
return ALIGN(BILBYFS_SUM_SZ + le32_to_cpu(sum->nb_sum_entry) * BILBYFS_SUM_ENTRY_SZ + BILBYFS_OBJ_SUM_OFFS_SZ, BILBYFS_OBJ_PADDING);
}
/* struct bilbyfs_info:
* @vfs_sb: VFS superblock structure
* @ubi: UBI volume descriptor
* @di: UBI device information
* @vi: UBI volume information
* @next_inum: next inode number to use
* @next_sqnum: next sequence number to use
* @is_ro: Is the FS in read-only mode?
* @wbuf: write-buffer for writing objects in a transaction
* @rbuf: read-buffer to store an entire leb
* @super: super block structure
* @super_offs: superblock on-flash address
* @dirty_list: a list containing how much garbage each erase-block has
* @used_leb_list: a list indicating whether an erase-block is used
* @fsm_lnum: lnum of currently active LEB
* @fsm_offs: offset of next free space in currently active LEB
* @gc_buf: buffer for garbage collection
* @no_summary: mount option value
*/
struct bilbyfs_info {
struct wrapper_data wd;
/* VFS specific */
struct super_block *vfs_sb;
/* UBI */
struct ubi_volume_desc *ubi;
struct ubi_device_info di;
struct ubi_volume_info vi;
/* BilbyFs */
/* FsOp */
u32 next_inum;
u64 next_sqnum;
u32 is_ro;
struct bilbyfs_dirent dirent;
struct obj_data *odata;
/* Index */
struct rbt_root *idx_hash;
/* Wbuf */
struct bilbyfs_wbuf wbuf;
struct bilbyfs_rbuf rbuf;
/* Ostore */
struct bilbyfs_super super;
struct bilbyfs_wbuf sup_wbuf;
int super_offs;
struct alloc_pool node_pool;
struct obj_sum *summary;
struct obj_addr *addrs;
u32 is_mounting;
/* FSM */
u32 *dirty_list;
u8 *used_leb_list;
u32 fsm_lnum;
u32 fsm_nb_free_eb;
/* GIM */
struct rbt_root *gim_hash;
/* GC */
struct bilbyfs_rbuf gc_buf;
/* Mount */
int no_summary;
int nb_pages;
int nb_extra_pages;
int gim_allocated_for_nothing;
};
/**
* next_sqnum: obtain a unique sequence number.
* @bi: global fs info
* This function returns an unique sequence number and
* should be the only function accessing @bi->next_sqnum.
*/
static inline u64 next_sqnum(struct bilbyfs_info *bi)
{
bi->next_sqnum++;
return bi->next_sqnum;
}
/**
* next_inum: obtain a unused inode number.
* @bi: global fs info
* This function returns an unused inode number and
* should be the only function accessing @bi->next_inum.
*/
static inline u32 next_inum(struct bilbyfs_info *bi)
{
bi->next_inum++;
return bi->next_inum;
}
/**
* inode_current_time - round current time to time granularity.
* @inode: inode
*/
struct timespec64 inode_current_time(struct inode *inode);
/* ReadDir ConteXt: This component helps to list a inline diretory by sequentially
* reading all directory entries in a directory.
*
* fsop_readdir_ctx: current position of the directory listing.
* @dentarr: current dentarr evaluated.
* @de: current diretory entry in the dentarr
* @id: id of the current dentarr;
*/
struct fsop_readdir_ctx {
struct obj_dentarr *dentarr;
struct obj_dentry *de;
obj_id id;
};
/* rdx_init: initialise a readdir context
* @bi: global fs info
* @rdx: context holder
* @ino: inode number of the directory to list
*/
void rdx_init(struct bilbyfs_info *bi, struct fsop_readdir_ctx *rdx, ino_t ino);
/* rdx_clean: clean a readdir context by freeing the potentially allocated
* dentarr.
* @bi: global fs info
* @rdx: context holder
*/
void rdx_clean(struct fsop_readdir_ctx *rdx);
/* rdx_next_dentry: obtain the next directory entry given a context.
* dentarr.
* @bi: global fs info
* @rdx: context holder
*/
struct obj_dentry *rdx_next_dentry(struct bilbyfs_info *bi, struct fsop_readdir_ctx *rdx);
/* File System Operations (fsop) component:
* This component implements all the logic of the file system.
* It only manipulates objects using their ID, every filesystem operation is
* implemented as a list of objects modifications.
*
* See fsop.c.
*/
/* fsop_iget: Read an inode from the disk.
* @bi: global fs info
* @inum: inode number to read
* @inode: inode pointer to store the result to
*
* The function returns a negative error code if unsuccessful and zero
* otherwise.
*/
int fsop_iget(struct bilbyfs_info *bi, unsigned long inum, struct inode *inode);
/* fsop_lookup: Inspect a directory to find a name
* @bi: global fs info
* @dir: inode of the directory to inspect
* @name: name of the file/directory to find
* @ino: pointer where to store the inode number
*
* fsop_lookup will look for the name specified by @dentry->d_name and store
* the inode number in the parameter @ino.
* The function returns a negative error code if unsuccessful and zero
* otherwise
*/
int fsop_lookup(struct bilbyfs_info *bi, struct inode *dir, const char *name, ino_t *ino);
/* fsop_link: Create a hardlink
* @bi: global fs info
* @inode: target inode
* @dir: source inode from which we want to add a directory entry
* @name: name of the link to create
*
* Add a link (directory entry) from @dir to @inode. The function
* has to deal with the nlink counters of inodes. The function returns a
* negative error code if unsuccessful and zero otherwise.
*/
int fsop_link(struct bilbyfs_info *bi, struct inode *inode, struct inode *dir, const char *name);
/* fsop_create: Create a file in a directory
* @bi: global fs info
* @dir: directory inode in which the file is created
* @name: name of the file to create
* @mode: permission, inode type...
* @excl: NFS specific exclusion flag? FIXME
* @inode: inode to fill
*
* If the function is successful it creates a file on disk in @dir and fill in
* information about the file in @inode.
* The function returns a negative error code if unsuccessful and zero
* otherwise.
*/
int fsop_create(struct bilbyfs_info *bi, struct inode *dir, const char *name,
umode_t mode, int excl, struct inode *inode);
/* fsop_unlink: Remove a hardlink from a file inode
* @bi: global fs info
* @dir: directory inode from which we remove a directory entry
* @name: name of the file to unlink
* @inode: inode to which @dentry points
*
* The function removes @dentry and decrements the nlink counter of the inode
* pointed by @dentry. If the counter reaches 0, the inode is removed as well
* The function returns a negative error code if unsuccessful and zero
* otherwise.
*/
int fsop_unlink(struct bilbyfs_info *bi, struct inode *dir, const char *name, struct inode *inode);
/* fsop_rmdir: Remove an entry from a directory
* @bi: global fs info
* @dir: directory inode from which we remove a directory entry
* @dentry: directory entry to remove
* @inode: inode to which @dentry points
*
* The function removes @dentry from the directory @dir.
* The function returns a negative error code if unsuccessful and zero
* otherwise.
*/
int fsop_rmdir(struct bilbyfs_info *bi, struct inode *dir, const char *name, struct inode *inode);
/* fsop_mkdir: Create a directory
* @bi: global fs info
* @dir: directory inode from which we create a directory entry
* @name: name of the directory to create
* @mode: permission...
* @inode: inode to fill
*
* The function removes @dentry from the directory @dir.
* The function returns a negative error code if unsuccessful and zero
* otherwise.
*/
int fsop_mkdir(struct bilbyfs_info *bi, struct inode *dir, const char *name, umode_t mode, struct inode *inode);
/* fsop_readdir: Read a directory sequentially
* @bi: global fs info
* @inode: directory inode
* @ctx: context for FS use
* The function returns a negative error code if unsuccessful and zero
* otherwise.
*/
int fsop_readdir(struct bilbyfs_info *bi, struct inode *inode, struct dir_context *ctx, struct fsop_readdir_ctx **rdx);
void fsop_dir_release(struct fsop_readdir_ctx *rdx);
/* fsop_symlink: Create a symbolic link
* @bi: global fs info
* @dir: directory inode in which we create symlink
* @name: the name of the symlink
* @symname: path to which the symlink points
* @inode: inode to fill
*
* If the function is successful it creates a symlink inode on disk
* and fill in information about the symlink in @inode. The new symlink's
* path is put into a data object pointed to by the inode.
* The function returns a negative error code if unsuccessful and zero
* otherwise.
*/
int fsop_symlink(struct bilbyfs_info *bi, struct inode *dir, const char *name, const char *symname, struct inode *inode);
/* fsop_rename: Change the name and parent for an inode
* @bi: global fs info
* @old_dir: parent directory inode of the source inode
* @old_name: source inode to be renamed
* @old_inode: inode to which @old_dentry points
* @new_dir: parent directory inode for destination inode (can be the same as old_dir)
* @new_name: new name for the destination inode
* @new_inode: destination inode, can be null if destination inode is the same as source inode
*
* The function returns a negative error code if unsuccessful and zero
* otherwise.
*/
int fsop_rename(struct bilbyfs_info *bi, struct inode *old_dir, const char *old_name,
struct inode *old_inode, struct inode *new_dir, const char *new_name,
struct inode *new_inode);
/* fsop_readpage: Read a page (could be multiple blocks) from a file inode
* @bi: global fs info
* @inode: file inode we read a page from
* @block: block index in the file
* @addr: address to store read data to
*
* The function returns a negative error code if unsuccessful and zero
* otherwise.
*/
int fsop_readpage(struct bilbyfs_info *bi, struct inode *inode, pgoff_t block, void *addr);
/* fsop_write_begin: Read pages overlapping range of data for a future write
* @bi: global fs info
* @inode: file inode we read a page from
* @pos: position in file from which we want to write
* @len: number of bytes we want to write
* @addr: address to store read data to
*
* Storage devices only allow to write data by blocks (e.g. 4096 bytes), so
* this function is meant to read data that is needed to be able to update the
* file from offset @pos to @pos + @len.
* The function returns a negative error code if unsuccessful and zero
* otherwise.
*/
int fsop_write_begin(struct bilbyfs_info *bi, struct inode *inode, int pos, int len, void *addr);
/* fsop_write_end: Write file data to disk
* @bi: global fs info
* @inode: file inode we read a page from
* @pos: position in file from which we want to write
* @len: number of bytes we want to write
* @addr: address to store read data to
*
* This function writes back the actual @file's data stored in @addr to disk.
* The function returns a negative error code if unsuccessful and zero
* otherwise.
*/
int fsop_write_end(struct bilbyfs_info *bi, struct inode *inode, int pos, int len, void *addr);
/* fsop_follow_link: Read a symbolic link inode
* @bi: global fs info
* @inode: inode in which the path is stored
* @path: pointer where to store the path read
*
* The function returns a negative error code if unsuccessful and zero
* otherwise.
*/
int fsop_follow_link(struct bilbyfs_info *bi, struct inode *inode, char *path);
/* fsop_setattr: Set attributes of an inode (file/directory/symlink)
* @bi: global fs info
* @dentry: inode to modify
* @attr: attributes to modify and their new value
*
* This function also handles truncate, which is just an inode size
* modification.
* The function returns a negative error code if unsuccessful and zero
* otherwise.
*/
int fsop_setattr(struct bilbyfs_info *bi, struct inode *inode, struct iattr *attr);
/* fsop_getattr: Read inode attributes
* @bi: global fs info
* @inode: inode to read
* @stat: stat structure to fill
*
* The function returns a negative error code if unsuccessful and zero
* otherwise.
*/
int fsop_getattr(struct bilbyfs_info *bi, struct inode *inode, struct kstat *stat);
/* fsop_evict_inode: Inode cache eviction
* @bi: global fs info
* @inode: inode to evict
*
* If the OS caches inodes, fsop_evict_inode will be called when the OS decides
* to remove the inode from the cache.
* The function returns a negative error code if unsuccessful and zero
* otherwise.
*/
void fsop_evict_inode(struct bilbyfs_info *bi, struct inode *inode);
/* fsop_evict_inode: Get statistic about the FS
* @bi: global fs info
* @kstat: kstat structure to fill
*
* This function enables reporting the amount of free space available among
* other stats.
* The function returns a negative error code if unsuccessful and zero
* otherwise.
*/
int fsop_statfs(struct bilbyfs_info *bi, struct kstatfs *kstat);
int fsop_budget_data_update(struct bilbyfs_info *bi);
void fsop_budget_cancel_data_update(struct bilbyfs_info *bi);
/* fsop_sync_fs: Synchronise the FS to disk
* @bi: global fs info
* @sb: superblock data structure
* @wait: flag indicated whether the function is allowed to wait
*
* The function returns a negative error code if unsuccessful and zero
* otherwise.
*/
int fsop_sync_fs(struct bilbyfs_info *bi, struct super_block *sb, int wait);
/* fsop_test_is_mount: Check whether the FS is already mounted
* @bi1: global fs info
* @bi2: global fs info
*
* The function returns 0 if the FS is not already mounted, 1 otherwise.
*/
int fsop_test_is_mount(struct bilbyfs_info *bi1, struct bilbyfs_info *bi2);
/* fsop_fill_super: Read the super block and fills the structure
* @bi: global fs info
* @sb: super block to fill
* @silent: flag indicated whether the FS should print error messages or not
* @rootino: pointer to an inode number
*
* The function must read the super block and find the root inode number.
* If successful the function set the root inode number in @rootino and
* returns 0. A negative error code is returned in case of failure.
*/
int fsop_fill_super(struct bilbyfs_info *bi, struct super_block *sb, int silent, ino_t *rootino, struct inode *root);
/* fsop_init: Initialise the FS
* @bi: global fs info
* @name: mount command line device name
* @bd_name: backing device name to be filled
*
* The function allocates vital FS data structures and open the storage
* device. The @bd_name must be filled with the backing device name.
* The function returns a negative error code in case of failure, 0 otherwise.
*/
int fsop_init(struct bilbyfs_info *bi, const char *name, char *bd_name);
/* fsop_umount: unmount the fs
* @bi: global fs info
*
* this function must deallocate most data structures allocated in init.
*/
void fsop_unmount(struct bilbyfs_info *bi);
/* fsop_clean: Free the remaining data structures left allocated by unmount
* @bi: global fs info
*/
void fsop_clean(struct bilbyfs_info *bi);
/* Linux Helpers */
struct ubi_volume_desc *open_ubi(const char *name, int mode);
/*
* Object Store () component:
* Reading/Writing objects from their id, writing is transactional using the
* `trans' propertry stored in objects' header.
*/
/* ostore_init: Initialise Ostore component
* @bi: global fs info
*
*/
int ostore_init(struct bilbyfs_info *bi);
/* ostore_clean: Clean up Ostore component
* @bi: global fs info
*
*/
void ostore_clean(struct bilbyfs_info *bi);
/* ostore_get_obj_size: request the size of an object
* @bi: global fs info
* @id: id of the object
*
* This function returns the size of an object or a negative error code.
*/
int ostore_get_obj_size(struct bilbyfs_info *bi, obj_id id);
/* ostore_read_obj: read an obj using its id
* @bi: global fs info
* @id: id of the object
* @buf: buffer to store data in
* @len: size of the buffer
*
* It is possible to read an object before, after and while a transaction.
* ostore_read_obj will only return an object written once the transaction
* has been commited.
* Return non-zero value if it fails.
*/
int ostore_read_obj(struct bilbyfs_info *bi, obj_id id, void *buf, u32 len);
enum { OSW_NONE = 0,
OSW_DEL = 1,
OSW_GC = 2,
OSW_SYNC = 4,
};
/* ostore_write_obj_list: write objects in the list to disk
* @bi: global fs info
* @obj_list: an array of objects to write, the header must be complete and
* the object cannot be modified as its CRC has been calculated.
* @count: number of objects in the list
* @write_flag: OSW_NONE, OSW_DEL (Deletion), OSW_GC (Garbage Collection)
*
* This function will writes all the objects in the list into the disk
* in one single transaction.
*
* Note that this function will set the object command header
* according to their position in the transaction
*
* A successful execution is indicated by a return value of 0.
* A failure is indicated by a negative error-code.
*
*/
int ostore_write_obj_list(struct bilbyfs_info *bi, void **obj_list, int count, int write_flag);
int ostore_sync(struct bilbyfs_info *bi, int force_summary);
/* ostore_erase: Erase an erase-block
* @bi: global fs info
* @lnum: LEB number of the erase-block
*/
int ostore_erase(struct bilbyfs_info *bi, int lnum);
/* ostore_scan_obj: Scan an erase-block and build a list of objects in the block
* @bi: global fs info
* @rbuf: buffer used to store the objects
* @lnum: LEB number of the erase-block
* @list: list to store object addresses in the buffer
* @max_count: maximum number of objects the list can store
*
* This function return the number of object in the erase block
* It also return a negative error code if unsuccessful
*/
int ostore_scan_obj(struct bilbyfs_info *bi, struct bilbyfs_rbuf *rbuf, int lnum,
void **list, int max_count);
int ostore_scan_leb_obj(struct bilbyfs_info *bi, struct bilbyfs_rbuf *rbuf, int lnum,
void **list, int max_count);
/* ostore_next_obj_id: return the next id from an id
* @bi: global fs info
* @id: id of the object
*
* This function just calls tix_next_obj_id, see def for doc.
*/
obj_id ostore_next_obj_id(struct bilbyfs_info *bi, obj_id id);
/* ostore_mount: scan the media and initialises underlying components
* @bi: global fs info
*
* This function initialises fsm and tindex components by scanning
* and reading all objects present on the media.
*/
int ostore_mount(struct bilbyfs_info *bi);
/* ostore_unmount: free all memory allocated by trans.
* @bi: global fs info
*
* This function frees all the memory allocated by trans.
*/
void ostore_unmount(struct bilbyfs_info *bi);
/* ostore_get_free_space: returns how much free space available on the media
* @bi: global fs info
*/
unsigned long long ostore_get_free_space(struct bilbyfs_info *bi);
unsigned long long ostore_get_available_space(struct bilbyfs_info *bi);
/* ostore_write_super: update the superblock on-flash
* @bi: global fs info
*
* This function is used by mount() to store the initial super-block.
* The function ostore_unmount() also implicitly call it.
* The function returns a negative error-code in case of failure.
*/
int ostore_write_super(struct bilbyfs_info *bi);
/*
* Write Buffer component:
* Reading/Writing objects in a buffer that is then flushed by wbuf_commit().
*/
/* wbuf_init: initialise wbuf component
* @bi: global fs info
*/
int wbuf_init(struct bilbyfs_info *bi);
/* wbuf_clean: clean-up wbuf component
* @bi: global fs info
*/
void wbuf_clean(struct bilbyfs_info *bi);
/* wbuf_start: initialise bi->wbuf for a transaction
* @bi: global fs info
*/
void wbuf_start(struct bilbyfs_info *bi, struct bilbyfs_wbuf *wbuf);
/* wbuf_read_obj: read an obj from wbuf if in cache or from UBI otherwise.
* @bi: global fs info
* @buf: buffer to store data in
* @addr: object's address
*
*/
int wbuf_read_obj(struct bilbyfs_info *bi, void *buf, struct obj_addr *addr);
/* wbuf_write_obj: write an object in a group
* @bi: global fs info
* @buf: buffer where to get data to write.
* @wbuf: write-buffer to use
* This function returns 0 if the object has been added to the transaction.
*/
int wbuf_write_obj(struct bilbyfs_info *bi, void *buf, int len, struct bilbyfs_wbuf *wbuf);
/* wbuf_prepare_commit: prepare (padding) the wbuf for a commit
* @bi: global fs info
* @paading_sz: when function return, amount of padding will be stored
* if set to NULL, no value will be stored
* @wbuf: write-buffer to use
*
* This function never fails, it always returns the size of the transaction
* including padding in bytes.
*/
int wbuf_prepare_commit(struct bilbyfs_info *bi, u32 *padding_sz, struct bilbyfs_wbuf *wbuf);
/* wbuf_commit: commit the buffer on-flash
* @bi: global fs info
* @lnum: LEB to write to
* @wbuf: write-buffer to use
*
* The function either succeed or fail, if the returned value is greater than
* 0, it means that the entire transaction has been successfully flushed
* on-flash and the return value is the number of bytes written to the LEB.
* In case of failure the function returns a negative error-code.
*
*/
int wbuf_commit(struct bilbyfs_info *bi, u32 lnum, struct bilbyfs_wbuf *wbuf);
/* wbuf_atom_leb_commit: update an LEB on flash atomically
* @bi: global fs info
* @lnum: LEB to update
* @wbuf: write-buffer to use
*
* This function overwrites @leb regardless of the current status of LEB,
* it enssentially unmap the leb and map to an empty one to write the data.
* This operation is quite slow and should only be used when strictly
* necessary.
*
* In case of failure the function returns a negative error-code.
*
*/
int wbuf_atom_leb_commit(struct bilbyfs_info *bi, int lnum, struct bilbyfs_wbuf *wbuf);
/* wbuf_read_leb: Reads an entire LEB in memory
* @bi: global fs info
* @lnum: LEB number to read
* @rbuf: read-buffer where to store the data read
*
* This function returns a negative error-code if unsuccessful.
*/
int wbuf_read_leb(struct bilbyfs_info *bi, int lnum, struct bilbyfs_rbuf *rbuf);
int wbuf_read_leb_fast(struct bilbyfs_info *bi, int lnum, struct bilbyfs_rbuf *rbuf);
/* wbuf_read_sum: Reads pages covering the summary object and returns offset in
* @sum_offs_ret
* @bi: global fs info
* @lnum: LEB number to read
* @rbuf: read-buffer where to store the data read
* @sum_offs_ret: pointer used to store the offset of the summary object.
*
* This function returns a negative error-code if unsuccessful.
*/
int wbuf_read_sum(struct bilbyfs_info *bi, int lnum, struct bilbyfs_rbuf *rbuf, u32 *sum_offs_ret);
/* wbuf_erase: Erase an earse-block
* @bi: global fs info
* @lnum: LEB number of the erase-block
*/
int wbuf_erase(struct bilbyfs_info *bi, int lnum);
/* wbuf_next_obj_addr: Find the next object in a read-buffer
* @bi: global fs info
* @addr: address to start from
* @rbuf: read-buffer to analyse
*
* Read the next object present in a LEB starting from
* addr->offs + addr->len in LEB addr->lnum.
*
* If there isn't any object present in the LEB the function
* returns %-ENOENT. In this case addr is unchanged.
*
* If addr->len = 0, as the function cannot find the next object,
* it will return the object at addr->offs if one exists.
*
* This function is needed to implement the mount operation and the
* transaction commit operation.
* We use it to perform the initial media scanning to populate the
* components fsm and index.
* We also use it to update the index & fsm components when the
* transaction in progress has been committed on-flash.
*
* This function returns a pointer to the next object if successful and
* writes the address of the next object stored on the media in @addr.
* If unsuccessful it returns %-ENOENT as mentionned above.
*/
void *wbuf_next_obj_addr(struct bilbyfs_info *bi, struct obj_addr *addr,
struct bilbyfs_rbuf *rbuf);
/*
* Object pool component: Preallocates tree nodes to protect against
* memory allocation errors in critical section of the FS where not
* updating an in-memory data structure would lead to an inconsistency
* between the in-mem state and on-disk state.
*/
int allocpool_init(struct alloc_pool *pool);
void allocpool_destroy(struct alloc_pool *pool);
int allocpool_alloc(struct alloc_pool *pool, int sz_pool, int sz_obj);
void allocpool_empty(struct alloc_pool *pool);
void *allocpool_pop(struct alloc_pool *pool);
void allocpool_free_single(void *p);
/*
* Free Space Management (fsm) component:
* Find logical erase-blocks with enough free space for wbuf's purpose.
*/
/* fsm_init: initialise the fsm component
* @bi: global fs info
*/
int fsm_init(struct bilbyfs_info *bi);
/* fsm_clean: clean-up the fsm component
* @bi: global fs info
*/
void fsm_clean(struct bilbyfs_info *bi);
/* fsm_alloc_eb: find a leb with @req_size free space to write objects.
* @bi: global fs info
* @osw_flag: Flags helping choosing erase-blocks to allocate
*
* This function returns a leb number >0 if successful or a negative error code.
*/
int fsm_alloc_eb(struct bilbyfs_info *bi, int osw_flag);
/* fsm_get_lnum: return current leb allocated
* @bi: global fs info
*/
u32 fsm_get_lnum(struct bilbyfs_info *bi);
/* fsm_mark_dirty: mark the address of an object as dirty space for garbage
* collection.
* @bi: global fs info
* @addr: address of the dirty object
*
*/
void fsm_mark_dirty(struct bilbyfs_info *bi, struct obj_addr *addr);
/* fsm_mark_used: mark a leb as used
* @bi: global fs info
* @lnum: which LEB
*/
void fsm_mark_used(struct bilbyfs_info *bi, int lnum);
/* fsm_mark_erased: Mark the erase-block as empty
* @bi: global fs info
* @lnum: LEB number of the erase-block that has been erased
*/
void fsm_mark_erased(struct bilbyfs_info *bi, int lnum);
/* fsm_get_free_space: returns how much free space available on the media
* @bi: global fs info
*/
unsigned long long fsm_get_free_space(struct bilbyfs_info *bi);
unsigned long long fsm_get_available_space(struct bilbyfs_info *bi);
/* fsm_get_dirtiest_eb: get erase-block with most garbage
* @bi: global fs info
*
* This function return erase-block logical number if the dirtiest
* erase-block is found.
* It returns -1 if no block is found
*/
int fsm_get_dirtiest_eb(struct bilbyfs_info *bi);
/* Red-Black tree component
*/
#define rbt_parent(r) ((struct rbt_node *)((r)->rbt_parent_color & ~3))
#define rbt_color(r) ((r)->rbt_parent_color & 1)
#define rbt_is_red(r) (!rbt_color(r))
#define rbt_is_black(r) rbt_color(r)
#define rbt_set_red(r) do { (r)->rbt_parent_color &= ~1; } while (0)
#define rbt_set_black(r) do { (r)->rbt_parent_color |= 1; } while (0)
static inline void rbt_set_parent(struct rbt_node *rb, struct rbt_node *p)
{
rb->rbt_parent_color = (rb->rbt_parent_color & 3) | (unsigned long)p;
}
static inline void rbt_set_color(struct rbt_node *rb, int color)
{
rb->rbt_parent_color = (rb->rbt_parent_color & ~1) | color;
}
#define RBT_ROOT (struct rbt_root) { NULL, }
#define rbt_entry(ptr, type, member) container_of(ptr, type, member)
#define RBT_EMPTY_ROOT(root) ((root)->rbt_node == NULL)
#define RBT_EMPTY_NODE(node) (rbt_parent(node) == node)
#define RBT_CLEAR_NODE(node) (rbt_set_parent(node, node))
void rbt_insert_color(struct rbt_node *, struct rbt_root *);
struct rbt_node *rbt_first(const struct rbt_root *root);
void rbt_erase(struct rbt_node *, struct rbt_root *);
void rbt_link_node(struct rbt_node * node, struct rbt_node * parent,
struct rbt_node ** rbt_link);
/*
* Index functions:
* The index caches the address of an object to avoid scanning the entire
* disk to find the object.
*/
/* idx_init: Initialise Index component
* @bi: global fs info
*
*/
int idx_init(struct bilbyfs_info *bi);
/* idx_clean: Clean up Index component
* @bi: global fs info
*
*/
void idx_clean(struct bilbyfs_info *bi);
/* idx_get_obj_addr: retrieve the address of an object from the index.
* @bi: global fs info
* @id: id of the object
* @addr: obj_addr struct to store the object's address in.
*
* This function returns 0 if the object was found in the index and addr is set.
* A negative error code is return otherwise, including -ENOENT if the object
* wasn't found.
*/
int idx_get_obj_addr(struct bilbyfs_info *bi, obj_id id, struct obj_addr *addr);
struct idx_node *idx_get_or_create_obj_addr_node(struct bilbyfs_info *bi, obj_id id);
/* idx_set_obj_addr: sets the address of an object designated by @id
* @id: id of the object
* @addr: address to store for the id
* @node: preallocated node that is freed by the function if unused.
*
* This function always returns 0.
*/
int idx_set_obj_addr(struct bilbyfs_info *bi, obj_id id, struct obj_addr *addr, struct idx_node *node);
/* idx_next_obj_id: returns the next id stored in the index.
* An id can be interpreted as a u64 and therefore each object is ordered
* using this interpretation.
* If there is no id greater than @id stored in the index, the function
* returns ~0
* @bi: global fs info
* @id: id of the object
*/
obj_id idx_next_obj_id(struct bilbyfs_info *bi, obj_id id);
/* idx_del_obj_addr: delete an object address from the index.
* @bi: global fs info
* @id: id of the object
*
* The function returns the removed node.
* The function cannot be called if the node doesn't exists, doing so
* will break an assertion check.
*/
void *idx_del_obj_addr(struct bilbyfs_info *bi, obj_id id);
/*
* DentArr component: Ideally we would store directory entry directly in the
* index and retrieve entries as we need. In practice, object IDs are limited
* in size (64 bits) and we cannot store the entire name attached to an
* directory entry in the object ID. Accessing a directory entry by name is
* required for the ->lookup() operation to be efficient.
* Therefore we only store a hash of the name in object IDs and we deal with
* collisions by storing arrays of directory entry (dentarr) in the index.
*/
/* dentarr_add_dentry: adds a directory entry in a dentarr
* @bi: global fs info
* @dentarr: dentarr where to add the directory entry
* @inode: inode to initialise the dentry with
* @name: name to initialise the dentry with
*
* The function can return -ENAMETOOLONG if the maximum number of collision for
* a name in a directory has been reached.
* Otherwise the function returns the size in bytes of the directory entry
* freshly added.
*/
int dentarr_add_dentry(struct bilbyfs_info *bi, struct obj_dentarr *dentarr,
struct inode *inode, const char *name);
/* dentarr_del_dentry: deletes a directory entry from a dentarr
* @bi: global fs info
* @dentarr: dentarr where to add the directory entry
* @name: name to delete
*
* This function can return -ENOMEM as error, when successul it returns
* the number of bytes removed from the dentarr.
* If the name was not found in the dentarr, the function returns 0.
*/
int dentarr_del_dentry(struct bilbyfs_info *bi, struct obj_dentarr *dentarr,
const char *name);
/* dentarr_check_empty: checks whether a dentarr is empty
* @bi: global fs info
* @dentarr: dentarr to examine
*
* This function return -ENOTEMPTY if the dentarr is not empty otherwise 0 is
* returned.
*/
int dentarr_check_empty(struct bilbyfs_info *bi, struct obj_dentarr *dentarr);
/* dentarr_lookup_nm: lookup a name in a `directory entry array'
* @bi: global fs info
* @darr: directory entry array object
* @name: name to lookup
*
* The function returns a pointer to the obj_dentry if the entry was found,
* %-ENOENT if the name was not found and a negative error code otherwise.
*/
struct obj_dentry *dentarr_lookup_nm(struct bilbyfs_info *bi,
struct obj_dentarr *dentarr,
const char *name);
struct obj_dentry *dentarr_first_dentry(struct obj_dentarr *dentarr);
struct obj_dentry *dentarr_next_dentry(struct obj_dentarr *dentarr,
struct obj_dentry *dentry);
/* dentarr_read: allocate and read a dentarr
* @bi: global fs info
* @id: id of the object to read
*
* This function returns a negative error code if the object
* does not exist.
*/
struct obj_dentarr *dentarr_read(struct bilbyfs_info *bi, obj_id id);
/* dentarr_read_or_create: read a dentarr or create one if it doesn't exist.
* @bi: global fs info
* @id: id of the object to read
*
* If the function creates a new dentarr, it allocates the memory space for
* a dentry.
* This function returns a negative error code if an error happens.
*/
struct obj_dentarr *dentarr_read_or_create(struct bilbyfs_info *bi, obj_id id);
/* PackObj component:
*
* stateless component that prepares objects to be written on the med$a.
*/
/**
* pack_obj_header - prepare object header to be written to flash.
* @obj: object pointer
* @sqnum: sequence number to store in the object.
* @trans_pos: position in transaction (%BILBYFS_TRANS_ST, ...)
*
* This function prepares an object headeer - it calculates the CRC and
* fills the common header.
*/
void pack_obj_header(void *obj, u64 sqnum, u8 trans_pos);
/**
* check_obj_header - check whether the pointer passed is an object header
* @buf: object pointer
* @max_obj_sz: maximum possible valid size of this object.
*
* The maximum possible object size must be calculated from the object offset
* in a LEB (an object cannot spread over multiple LEBs).
* It returns 0 if the pointer points to an object.
*/
int check_obj_header(void *obj, int max_obj_sz);
/**
* pack_obj_inode - pack an inode object.
* @ino: obj where to store the inode
* @inode: inode to pack
*/
void pack_obj_inode(struct obj_inode *ino, struct inode *inode);
/**
* pack_obj_dentarr - pack a dentarr object.
* @dentarr: obj dentarr to pack
* @id: id of the object to pack
* @nbdentry: number of dentry embedded in the dentarr
* @size: size in bytes of the dentarr object
*/
void pack_obj_dentarr(struct obj_dentarr *dentarr, obj_id id,
int nbdentry, int size);
/**
* pack_obj_data - pack a data object.
* @odata: obj data to pack
* @id: id of the object to pack
* @sz_data: size of the data to store in the obj_data
* @data: pointer to the data to store in the obj_data
*/
void pack_obj_data(struct obj_data *odata, obj_id id, int sz_data,
const void *data);
/**
* pack_obj_dentry - pack a dentry object.
* @de: obj dentry to pack
* @inode: inode the dentry is linked with
* @name: name to store in the dentry
*/
void pack_obj_dentry(struct obj_dentry *de, struct inode *inode, const char *name);
/**
* pack_obj_pad - pad a padding object.
* @pad: padding object to pack
* @pad_sz: padding size;
*/
void pack_obj_pad(struct obj_ch *pad, int pad_sz);
/**
* unpack_obj_inode - unpack an inode object.
* @inode: inode where to store the object inode
* @ino: obj inode to unpack
*/
void unpack_obj_inode(struct inode *inode, struct obj_inode *ino);
/**
* pack_obj_super - pack a super object.
* @super: obj super to unpack
* @bsuper: super object memory representation
*/
void pack_obj_super(struct obj_super *super, const struct bilbyfs_super *bsuper);
/**
* unpack_obj_super - unpack a super object.
* @bsuper: super in memory representation where to store the obj_super
* @super: obj super to unpack
*/
void unpack_obj_super(struct bilbyfs_super *bsuper, struct obj_super *super);
/**
* pack_obj_del - pack a deletion object
* @obj: potentially a deletion object, but other objects can be casted to
* deletion object as well
* @id: object id of the object to be deleted
*/
void pack_obj_del(void *obj, obj_id id);
/**
* pack_obj_sum - pack a summary object
* @sum: summary object to pack
* @offs: erase-block offset to at which the summary object itself lives.
*/
void pack_obj_sum(struct obj_sum *sum, u32 offs);
/**
* GIM - garbage information manager
* GIM manages the all information about garbage on disk.
* It maintains dirty list which specify how much garbage each erase block has.
* It also keep a garbage object list
*/
/* gim_init: Initialise GIM component
* @bi: global fs info
*/
int gim_init(struct bilbyfs_info *bi);
/* gim_clean: clean up GIM component
* @bi: global fs info
*/
void gim_clean(struct bilbyfs_info *bi);
/* gim_mark_garbage: mark an object as garbage
* @bi: global fs info
* @id: object id
* @addr: object address containing sequence number
* @node: preallocated node that is freed by the function if not used.
*
* This function return negative error code upon failure
*/
int gim_mark_garbage(struct bilbyfs_info *bi, obj_id id, struct obj_addr *addr, struct gim_node *node);
int gim_mark_garbage_cnt(struct bilbyfs_info *bi, obj_id id, struct obj_addr *addr, u32 count, struct gim_node *allocated_node);
/* gim_is_removable: determine if an object can be garbage collected
* @bi: global fs info
* @obj: object to be tested
*
* This function return a boolean indicate whether the garbage can be removed
* from disk or not
*/
int gim_is_removable(struct bilbyfs_info *bi, struct obj_ch *obj);
/* gim_garbage_collected: mark as object has been garbage collected
* @bi: global fs info
* @obj: object that have been garbage collected
*
* This function return negative error code upon failure
*/
int gim_garbage_collected(struct bilbyfs_info *bi, struct obj_ch *obj);
/**
* Garbage Collector
*/
/* gc_init: Initialise the garbage collector
* @bi: global fs info
*
* The function returns a negative error code in case of failure, 0 otherwise
*/
int gc_init(struct bilbyfs_info *bi);
/* gc_clean: Clean up GC component
* @bi: global fs info
*/
void gc_clean(struct bilbyfs_info *bi);
/* gc_garbage_collect: Perform garbage collection
* @bi: global fs info
*/
int gc_garbage_collect(struct bilbyfs_info *bi);
/* Debug functions */
void dump_sum_entry( struct obj_sum_entry *entry);
void dump_obj_addr( struct obj_addr *addr);
#endif /* !__BILBYFS_H__ */