Path: blob/master/drivers/infiniband/hw/ipath/ipath_fs.c
15112 views
/*1* Copyright (c) 2006, 2007 QLogic Corporation. All rights reserved.2* Copyright (c) 2006 PathScale, Inc. All rights reserved.3*4* This software is available to you under a choice of one of two5* licenses. You may choose to be licensed under the terms of the GNU6* General Public License (GPL) Version 2, available from the file7* COPYING in the main directory of this source tree, or the8* OpenIB.org BSD license below:9*10* Redistribution and use in source and binary forms, with or11* without modification, are permitted provided that the following12* conditions are met:13*14* - Redistributions of source code must retain the above15* copyright notice, this list of conditions and the following16* disclaimer.17*18* - Redistributions in binary form must reproduce the above19* copyright notice, this list of conditions and the following20* disclaimer in the documentation and/or other materials21* provided with the distribution.22*23* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,24* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF25* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND26* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS27* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN28* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN29* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE30* SOFTWARE.31*/3233#include <linux/module.h>34#include <linux/fs.h>35#include <linux/mount.h>36#include <linux/pagemap.h>37#include <linux/init.h>38#include <linux/namei.h>39#include <linux/slab.h>4041#include "ipath_kernel.h"4243#define IPATHFS_MAGIC 0x726a774445static struct super_block *ipath_super;4647static int ipathfs_mknod(struct inode *dir, struct dentry *dentry,48int mode, const struct file_operations *fops,49void *data)50{51int error;52struct inode *inode = new_inode(dir->i_sb);5354if (!inode) {55error = -EPERM;56goto bail;57}5859inode->i_ino = get_next_ino();60inode->i_mode = mode;61inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;62inode->i_private = data;63if ((mode & S_IFMT) == S_IFDIR) {64inode->i_op = &simple_dir_inode_operations;65inc_nlink(inode);66inc_nlink(dir);67}6869inode->i_fop = fops;7071d_instantiate(dentry, inode);72error = 0;7374bail:75return error;76}7778static int create_file(const char *name, mode_t mode,79struct dentry *parent, struct dentry **dentry,80const struct file_operations *fops, void *data)81{82int error;8384*dentry = NULL;85mutex_lock(&parent->d_inode->i_mutex);86*dentry = lookup_one_len(name, parent, strlen(name));87if (!IS_ERR(*dentry))88error = ipathfs_mknod(parent->d_inode, *dentry,89mode, fops, data);90else91error = PTR_ERR(dentry);92mutex_unlock(&parent->d_inode->i_mutex);9394return error;95}9697static ssize_t atomic_stats_read(struct file *file, char __user *buf,98size_t count, loff_t *ppos)99{100return simple_read_from_buffer(buf, count, ppos, &ipath_stats,101sizeof ipath_stats);102}103104static const struct file_operations atomic_stats_ops = {105.read = atomic_stats_read,106.llseek = default_llseek,107};108109static ssize_t atomic_counters_read(struct file *file, char __user *buf,110size_t count, loff_t *ppos)111{112struct infinipath_counters counters;113struct ipath_devdata *dd;114115dd = file->f_path.dentry->d_inode->i_private;116dd->ipath_f_read_counters(dd, &counters);117118return simple_read_from_buffer(buf, count, ppos, &counters,119sizeof counters);120}121122static const struct file_operations atomic_counters_ops = {123.read = atomic_counters_read,124.llseek = default_llseek,125};126127static ssize_t flash_read(struct file *file, char __user *buf,128size_t count, loff_t *ppos)129{130struct ipath_devdata *dd;131ssize_t ret;132loff_t pos;133char *tmp;134135pos = *ppos;136137if ( pos < 0) {138ret = -EINVAL;139goto bail;140}141142if (pos >= sizeof(struct ipath_flash)) {143ret = 0;144goto bail;145}146147if (count > sizeof(struct ipath_flash) - pos)148count = sizeof(struct ipath_flash) - pos;149150tmp = kmalloc(count, GFP_KERNEL);151if (!tmp) {152ret = -ENOMEM;153goto bail;154}155156dd = file->f_path.dentry->d_inode->i_private;157if (ipath_eeprom_read(dd, pos, tmp, count)) {158ipath_dev_err(dd, "failed to read from flash\n");159ret = -ENXIO;160goto bail_tmp;161}162163if (copy_to_user(buf, tmp, count)) {164ret = -EFAULT;165goto bail_tmp;166}167168*ppos = pos + count;169ret = count;170171bail_tmp:172kfree(tmp);173174bail:175return ret;176}177178static ssize_t flash_write(struct file *file, const char __user *buf,179size_t count, loff_t *ppos)180{181struct ipath_devdata *dd;182ssize_t ret;183loff_t pos;184char *tmp;185186pos = *ppos;187188if (pos != 0) {189ret = -EINVAL;190goto bail;191}192193if (count != sizeof(struct ipath_flash)) {194ret = -EINVAL;195goto bail;196}197198tmp = kmalloc(count, GFP_KERNEL);199if (!tmp) {200ret = -ENOMEM;201goto bail;202}203204if (copy_from_user(tmp, buf, count)) {205ret = -EFAULT;206goto bail_tmp;207}208209dd = file->f_path.dentry->d_inode->i_private;210if (ipath_eeprom_write(dd, pos, tmp, count)) {211ret = -ENXIO;212ipath_dev_err(dd, "failed to write to flash\n");213goto bail_tmp;214}215216*ppos = pos + count;217ret = count;218219bail_tmp:220kfree(tmp);221222bail:223return ret;224}225226static const struct file_operations flash_ops = {227.read = flash_read,228.write = flash_write,229.llseek = default_llseek,230};231232static int create_device_files(struct super_block *sb,233struct ipath_devdata *dd)234{235struct dentry *dir, *tmp;236char unit[10];237int ret;238239snprintf(unit, sizeof unit, "%02d", dd->ipath_unit);240ret = create_file(unit, S_IFDIR|S_IRUGO|S_IXUGO, sb->s_root, &dir,241&simple_dir_operations, dd);242if (ret) {243printk(KERN_ERR "create_file(%s) failed: %d\n", unit, ret);244goto bail;245}246247ret = create_file("atomic_counters", S_IFREG|S_IRUGO, dir, &tmp,248&atomic_counters_ops, dd);249if (ret) {250printk(KERN_ERR "create_file(%s/atomic_counters) "251"failed: %d\n", unit, ret);252goto bail;253}254255ret = create_file("flash", S_IFREG|S_IWUSR|S_IRUGO, dir, &tmp,256&flash_ops, dd);257if (ret) {258printk(KERN_ERR "create_file(%s/flash) "259"failed: %d\n", unit, ret);260goto bail;261}262263bail:264return ret;265}266267static int remove_file(struct dentry *parent, char *name)268{269struct dentry *tmp;270int ret;271272tmp = lookup_one_len(name, parent, strlen(name));273274if (IS_ERR(tmp)) {275ret = PTR_ERR(tmp);276goto bail;277}278279spin_lock(&tmp->d_lock);280if (!(d_unhashed(tmp) && tmp->d_inode)) {281dget_dlock(tmp);282__d_drop(tmp);283spin_unlock(&tmp->d_lock);284simple_unlink(parent->d_inode, tmp);285} else286spin_unlock(&tmp->d_lock);287288ret = 0;289bail:290/*291* We don't expect clients to care about the return value, but292* it's there if they need it.293*/294return ret;295}296297static int remove_device_files(struct super_block *sb,298struct ipath_devdata *dd)299{300struct dentry *dir, *root;301char unit[10];302int ret;303304root = dget(sb->s_root);305mutex_lock(&root->d_inode->i_mutex);306snprintf(unit, sizeof unit, "%02d", dd->ipath_unit);307dir = lookup_one_len(unit, root, strlen(unit));308309if (IS_ERR(dir)) {310ret = PTR_ERR(dir);311printk(KERN_ERR "Lookup of %s failed\n", unit);312goto bail;313}314315remove_file(dir, "flash");316remove_file(dir, "atomic_counters");317d_delete(dir);318ret = simple_rmdir(root->d_inode, dir);319320bail:321mutex_unlock(&root->d_inode->i_mutex);322dput(root);323return ret;324}325326static int ipathfs_fill_super(struct super_block *sb, void *data,327int silent)328{329struct ipath_devdata *dd, *tmp;330unsigned long flags;331int ret;332333static struct tree_descr files[] = {334[2] = {"atomic_stats", &atomic_stats_ops, S_IRUGO},335{""},336};337338ret = simple_fill_super(sb, IPATHFS_MAGIC, files);339if (ret) {340printk(KERN_ERR "simple_fill_super failed: %d\n", ret);341goto bail;342}343344spin_lock_irqsave(&ipath_devs_lock, flags);345346list_for_each_entry_safe(dd, tmp, &ipath_dev_list, ipath_list) {347spin_unlock_irqrestore(&ipath_devs_lock, flags);348ret = create_device_files(sb, dd);349if (ret)350goto bail;351spin_lock_irqsave(&ipath_devs_lock, flags);352}353354spin_unlock_irqrestore(&ipath_devs_lock, flags);355356bail:357return ret;358}359360static struct dentry *ipathfs_mount(struct file_system_type *fs_type,361int flags, const char *dev_name, void *data)362{363struct dentry *ret;364ret = mount_single(fs_type, flags, data, ipathfs_fill_super);365if (!IS_ERR(ret))366ipath_super = ret->d_sb;367return ret;368}369370static void ipathfs_kill_super(struct super_block *s)371{372kill_litter_super(s);373ipath_super = NULL;374}375376int ipathfs_add_device(struct ipath_devdata *dd)377{378int ret;379380if (ipath_super == NULL) {381ret = 0;382goto bail;383}384385ret = create_device_files(ipath_super, dd);386387bail:388return ret;389}390391int ipathfs_remove_device(struct ipath_devdata *dd)392{393int ret;394395if (ipath_super == NULL) {396ret = 0;397goto bail;398}399400ret = remove_device_files(ipath_super, dd);401402bail:403return ret;404}405406static struct file_system_type ipathfs_fs_type = {407.owner = THIS_MODULE,408.name = "ipathfs",409.mount = ipathfs_mount,410.kill_sb = ipathfs_kill_super,411};412413int __init ipath_init_ipathfs(void)414{415return register_filesystem(&ipathfs_fs_type);416}417418void __exit ipath_exit_ipathfs(void)419{420unregister_filesystem(&ipathfs_fs_type);421}422423424