node99.org | cv

collecting filesystem statistics.

Homework 3
Due November 30th via handin

Cscope

In this assignment you'll be modifying several system calls, so it will be useful to know where these calls are defined. A handy program for navigating large coding projects is Cscope, which builds a database of your code and allows you to intelligently search it. I'll describe how to use Cscope in case you are interested -- it is completely optional, and takes some practice to use. Another option is an online cross reference, such as the one at linux.no.

First you'll need to install it:

sudo apt-get install cscope

Then you'll need to tell it to build a database (this may take a while):

cd linux
cscope -b -q -k -R

Finally, to invoke cscope, make sure you're in your linux source code directory and run:

cscope -d

You'll need to be in your linux source directory every time you run it, since that's where it will build its symbol database. You can experiment with different command line options to store the database elsewhere.

Cscope is a text-based program that divides the window into 2 panes. The top pane contains search results, and the bottom pane allows you to perform different types of searches. You can navigate between panes using the tab key, and between items within a pane using the arrow keys. The bottom pane looks something like this:
  Find this C symbol:
  Find this global definition:
  Find functions called by this function:
  Find functions calling this function:
  Find this text string:
  Change this text string:
  Find this egrep pattern:
  Find this file:
  Find files #including this file:

It takes some getting used to, but try this: open Cscope, use the down arrow to select "global definition", then type in sys_read and hit enter. Cscope will open up your editor at the line where sys_read is defined. By default your editor is vi, so you can use :q to return to Cscope. When there are multiple search results, you will move to the top pane and can select a result with the arrow keys.

To quit Cscope, press control-d.

Syscall Changes

Inside a variety of system calls, you'll be checking if a flag is set (similar to PA2), and if so then using printk to output statistics to the system log. The assignment sheet tells you what to print out for each call.

Some information is available directly from the system call parameters, and some must be obtained using additional functions to obtain pointers to kernel data structures. As a starting point, you'll want to be familiar with the file structure, given below.

linux/include/linux/fs.h:

struct file {
	/*
	 * fu_list becomes invalid after file_free is called and queued via
	 * fu_rcuhead for RCU freeing
	 */
	union {
		struct list_head	fu_list;
		struct rcu_head 	fu_rcuhead;
	} f_u;
	struct path		f_path;
#define f_dentry	f_path.dentry
#define f_vfsmnt	f_path.mnt
	const struct file_operations	*f_op;
	atomic_t		f_count;
	unsigned int 		f_flags;
	mode_t			f_mode;
	loff_t			f_pos;
	struct fown_struct	f_owner;
	unsigned int		f_uid, f_gid;
	struct file_ra_state	f_ra;

	unsigned long		f_version;
#ifdef CONFIG_SECURITY
	void			*f_security;
#endif
	/* needed for tty driver, and maybe others */
	void			*private_data;

#ifdef CONFIG_EPOLL
	/* Used by fs/eventpoll.c to link all the hooks to this file */
	struct list_head	f_ep_links;
	spinlock_t		f_ep_lock;
#endif /* #ifdef CONFIG_EPOLL */
	struct address_space	*f_mapping;
};

You'll need to figure out how to grab the inode number associated with a file structure. There's a chain of pointers that will lead you there. I would start with file->f_dentry, and follow the chain until you can access the file's inode->i_ino. You can verify the inode by using the -i flag of the ls command, shown below with abbreviated dmesg output.

# dmesg
[   74.457862] open path urandom inode 3293 flags 0
# ls -i /dev/urandom
3293 /dev/urandom

Related Structures

linux/include/linux/namei.h:

struct path {
	struct vfsmount *mnt;
	struct dentry *dentry;
};

linux/include/linux/dcache.h:

struct dentry {
	atomic_t d_count;
	unsigned int d_flags;		/* protected by d_lock */
	spinlock_t d_lock;		/* per dentry lock */
	struct inode *d_inode;		/* Where the name belongs to - NULL is
					 * negative */
	/*
	 * The next three fields are touched by __d_lookup.  Place them here
	 * so they all fit in a cache line.
	 */
	struct hlist_node d_hash;	/* lookup hash list */
	struct dentry *d_parent;	/* parent directory */
	struct qstr d_name;

	struct list_head d_lru;		/* LRU list */
	/*
	 * d_child and d_rcu can share memory
	 */
	union {
		struct list_head d_child;	/* child of parent list */
	 	struct rcu_head d_rcu;
	} d_u;
	struct list_head d_subdirs;	/* our children */
	struct list_head d_alias;	/* inode alias list */
	unsigned long d_time;		/* used by d_revalidate */
	struct dentry_operations *d_op;
	struct super_block *d_sb;	/* The root of the dentry tree */
	void *d_fsdata;			/* fs-specific data */
#ifdef CONFIG_PROFILING
	struct dcookie_struct *d_cookie; /* cookie, if any */
#endif
	int d_mounted;
	unsigned char d_iname[DNAME_INLINE_LEN_MIN];	/* small names */
};

struct qstr {
	unsigned int hash;
	unsigned int len;
	const unsigned char *name;
};

linux/include/linux/fs.h:

struct inode {
	struct hlist_node	i_hash;
	struct list_head	i_list;
	struct list_head	i_sb_list;
	struct list_head	i_dentry;
	unsigned long		i_ino;
	atomic_t		i_count;
	unsigned int		i_nlink;
	uid_t			i_uid;
	gid_t			i_gid;
	dev_t			i_rdev;
	unsigned long		i_version;
	loff_t			i_size;
#ifdef __NEED_I_SIZE_ORDERED
	seqcount_t		i_size_seqcount;
#endif
	struct timespec		i_atime;
	struct timespec		i_mtime;
	struct timespec		i_ctime;
	unsigned int		i_blkbits;
	blkcnt_t		i_blocks;
	unsigned short          i_bytes;
	umode_t			i_mode;
	spinlock_t		i_lock;	/* i_blocks, i_bytes, maybe i_size */
	struct mutex		i_mutex;
	struct rw_semaphore	i_alloc_sem;
	struct inode_operations	*i_op;
	const struct file_operations	*i_fop;	/* former ->i_op->default_file_ops */
	struct super_block	*i_sb;
	struct file_lock	*i_flock;
	struct address_space	*i_mapping;
	struct address_space	i_data;
#ifdef CONFIG_QUOTA
	struct dquot		*i_dquot[MAXQUOTAS];
#endif
	struct list_head	i_devices;
	union {
		struct pipe_inode_info	*i_pipe;
		struct block_device	*i_bdev;
		struct cdev		*i_cdev;
	};
	int			i_cindex;

	__u32			i_generation;

#ifdef CONFIG_DNOTIFY
	unsigned long		i_dnotify_mask; /* Directory notify events */
	struct dnotify_struct	*i_dnotify; /* for directory notifications */
#endif

#ifdef CONFIG_INOTIFY
	struct list_head	inotify_watches; /* watches on this inode */
	struct mutex		inotify_mutex;	/* protects the watches list */
#endif

	unsigned long		i_state;
	unsigned long		dirtied_when;	/* jiffies of first dirtying */

	unsigned int		i_flags;

	atomic_t		i_writecount;
#ifdef CONFIG_SECURITY
	void			*i_security;
#endif
	void			*i_private; /* fs or device private pointer */
};

Other Hints

You may find the do_gettimeofday function useful. It takes a struct timeval pointer as a parameter. Afterwards, the struct will contain the number of seconds since the Unix epoch (Jan. 1 1970) in the tv_sec variable.