2007-10-21 12:11:55 +02:00
|
|
|
/* *****************************************************************
|
|
|
|
* (C) Copyright 2007 Benjamin Krill <ben@codiert.org>
|
|
|
|
*
|
|
|
|
* 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
|
|
|
|
* *****************************************************************/
|
2007-06-28 18:20:34 +02:00
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/version.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/fs.h>
|
|
|
|
#include <linux/pagemap.h>
|
2007-07-18 20:12:10 +02:00
|
|
|
#include <linux/slab.h>
|
2007-06-28 18:20:34 +02:00
|
|
|
|
|
|
|
#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 = {
|
2007-06-28 22:43:40 +02:00
|
|
|
.lookup = simple_lookup
|
2007-06-28 18:20:34 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
const struct inode_operations fpgafs_dir_inode_operations = {
|
|
|
|
.lookup = simple_lookup,
|
|
|
|
.mkdir = fpgafs_mkdir,
|
2007-06-28 22:43:40 +02:00
|
|
|
.rmdir = fpgafs_rmdir
|
2007-06-28 18:20:34 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
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;
|
2007-07-10 16:18:07 +02:00
|
|
|
files++;
|
2007-06-28 18:20:34 +02:00
|
|
|
}
|
|
|
|
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;
|
2007-07-10 17:21:58 +02:00
|
|
|
inode->i_private = FPGAFS_I(inode)->i_ctx = ctx;
|
2007-06-28 18:20:34 +02:00
|
|
|
d_add(dentry, inode);
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int fpgafs_mkdir( struct inode *dir, struct dentry *dentry, int mode)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct inode *inode;
|
2007-06-28 22:43:40 +02:00
|
|
|
struct fpga_context *ctx = NULL;
|
2007-06-28 18:20:34 +02:00
|
|
|
|
|
|
|
ret = -ENOSPC;
|
|
|
|
inode = fpgafs_new_inode(dir->i_sb, mode | S_IFDIR);
|
|
|
|
if (!inode)
|
2007-07-09 13:34:47 +02:00
|
|
|
return ret;
|
|
|
|
|
|
|
|
mutex_lock(&inode->i_mutex);
|
2007-06-28 18:20:34 +02:00
|
|
|
|
|
|
|
if (dir->i_mode & S_ISGID) {
|
|
|
|
inode->i_gid = dir->i_gid;
|
|
|
|
inode->i_mode &= S_ISGID;
|
|
|
|
}
|
2007-07-10 17:07:35 +02:00
|
|
|
|
|
|
|
ctx = alloc_fpga_context();
|
|
|
|
FPGAFS_I(inode)->i_ctx = ctx;
|
|
|
|
if (!ctx)
|
|
|
|
goto unmutex;
|
2007-06-28 18:20:34 +02:00
|
|
|
|
|
|
|
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++;
|
|
|
|
|
2007-07-10 17:07:35 +02:00
|
|
|
unmutex:
|
2007-06-28 18:20:34 +02:00
|
|
|
mutex_unlock(&inode->i_mutex);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void fpgafs_prune_dir(struct dentry *dir)
|
|
|
|
{
|
|
|
|
struct dentry *dentry, *tmp;
|
2007-07-10 16:18:07 +02:00
|
|
|
//mutex_lock(&dir->d_inode->i_mutex);
|
2007-06-28 18:20:34 +02:00
|
|
|
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);
|
2007-07-10 16:18:07 +02:00
|
|
|
//mutex_unlock(&dir->d_inode->i_mutex);
|
2007-06-28 18:20:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int fpgafs_rmdir(struct inode *dir, struct dentry *dentry)
|
|
|
|
{
|
|
|
|
/* remove all entries */
|
2007-07-10 17:07:35 +02:00
|
|
|
free_fpga_context(FPGAFS_I(dentry->d_inode)->i_ctx);
|
2007-06-28 22:43:40 +02:00
|
|
|
fpgafs_prune_dir(dentry);
|
2007-06-28 18:20:34 +02:00
|
|
|
//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 kmem_cache * cachep,
|
|
|
|
unsigned long flags)
|
|
|
|
{
|
|
|
|
struct fpgafs_inode_info *fsi = p;
|
2007-07-18 20:12:10 +02:00
|
|
|
inode_init_once(&fsi->vfs_inode);
|
2007-06-28 18:20:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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, fpgafs_init_once, NULL);
|
|
|
|
if(!fpgafs_inode_cache)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
register_filesystem(&fpgafs_type);
|
2007-07-10 09:29:41 +02:00
|
|
|
printk("fpgafs: Benjamin Krill <ben@codiert.org>\n");
|
|
|
|
printk("fpgafs: successfull loaded ...\n");
|
2007-06-28 18:20:34 +02:00
|
|
|
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 <ben@codiert.org>");
|