* look at struct inode, cont. in include/linux/fs.h - main header file for the VFS OSs use basic old data structures like link lists and hash tables, rare to find even a red-black tree or B-tree. describes linked list ops: just add "struct list_head" to any struct foo, to create a linked list of "struct foo" objects. struct hlist_node i_hash; hlist is hash table (buckets of linked lists): useful for fast lookups by some unique key (perhaps inode->i_num?) more on h/list when we review struct dentry struct list_head i_io_list; /* backing dev IO list */ list of any inodes w/ pending I/O (perhaps waiting to read data) struct list_head i_lru; /* inode LRU list */ rough LRU order of inodes within this superblock (a single mount) struct list_head i_sb_list; all inodes of this single mount unordered struct list_head i_wb_list; /* backing dev writeback list */ list of inodes that are dirty, but waiting for dirty data to flush (write) back union { struct hlist_head i_dentry; struct rcu_head i_rcu; }; struct list_head i_devices; atomic64_t i_version; sometimes called the "generation" of the object. Each time same inode struct ptr in kernel mem is re-used, inc i_version++. Used to record the generation of an object in a long running system (OS): each time you free and reuse the same inode ptr, you inc version by 1, so users holding the object can tell WHICH generation of the object they have. void *i_private; /* fs or device private pointer */ a void* can be used to store ANYTHING, if you wanted. This is principle of "extensibility". In 2.4 kernels, inode struct ended with a large union of "struct FSNAME_inode_info FSNAME_i", etc. Cons: - memory wasted: union size is size of largest member, others may not need as much space. - poor maintainability: have to update union each time you add a new f/s. Pros: - good mem locality of generic vfs inode info and f/s-specific inode info (helps CPU cache locality). Solution 1: - use void* ptr: no mem wasted, easy to maintain, but you lose cache locality! Solution 2: like #1, but also get good locality. - use a "container" or an out-of-band data structure - in 2.4: vfs allocated inodes, now actual f/s has to alloc inodes ->i_private still left behind, just in case someone doesn't want to use a container: rules, the same "user" who puts something into i_private, must also remove it. * struct inode_operations a struct with ptrs to functions that operate on inodes many look like regular syscalls int (*unlink) (struct inode *,struct dentry *); takes an inode and dentry, returns an int. returns 0 on success, -errno on failure. input inode: - inode of parent directory, need to modify this object - inode comes locked already (by VFS) input dentry: - the name of the object we want the f/s to remove. - assume gets a positive dentry; on success, dentry turns negative. - VFS always passes positive dentries (else VFS returns ENOENT to caller), saves work from actual f/s. Next time: rename, and other ops. ////////////////////////////////////////////////////////////////////////////// // allocate an inode "container" for file system "foo" // vfs calls each specific f/s to alloc its own inode + the vfs "struct inode" struct inode *foofs_alloc_inode() { struct container *ptr; ptr = kmalloc(sizeof(struct foofs_inode_info) + sizeof(struct inode)); return &ptr[sizeof(struct foofs_inode_info)]; } // vfs passes inode to some op int foofs_some_op(struct inode *ip) { struct foofs_inode_info *private_ip; private_ip = ip - sizeof(struct foofs_inode_info); } // examples of other OOB data structures struct bar { int i; float f; char name[0]; // sometimes buf[1] };