/* ***************************************************************** * (C) Copyright 2007 Benjamin Krill * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, * MA 02110-1301 USA * *****************************************************************/ #include #include #include #include #include #include #include "fpgafs.h" static struct inode *fpgafs_alloc_inode(struct super_block *sb); static void fpgafs_destroy_inode(struct inode *inode); static void fpgafs_delete_inode(struct inode *inode); static struct inode *fpgafs_new_inode(struct super_block *sb, int mode); static void fpgafs_prune_dir(struct dentry *dir); int fpgafs_mkdir(struct inode *dir, struct dentry *dentry, int mode); static int fpgafs_rmdir(struct inode *dir, struct dentry *dentry); static int fpgafs_new_file(struct super_block *sb, struct dentry *dentry, const struct file_operations *fops, int mode, struct fpga_context *ctx); static int fpgafs_setattr(struct dentry *dentry, struct iattr *attr); const struct file_operations fpgafs_context_foperations = { .open = dcache_dir_open, //.release = fpgafs_dir_close, .llseek = dcache_dir_lseek, .read = generic_read_dir, .readdir = dcache_readdir, .fsync = simple_sync_file, }; const struct inode_operations fpgafs_simple_dir_inode_operations = { .lookup = simple_lookup }; const struct inode_operations fpgafs_dir_inode_operations = { .lookup = simple_lookup, .mkdir = fpgafs_mkdir, .rmdir = fpgafs_rmdir }; static struct kmem_cache *fpgafs_inode_cache; /* basic inode operations */ static struct inode *fpgafs_alloc_inode(struct super_block *sb) { struct fpgafs_inode_info *fsi; fsi = kmem_cache_alloc(fpgafs_inode_cache, GFP_KERNEL); if (!fsi) return NULL; fsi->i_ctx = NULL; return &fsi->vfs_inode; } static void fpgafs_destroy_inode(struct inode *inode) { kmem_cache_free(fpgafs_inode_cache, FPGAFS_I(inode)); } static void fpgafs_delete_inode(struct inode *inode) { //struct fpgafs_inode_info *fsi = FPGAFS_I(inode); // XXX: do some fpga interface operations.... // if (fsi->i_ctx) // put_fpga_context(ei->i_ctx); clear_inode(inode); } static struct inode *fpgafs_new_inode(struct super_block *sb, int mode) { struct inode *inode; inode = new_inode(sb); if (!inode) return inode; inode->i_mode = mode; inode->i_uid = current->fsuid; inode->i_gid = current->fsgid; inode->i_blocks = 0; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; return inode; } /* FPGA FS specific functions */ static int fpgafs_fill_dir(struct dentry *dir, struct tree_descr *files, int mode, struct fpga_context *ctx) { struct dentry *dentry; int ret; while (files->name && files->name[0]) { ret = -ENOMEM; dentry = d_alloc_name(dir, files->name); if (!dentry) goto out; ret = fpgafs_new_file(dir->d_sb, dentry, files->ops, files->mode & mode, ctx); if (ret) goto out; files++; } return 0; out: fpgafs_prune_dir(dir); return ret; } /* specific operations */ static int fpgafs_setattr(struct dentry *dentry, struct iattr *attr) { struct inode *inode = dentry->d_inode; if ((attr->ia_valid & ATTR_SIZE) && (attr->ia_size != inode->i_size)) return -EINVAL; return inode_setattr(inode, attr); } static int fpgafs_new_file(struct super_block *sb, struct dentry *dentry, const struct file_operations *fops, int mode, struct fpga_context *ctx) { static struct inode_operations fpgafs_file_iops = { .setattr = fpgafs_setattr, }; struct inode *inode; int ret; ret = -ENOSPC; inode = fpgafs_new_inode(sb, S_IFREG | mode); if (!inode) goto out; ret = 0; inode->i_op = &fpgafs_file_iops; inode->i_fop = fops; inode->i_private = FPGAFS_I(inode)->i_ctx = ctx; d_add(dentry, inode); out: return ret; } int fpgafs_mkdir( struct inode *dir, struct dentry *dentry, int mode) { int ret; struct inode *inode; struct fpga_context *ctx = NULL; ret = -ENOSPC; inode = fpgafs_new_inode(dir->i_sb, mode | S_IFDIR); if (!inode) return ret; mutex_lock(&inode->i_mutex); if (dir->i_mode & S_ISGID) { inode->i_gid = dir->i_gid; inode->i_mode &= S_ISGID; } ctx = alloc_fpga_context(); FPGAFS_I(inode)->i_ctx = ctx; if (!ctx) goto unmutex; inode->i_op = &fpgafs_simple_dir_inode_operations; inode->i_fop = &simple_dir_operations; ret = fpgafs_fill_dir(dentry, fpgafs_dir_contents, mode, ctx); ret = 0; //if (ret) { // FREEE CONTENT // goto unmutex; //} d_instantiate(dentry, inode); dget(dentry); dir->i_nlink++; dentry->d_inode->i_nlink++; unmutex: mutex_unlock(&inode->i_mutex); return ret; } static void fpgafs_prune_dir(struct dentry *dir) { struct dentry *dentry, *tmp; //mutex_lock(&dir->d_inode->i_mutex); list_for_each_entry_safe(dentry, tmp, &dir->d_subdirs, d_u.d_child) { spin_lock(&dcache_lock); spin_lock(&dentry->d_lock); if (!(d_unhashed(dentry)) && dentry->d_inode) { dget_locked(dentry); __d_drop(dentry); spin_unlock(&dentry->d_lock); simple_unlink(dir->d_inode, dentry); spin_unlock(&dcache_lock); dput(dentry); } else { spin_unlock(&dentry->d_lock); spin_unlock(&dcache_lock); } } shrink_dcache_parent(dir); //mutex_unlock(&dir->d_inode->i_mutex); } static int fpgafs_rmdir(struct inode *dir, struct dentry *dentry) { /* remove all entries */ free_fpga_context(FPGAFS_I(dentry->d_inode)->i_ctx); fpgafs_prune_dir(dentry); //fpgafs_remove_inode return simple_rmdir(dir, dentry); } /* SUPERBLOCK operations */ int fpgafs_fill_sb(struct super_block *sb, void *data, int silent) { struct inode *inode; int ret = -ENOMEM; static struct super_operations fpgafs_ops = { //.read_inode = fpgafs_read_inode, //.put_super = fpgafs_put_super, .alloc_inode = fpgafs_alloc_inode, .destroy_inode = fpgafs_destroy_inode, .statfs = simple_statfs, .delete_inode = fpgafs_delete_inode, .drop_inode = generic_delete_inode, }; sb->s_maxbytes = MAX_LFS_FILESIZE; sb->s_blocksize = PAGE_CACHE_SIZE; sb->s_blocksize_bits = PAGE_CACHE_SHIFT; sb->s_magic = FPGAFS_MAGIC; sb->s_op = &fpgafs_ops; inode = fpgafs_new_inode(sb, S_IFDIR | 0775); if (!inode) return ret; inode->i_op = &fpgafs_dir_inode_operations; inode->i_fop = &simple_dir_operations; FPGAFS_I(inode)->i_ctx = NULL; sb->s_root = d_alloc_root(inode); if (!sb->s_root) return ret; return 0; } static int fpgafs_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data, struct vfsmount *mnt) { return get_sb_nodev(fs_type, flags, data, fpgafs_fill_sb, mnt); } static struct file_system_type fpgafs_type = { .owner = THIS_MODULE, .name = "fpgafs", .get_sb = fpgafs_get_sb, .kill_sb = kill_anon_super }; static void fpgafs_init_once(void *p) { struct fpgafs_inode_info *fsi = p; inode_init_once(&fsi->vfs_inode); } /* init exit functions ... */ int __init fpgafs_init(void) { fpgafs_inode_cache = kmem_cache_create("fpgafs_inode_cache", sizeof(struct fpgafs_inode_info), 0, SLAB_HWCACHE_ALIGN, (void *)&fpgafs_init_once); if(!fpgafs_inode_cache) return -ENOMEM; register_filesystem(&fpgafs_type); printk("fpgafs: Benjamin Krill \n"); printk("fpgafs: successfull loaded ...\n"); return 0; } void __exit fpgafs_exit(void) { kmem_cache_destroy(fpgafs_inode_cache); unregister_filesystem(&fpgafs_type); } module_init(fpgafs_init); module_exit(fpgafs_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Benjamin Krill ");