18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (c) 2012 Intel Corporation. All rights reserved.
38c2ecf20Sopenharmony_ci * Copyright (c) 2006 - 2012 QLogic Corporation. All rights reserved.
48c2ecf20Sopenharmony_ci * Copyright (c) 2006 PathScale, Inc. All rights reserved.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * This software is available to you under a choice of one of two
78c2ecf20Sopenharmony_ci * licenses.  You may choose to be licensed under the terms of the GNU
88c2ecf20Sopenharmony_ci * General Public License (GPL) Version 2, available from the file
98c2ecf20Sopenharmony_ci * COPYING in the main directory of this source tree, or the
108c2ecf20Sopenharmony_ci * OpenIB.org BSD license below:
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci *     Redistribution and use in source and binary forms, with or
138c2ecf20Sopenharmony_ci *     without modification, are permitted provided that the following
148c2ecf20Sopenharmony_ci *     conditions are met:
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci *      - Redistributions of source code must retain the above
178c2ecf20Sopenharmony_ci *        copyright notice, this list of conditions and the following
188c2ecf20Sopenharmony_ci *        disclaimer.
198c2ecf20Sopenharmony_ci *
208c2ecf20Sopenharmony_ci *      - Redistributions in binary form must reproduce the above
218c2ecf20Sopenharmony_ci *        copyright notice, this list of conditions and the following
228c2ecf20Sopenharmony_ci *        disclaimer in the documentation and/or other materials
238c2ecf20Sopenharmony_ci *        provided with the distribution.
248c2ecf20Sopenharmony_ci *
258c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
268c2ecf20Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
278c2ecf20Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
288c2ecf20Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
298c2ecf20Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
308c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
318c2ecf20Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
328c2ecf20Sopenharmony_ci * SOFTWARE.
338c2ecf20Sopenharmony_ci */
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#include <linux/module.h>
368c2ecf20Sopenharmony_ci#include <linux/fs.h>
378c2ecf20Sopenharmony_ci#include <linux/fs_context.h>
388c2ecf20Sopenharmony_ci#include <linux/mount.h>
398c2ecf20Sopenharmony_ci#include <linux/pagemap.h>
408c2ecf20Sopenharmony_ci#include <linux/init.h>
418c2ecf20Sopenharmony_ci#include <linux/namei.h>
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#include "qib.h"
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#define QIBFS_MAGIC 0x726a77
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic struct super_block *qib_super;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#define private2dd(file) (file_inode(file)->i_private)
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic int qibfs_mknod(struct inode *dir, struct dentry *dentry,
528c2ecf20Sopenharmony_ci		       umode_t mode, const struct file_operations *fops,
538c2ecf20Sopenharmony_ci		       void *data)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	int error;
568c2ecf20Sopenharmony_ci	struct inode *inode = new_inode(dir->i_sb);
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	if (!inode) {
598c2ecf20Sopenharmony_ci		error = -EPERM;
608c2ecf20Sopenharmony_ci		goto bail;
618c2ecf20Sopenharmony_ci	}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	inode->i_ino = get_next_ino();
648c2ecf20Sopenharmony_ci	inode->i_mode = mode;
658c2ecf20Sopenharmony_ci	inode->i_uid = GLOBAL_ROOT_UID;
668c2ecf20Sopenharmony_ci	inode->i_gid = GLOBAL_ROOT_GID;
678c2ecf20Sopenharmony_ci	inode->i_blocks = 0;
688c2ecf20Sopenharmony_ci	inode->i_atime = current_time(inode);
698c2ecf20Sopenharmony_ci	inode->i_mtime = inode->i_atime;
708c2ecf20Sopenharmony_ci	inode->i_ctime = inode->i_atime;
718c2ecf20Sopenharmony_ci	inode->i_private = data;
728c2ecf20Sopenharmony_ci	if (S_ISDIR(mode)) {
738c2ecf20Sopenharmony_ci		inode->i_op = &simple_dir_inode_operations;
748c2ecf20Sopenharmony_ci		inc_nlink(inode);
758c2ecf20Sopenharmony_ci		inc_nlink(dir);
768c2ecf20Sopenharmony_ci	}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	inode->i_fop = fops;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	d_instantiate(dentry, inode);
818c2ecf20Sopenharmony_ci	error = 0;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cibail:
848c2ecf20Sopenharmony_ci	return error;
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic int create_file(const char *name, umode_t mode,
888c2ecf20Sopenharmony_ci		       struct dentry *parent, struct dentry **dentry,
898c2ecf20Sopenharmony_ci		       const struct file_operations *fops, void *data)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	int error;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	inode_lock(d_inode(parent));
948c2ecf20Sopenharmony_ci	*dentry = lookup_one_len(name, parent, strlen(name));
958c2ecf20Sopenharmony_ci	if (!IS_ERR(*dentry))
968c2ecf20Sopenharmony_ci		error = qibfs_mknod(d_inode(parent), *dentry,
978c2ecf20Sopenharmony_ci				    mode, fops, data);
988c2ecf20Sopenharmony_ci	else
998c2ecf20Sopenharmony_ci		error = PTR_ERR(*dentry);
1008c2ecf20Sopenharmony_ci	inode_unlock(d_inode(parent));
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	return error;
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic ssize_t driver_stats_read(struct file *file, char __user *buf,
1068c2ecf20Sopenharmony_ci				 size_t count, loff_t *ppos)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	qib_stats.sps_ints = qib_sps_ints();
1098c2ecf20Sopenharmony_ci	return simple_read_from_buffer(buf, count, ppos, &qib_stats,
1108c2ecf20Sopenharmony_ci				       sizeof(qib_stats));
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci/*
1148c2ecf20Sopenharmony_ci * driver stats field names, one line per stat, single string.  Used by
1158c2ecf20Sopenharmony_ci * programs like ipathstats to print the stats in a way which works for
1168c2ecf20Sopenharmony_ci * different versions of drivers, without changing program source.
1178c2ecf20Sopenharmony_ci * if qlogic_ib_stats changes, this needs to change.  Names need to be
1188c2ecf20Sopenharmony_ci * 12 chars or less (w/o newline), for proper display by ipathstats utility.
1198c2ecf20Sopenharmony_ci */
1208c2ecf20Sopenharmony_cistatic const char qib_statnames[] =
1218c2ecf20Sopenharmony_ci	"KernIntr\n"
1228c2ecf20Sopenharmony_ci	"ErrorIntr\n"
1238c2ecf20Sopenharmony_ci	"Tx_Errs\n"
1248c2ecf20Sopenharmony_ci	"Rcv_Errs\n"
1258c2ecf20Sopenharmony_ci	"H/W_Errs\n"
1268c2ecf20Sopenharmony_ci	"NoPIOBufs\n"
1278c2ecf20Sopenharmony_ci	"CtxtsOpen\n"
1288c2ecf20Sopenharmony_ci	"RcvLen_Errs\n"
1298c2ecf20Sopenharmony_ci	"EgrBufFull\n"
1308c2ecf20Sopenharmony_ci	"EgrHdrFull\n"
1318c2ecf20Sopenharmony_ci	;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic ssize_t driver_names_read(struct file *file, char __user *buf,
1348c2ecf20Sopenharmony_ci				 size_t count, loff_t *ppos)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	return simple_read_from_buffer(buf, count, ppos, qib_statnames,
1378c2ecf20Sopenharmony_ci		sizeof(qib_statnames) - 1); /* no null */
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistatic const struct file_operations driver_ops[] = {
1418c2ecf20Sopenharmony_ci	{ .read = driver_stats_read, .llseek = generic_file_llseek, },
1428c2ecf20Sopenharmony_ci	{ .read = driver_names_read, .llseek = generic_file_llseek, },
1438c2ecf20Sopenharmony_ci};
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci/* read the per-device counters */
1468c2ecf20Sopenharmony_cistatic ssize_t dev_counters_read(struct file *file, char __user *buf,
1478c2ecf20Sopenharmony_ci				 size_t count, loff_t *ppos)
1488c2ecf20Sopenharmony_ci{
1498c2ecf20Sopenharmony_ci	u64 *counters;
1508c2ecf20Sopenharmony_ci	size_t avail;
1518c2ecf20Sopenharmony_ci	struct qib_devdata *dd = private2dd(file);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	avail = dd->f_read_cntrs(dd, *ppos, NULL, &counters);
1548c2ecf20Sopenharmony_ci	return simple_read_from_buffer(buf, count, ppos, counters, avail);
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci/* read the per-device counters */
1588c2ecf20Sopenharmony_cistatic ssize_t dev_names_read(struct file *file, char __user *buf,
1598c2ecf20Sopenharmony_ci			      size_t count, loff_t *ppos)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	char *names;
1628c2ecf20Sopenharmony_ci	size_t avail;
1638c2ecf20Sopenharmony_ci	struct qib_devdata *dd = private2dd(file);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	avail = dd->f_read_cntrs(dd, *ppos, &names, NULL);
1668c2ecf20Sopenharmony_ci	return simple_read_from_buffer(buf, count, ppos, names, avail);
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic const struct file_operations cntr_ops[] = {
1708c2ecf20Sopenharmony_ci	{ .read = dev_counters_read, .llseek = generic_file_llseek, },
1718c2ecf20Sopenharmony_ci	{ .read = dev_names_read, .llseek = generic_file_llseek, },
1728c2ecf20Sopenharmony_ci};
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci/*
1758c2ecf20Sopenharmony_ci * Could use file_inode(file)->i_ino to figure out which file,
1768c2ecf20Sopenharmony_ci * instead of separate routine for each, but for now, this works...
1778c2ecf20Sopenharmony_ci */
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci/* read the per-port names (same for each port) */
1808c2ecf20Sopenharmony_cistatic ssize_t portnames_read(struct file *file, char __user *buf,
1818c2ecf20Sopenharmony_ci			      size_t count, loff_t *ppos)
1828c2ecf20Sopenharmony_ci{
1838c2ecf20Sopenharmony_ci	char *names;
1848c2ecf20Sopenharmony_ci	size_t avail;
1858c2ecf20Sopenharmony_ci	struct qib_devdata *dd = private2dd(file);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	avail = dd->f_read_portcntrs(dd, *ppos, 0, &names, NULL);
1888c2ecf20Sopenharmony_ci	return simple_read_from_buffer(buf, count, ppos, names, avail);
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci/* read the per-port counters for port 1 (pidx 0) */
1928c2ecf20Sopenharmony_cistatic ssize_t portcntrs_1_read(struct file *file, char __user *buf,
1938c2ecf20Sopenharmony_ci				size_t count, loff_t *ppos)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	u64 *counters;
1968c2ecf20Sopenharmony_ci	size_t avail;
1978c2ecf20Sopenharmony_ci	struct qib_devdata *dd = private2dd(file);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	avail = dd->f_read_portcntrs(dd, *ppos, 0, NULL, &counters);
2008c2ecf20Sopenharmony_ci	return simple_read_from_buffer(buf, count, ppos, counters, avail);
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci/* read the per-port counters for port 2 (pidx 1) */
2048c2ecf20Sopenharmony_cistatic ssize_t portcntrs_2_read(struct file *file, char __user *buf,
2058c2ecf20Sopenharmony_ci				size_t count, loff_t *ppos)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	u64 *counters;
2088c2ecf20Sopenharmony_ci	size_t avail;
2098c2ecf20Sopenharmony_ci	struct qib_devdata *dd = private2dd(file);
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	avail = dd->f_read_portcntrs(dd, *ppos, 1, NULL, &counters);
2128c2ecf20Sopenharmony_ci	return simple_read_from_buffer(buf, count, ppos, counters, avail);
2138c2ecf20Sopenharmony_ci}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_cistatic const struct file_operations portcntr_ops[] = {
2168c2ecf20Sopenharmony_ci	{ .read = portnames_read, .llseek = generic_file_llseek, },
2178c2ecf20Sopenharmony_ci	{ .read = portcntrs_1_read, .llseek = generic_file_llseek, },
2188c2ecf20Sopenharmony_ci	{ .read = portcntrs_2_read, .llseek = generic_file_llseek, },
2198c2ecf20Sopenharmony_ci};
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci/*
2228c2ecf20Sopenharmony_ci * read the per-port QSFP data for port 1 (pidx 0)
2238c2ecf20Sopenharmony_ci */
2248c2ecf20Sopenharmony_cistatic ssize_t qsfp_1_read(struct file *file, char __user *buf,
2258c2ecf20Sopenharmony_ci			   size_t count, loff_t *ppos)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	struct qib_devdata *dd = private2dd(file);
2288c2ecf20Sopenharmony_ci	char *tmp;
2298c2ecf20Sopenharmony_ci	int ret;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	tmp = kmalloc(PAGE_SIZE, GFP_KERNEL);
2328c2ecf20Sopenharmony_ci	if (!tmp)
2338c2ecf20Sopenharmony_ci		return -ENOMEM;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	ret = qib_qsfp_dump(dd->pport, tmp, PAGE_SIZE);
2368c2ecf20Sopenharmony_ci	if (ret > 0)
2378c2ecf20Sopenharmony_ci		ret = simple_read_from_buffer(buf, count, ppos, tmp, ret);
2388c2ecf20Sopenharmony_ci	kfree(tmp);
2398c2ecf20Sopenharmony_ci	return ret;
2408c2ecf20Sopenharmony_ci}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci/*
2438c2ecf20Sopenharmony_ci * read the per-port QSFP data for port 2 (pidx 1)
2448c2ecf20Sopenharmony_ci */
2458c2ecf20Sopenharmony_cistatic ssize_t qsfp_2_read(struct file *file, char __user *buf,
2468c2ecf20Sopenharmony_ci			   size_t count, loff_t *ppos)
2478c2ecf20Sopenharmony_ci{
2488c2ecf20Sopenharmony_ci	struct qib_devdata *dd = private2dd(file);
2498c2ecf20Sopenharmony_ci	char *tmp;
2508c2ecf20Sopenharmony_ci	int ret;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	if (dd->num_pports < 2)
2538c2ecf20Sopenharmony_ci		return -ENODEV;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	tmp = kmalloc(PAGE_SIZE, GFP_KERNEL);
2568c2ecf20Sopenharmony_ci	if (!tmp)
2578c2ecf20Sopenharmony_ci		return -ENOMEM;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	ret = qib_qsfp_dump(dd->pport + 1, tmp, PAGE_SIZE);
2608c2ecf20Sopenharmony_ci	if (ret > 0)
2618c2ecf20Sopenharmony_ci		ret = simple_read_from_buffer(buf, count, ppos, tmp, ret);
2628c2ecf20Sopenharmony_ci	kfree(tmp);
2638c2ecf20Sopenharmony_ci	return ret;
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_cistatic const struct file_operations qsfp_ops[] = {
2678c2ecf20Sopenharmony_ci	{ .read = qsfp_1_read, .llseek = generic_file_llseek, },
2688c2ecf20Sopenharmony_ci	{ .read = qsfp_2_read, .llseek = generic_file_llseek, },
2698c2ecf20Sopenharmony_ci};
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cistatic ssize_t flash_read(struct file *file, char __user *buf,
2728c2ecf20Sopenharmony_ci			  size_t count, loff_t *ppos)
2738c2ecf20Sopenharmony_ci{
2748c2ecf20Sopenharmony_ci	struct qib_devdata *dd;
2758c2ecf20Sopenharmony_ci	ssize_t ret;
2768c2ecf20Sopenharmony_ci	loff_t pos;
2778c2ecf20Sopenharmony_ci	char *tmp;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	pos = *ppos;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	if (pos < 0) {
2828c2ecf20Sopenharmony_ci		ret = -EINVAL;
2838c2ecf20Sopenharmony_ci		goto bail;
2848c2ecf20Sopenharmony_ci	}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	if (pos >= sizeof(struct qib_flash)) {
2878c2ecf20Sopenharmony_ci		ret = 0;
2888c2ecf20Sopenharmony_ci		goto bail;
2898c2ecf20Sopenharmony_ci	}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	if (count > sizeof(struct qib_flash) - pos)
2928c2ecf20Sopenharmony_ci		count = sizeof(struct qib_flash) - pos;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	tmp = kmalloc(count, GFP_KERNEL);
2958c2ecf20Sopenharmony_ci	if (!tmp) {
2968c2ecf20Sopenharmony_ci		ret = -ENOMEM;
2978c2ecf20Sopenharmony_ci		goto bail;
2988c2ecf20Sopenharmony_ci	}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	dd = private2dd(file);
3018c2ecf20Sopenharmony_ci	if (qib_eeprom_read(dd, pos, tmp, count)) {
3028c2ecf20Sopenharmony_ci		qib_dev_err(dd, "failed to read from flash\n");
3038c2ecf20Sopenharmony_ci		ret = -ENXIO;
3048c2ecf20Sopenharmony_ci		goto bail_tmp;
3058c2ecf20Sopenharmony_ci	}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	if (copy_to_user(buf, tmp, count)) {
3088c2ecf20Sopenharmony_ci		ret = -EFAULT;
3098c2ecf20Sopenharmony_ci		goto bail_tmp;
3108c2ecf20Sopenharmony_ci	}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	*ppos = pos + count;
3138c2ecf20Sopenharmony_ci	ret = count;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_cibail_tmp:
3168c2ecf20Sopenharmony_ci	kfree(tmp);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_cibail:
3198c2ecf20Sopenharmony_ci	return ret;
3208c2ecf20Sopenharmony_ci}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_cistatic ssize_t flash_write(struct file *file, const char __user *buf,
3238c2ecf20Sopenharmony_ci			   size_t count, loff_t *ppos)
3248c2ecf20Sopenharmony_ci{
3258c2ecf20Sopenharmony_ci	struct qib_devdata *dd;
3268c2ecf20Sopenharmony_ci	ssize_t ret;
3278c2ecf20Sopenharmony_ci	loff_t pos;
3288c2ecf20Sopenharmony_ci	char *tmp;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	pos = *ppos;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	if (pos != 0 || count != sizeof(struct qib_flash))
3338c2ecf20Sopenharmony_ci		return -EINVAL;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	tmp = memdup_user(buf, count);
3368c2ecf20Sopenharmony_ci	if (IS_ERR(tmp))
3378c2ecf20Sopenharmony_ci		return PTR_ERR(tmp);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	dd = private2dd(file);
3408c2ecf20Sopenharmony_ci	if (qib_eeprom_write(dd, pos, tmp, count)) {
3418c2ecf20Sopenharmony_ci		ret = -ENXIO;
3428c2ecf20Sopenharmony_ci		qib_dev_err(dd, "failed to write to flash\n");
3438c2ecf20Sopenharmony_ci		goto bail_tmp;
3448c2ecf20Sopenharmony_ci	}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	*ppos = pos + count;
3478c2ecf20Sopenharmony_ci	ret = count;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_cibail_tmp:
3508c2ecf20Sopenharmony_ci	kfree(tmp);
3518c2ecf20Sopenharmony_ci	return ret;
3528c2ecf20Sopenharmony_ci}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_cistatic const struct file_operations flash_ops = {
3558c2ecf20Sopenharmony_ci	.read = flash_read,
3568c2ecf20Sopenharmony_ci	.write = flash_write,
3578c2ecf20Sopenharmony_ci	.llseek = default_llseek,
3588c2ecf20Sopenharmony_ci};
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_cistatic int add_cntr_files(struct super_block *sb, struct qib_devdata *dd)
3618c2ecf20Sopenharmony_ci{
3628c2ecf20Sopenharmony_ci	struct dentry *dir, *tmp;
3638c2ecf20Sopenharmony_ci	char unit[10];
3648c2ecf20Sopenharmony_ci	int ret, i;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	/* create the per-unit directory */
3678c2ecf20Sopenharmony_ci	snprintf(unit, sizeof(unit), "%u", dd->unit);
3688c2ecf20Sopenharmony_ci	ret = create_file(unit, S_IFDIR|S_IRUGO|S_IXUGO, sb->s_root, &dir,
3698c2ecf20Sopenharmony_ci			  &simple_dir_operations, dd);
3708c2ecf20Sopenharmony_ci	if (ret) {
3718c2ecf20Sopenharmony_ci		pr_err("create_file(%s) failed: %d\n", unit, ret);
3728c2ecf20Sopenharmony_ci		goto bail;
3738c2ecf20Sopenharmony_ci	}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	/* create the files in the new directory */
3768c2ecf20Sopenharmony_ci	ret = create_file("counters", S_IFREG|S_IRUGO, dir, &tmp,
3778c2ecf20Sopenharmony_ci			  &cntr_ops[0], dd);
3788c2ecf20Sopenharmony_ci	if (ret) {
3798c2ecf20Sopenharmony_ci		pr_err("create_file(%s/counters) failed: %d\n",
3808c2ecf20Sopenharmony_ci		       unit, ret);
3818c2ecf20Sopenharmony_ci		goto bail;
3828c2ecf20Sopenharmony_ci	}
3838c2ecf20Sopenharmony_ci	ret = create_file("counter_names", S_IFREG|S_IRUGO, dir, &tmp,
3848c2ecf20Sopenharmony_ci			  &cntr_ops[1], dd);
3858c2ecf20Sopenharmony_ci	if (ret) {
3868c2ecf20Sopenharmony_ci		pr_err("create_file(%s/counter_names) failed: %d\n",
3878c2ecf20Sopenharmony_ci		       unit, ret);
3888c2ecf20Sopenharmony_ci		goto bail;
3898c2ecf20Sopenharmony_ci	}
3908c2ecf20Sopenharmony_ci	ret = create_file("portcounter_names", S_IFREG|S_IRUGO, dir, &tmp,
3918c2ecf20Sopenharmony_ci			  &portcntr_ops[0], dd);
3928c2ecf20Sopenharmony_ci	if (ret) {
3938c2ecf20Sopenharmony_ci		pr_err("create_file(%s/%s) failed: %d\n",
3948c2ecf20Sopenharmony_ci		       unit, "portcounter_names", ret);
3958c2ecf20Sopenharmony_ci		goto bail;
3968c2ecf20Sopenharmony_ci	}
3978c2ecf20Sopenharmony_ci	for (i = 1; i <= dd->num_pports; i++) {
3988c2ecf20Sopenharmony_ci		char fname[24];
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci		sprintf(fname, "port%dcounters", i);
4018c2ecf20Sopenharmony_ci		/* create the files in the new directory */
4028c2ecf20Sopenharmony_ci		ret = create_file(fname, S_IFREG|S_IRUGO, dir, &tmp,
4038c2ecf20Sopenharmony_ci				  &portcntr_ops[i], dd);
4048c2ecf20Sopenharmony_ci		if (ret) {
4058c2ecf20Sopenharmony_ci			pr_err("create_file(%s/%s) failed: %d\n",
4068c2ecf20Sopenharmony_ci				unit, fname, ret);
4078c2ecf20Sopenharmony_ci			goto bail;
4088c2ecf20Sopenharmony_ci		}
4098c2ecf20Sopenharmony_ci		if (!(dd->flags & QIB_HAS_QSFP))
4108c2ecf20Sopenharmony_ci			continue;
4118c2ecf20Sopenharmony_ci		sprintf(fname, "qsfp%d", i);
4128c2ecf20Sopenharmony_ci		ret = create_file(fname, S_IFREG|S_IRUGO, dir, &tmp,
4138c2ecf20Sopenharmony_ci				  &qsfp_ops[i - 1], dd);
4148c2ecf20Sopenharmony_ci		if (ret) {
4158c2ecf20Sopenharmony_ci			pr_err("create_file(%s/%s) failed: %d\n",
4168c2ecf20Sopenharmony_ci				unit, fname, ret);
4178c2ecf20Sopenharmony_ci			goto bail;
4188c2ecf20Sopenharmony_ci		}
4198c2ecf20Sopenharmony_ci	}
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	ret = create_file("flash", S_IFREG|S_IWUSR|S_IRUGO, dir, &tmp,
4228c2ecf20Sopenharmony_ci			  &flash_ops, dd);
4238c2ecf20Sopenharmony_ci	if (ret)
4248c2ecf20Sopenharmony_ci		pr_err("create_file(%s/flash) failed: %d\n",
4258c2ecf20Sopenharmony_ci			unit, ret);
4268c2ecf20Sopenharmony_cibail:
4278c2ecf20Sopenharmony_ci	return ret;
4288c2ecf20Sopenharmony_ci}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_cistatic int remove_file(struct dentry *parent, char *name)
4318c2ecf20Sopenharmony_ci{
4328c2ecf20Sopenharmony_ci	struct dentry *tmp;
4338c2ecf20Sopenharmony_ci	int ret;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	tmp = lookup_one_len(name, parent, strlen(name));
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	if (IS_ERR(tmp)) {
4388c2ecf20Sopenharmony_ci		ret = PTR_ERR(tmp);
4398c2ecf20Sopenharmony_ci		goto bail;
4408c2ecf20Sopenharmony_ci	}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	spin_lock(&tmp->d_lock);
4438c2ecf20Sopenharmony_ci	if (simple_positive(tmp)) {
4448c2ecf20Sopenharmony_ci		__d_drop(tmp);
4458c2ecf20Sopenharmony_ci		spin_unlock(&tmp->d_lock);
4468c2ecf20Sopenharmony_ci		simple_unlink(d_inode(parent), tmp);
4478c2ecf20Sopenharmony_ci	} else {
4488c2ecf20Sopenharmony_ci		spin_unlock(&tmp->d_lock);
4498c2ecf20Sopenharmony_ci	}
4508c2ecf20Sopenharmony_ci	dput(tmp);
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	ret = 0;
4538c2ecf20Sopenharmony_cibail:
4548c2ecf20Sopenharmony_ci	/*
4558c2ecf20Sopenharmony_ci	 * We don't expect clients to care about the return value, but
4568c2ecf20Sopenharmony_ci	 * it's there if they need it.
4578c2ecf20Sopenharmony_ci	 */
4588c2ecf20Sopenharmony_ci	return ret;
4598c2ecf20Sopenharmony_ci}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_cistatic int remove_device_files(struct super_block *sb,
4628c2ecf20Sopenharmony_ci			       struct qib_devdata *dd)
4638c2ecf20Sopenharmony_ci{
4648c2ecf20Sopenharmony_ci	struct dentry *dir, *root;
4658c2ecf20Sopenharmony_ci	char unit[10];
4668c2ecf20Sopenharmony_ci	int ret, i;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	root = dget(sb->s_root);
4698c2ecf20Sopenharmony_ci	inode_lock(d_inode(root));
4708c2ecf20Sopenharmony_ci	snprintf(unit, sizeof(unit), "%u", dd->unit);
4718c2ecf20Sopenharmony_ci	dir = lookup_one_len(unit, root, strlen(unit));
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	if (IS_ERR(dir)) {
4748c2ecf20Sopenharmony_ci		ret = PTR_ERR(dir);
4758c2ecf20Sopenharmony_ci		pr_err("Lookup of %s failed\n", unit);
4768c2ecf20Sopenharmony_ci		goto bail;
4778c2ecf20Sopenharmony_ci	}
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	inode_lock(d_inode(dir));
4808c2ecf20Sopenharmony_ci	remove_file(dir, "counters");
4818c2ecf20Sopenharmony_ci	remove_file(dir, "counter_names");
4828c2ecf20Sopenharmony_ci	remove_file(dir, "portcounter_names");
4838c2ecf20Sopenharmony_ci	for (i = 0; i < dd->num_pports; i++) {
4848c2ecf20Sopenharmony_ci		char fname[24];
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci		sprintf(fname, "port%dcounters", i + 1);
4878c2ecf20Sopenharmony_ci		remove_file(dir, fname);
4888c2ecf20Sopenharmony_ci		if (dd->flags & QIB_HAS_QSFP) {
4898c2ecf20Sopenharmony_ci			sprintf(fname, "qsfp%d", i + 1);
4908c2ecf20Sopenharmony_ci			remove_file(dir, fname);
4918c2ecf20Sopenharmony_ci		}
4928c2ecf20Sopenharmony_ci	}
4938c2ecf20Sopenharmony_ci	remove_file(dir, "flash");
4948c2ecf20Sopenharmony_ci	inode_unlock(d_inode(dir));
4958c2ecf20Sopenharmony_ci	ret = simple_rmdir(d_inode(root), dir);
4968c2ecf20Sopenharmony_ci	d_drop(dir);
4978c2ecf20Sopenharmony_ci	dput(dir);
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_cibail:
5008c2ecf20Sopenharmony_ci	inode_unlock(d_inode(root));
5018c2ecf20Sopenharmony_ci	dput(root);
5028c2ecf20Sopenharmony_ci	return ret;
5038c2ecf20Sopenharmony_ci}
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci/*
5068c2ecf20Sopenharmony_ci * This fills everything in when the fs is mounted, to handle umount/mount
5078c2ecf20Sopenharmony_ci * after device init.  The direct add_cntr_files() call handles adding
5088c2ecf20Sopenharmony_ci * them from the init code, when the fs is already mounted.
5098c2ecf20Sopenharmony_ci */
5108c2ecf20Sopenharmony_cistatic int qibfs_fill_super(struct super_block *sb, struct fs_context *fc)
5118c2ecf20Sopenharmony_ci{
5128c2ecf20Sopenharmony_ci	struct qib_devdata *dd;
5138c2ecf20Sopenharmony_ci	unsigned long index;
5148c2ecf20Sopenharmony_ci	int ret;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	static const struct tree_descr files[] = {
5178c2ecf20Sopenharmony_ci		[2] = {"driver_stats", &driver_ops[0], S_IRUGO},
5188c2ecf20Sopenharmony_ci		[3] = {"driver_stats_names", &driver_ops[1], S_IRUGO},
5198c2ecf20Sopenharmony_ci		{""},
5208c2ecf20Sopenharmony_ci	};
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	ret = simple_fill_super(sb, QIBFS_MAGIC, files);
5238c2ecf20Sopenharmony_ci	if (ret) {
5248c2ecf20Sopenharmony_ci		pr_err("simple_fill_super failed: %d\n", ret);
5258c2ecf20Sopenharmony_ci		goto bail;
5268c2ecf20Sopenharmony_ci	}
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	xa_for_each(&qib_dev_table, index, dd) {
5298c2ecf20Sopenharmony_ci		ret = add_cntr_files(sb, dd);
5308c2ecf20Sopenharmony_ci		if (ret)
5318c2ecf20Sopenharmony_ci			goto bail;
5328c2ecf20Sopenharmony_ci	}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_cibail:
5358c2ecf20Sopenharmony_ci	return ret;
5368c2ecf20Sopenharmony_ci}
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_cistatic int qibfs_get_tree(struct fs_context *fc)
5398c2ecf20Sopenharmony_ci{
5408c2ecf20Sopenharmony_ci	int ret = get_tree_single(fc, qibfs_fill_super);
5418c2ecf20Sopenharmony_ci	if (ret == 0)
5428c2ecf20Sopenharmony_ci		qib_super = fc->root->d_sb;
5438c2ecf20Sopenharmony_ci	return ret;
5448c2ecf20Sopenharmony_ci}
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_cistatic const struct fs_context_operations qibfs_context_ops = {
5478c2ecf20Sopenharmony_ci	.get_tree	= qibfs_get_tree,
5488c2ecf20Sopenharmony_ci};
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_cistatic int qibfs_init_fs_context(struct fs_context *fc)
5518c2ecf20Sopenharmony_ci{
5528c2ecf20Sopenharmony_ci	fc->ops = &qibfs_context_ops;
5538c2ecf20Sopenharmony_ci	return 0;
5548c2ecf20Sopenharmony_ci}
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_cistatic void qibfs_kill_super(struct super_block *s)
5578c2ecf20Sopenharmony_ci{
5588c2ecf20Sopenharmony_ci	kill_litter_super(s);
5598c2ecf20Sopenharmony_ci	qib_super = NULL;
5608c2ecf20Sopenharmony_ci}
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ciint qibfs_add(struct qib_devdata *dd)
5638c2ecf20Sopenharmony_ci{
5648c2ecf20Sopenharmony_ci	int ret;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	/*
5678c2ecf20Sopenharmony_ci	 * On first unit initialized, qib_super will not yet exist
5688c2ecf20Sopenharmony_ci	 * because nobody has yet tried to mount the filesystem, so
5698c2ecf20Sopenharmony_ci	 * we can't consider that to be an error; if an error occurs
5708c2ecf20Sopenharmony_ci	 * during the mount, that will get a complaint, so this is OK.
5718c2ecf20Sopenharmony_ci	 * add_cntr_files() for all units is done at mount from
5728c2ecf20Sopenharmony_ci	 * qibfs_fill_super(), so one way or another, everything works.
5738c2ecf20Sopenharmony_ci	 */
5748c2ecf20Sopenharmony_ci	if (qib_super == NULL)
5758c2ecf20Sopenharmony_ci		ret = 0;
5768c2ecf20Sopenharmony_ci	else
5778c2ecf20Sopenharmony_ci		ret = add_cntr_files(qib_super, dd);
5788c2ecf20Sopenharmony_ci	return ret;
5798c2ecf20Sopenharmony_ci}
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ciint qibfs_remove(struct qib_devdata *dd)
5828c2ecf20Sopenharmony_ci{
5838c2ecf20Sopenharmony_ci	int ret = 0;
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	if (qib_super)
5868c2ecf20Sopenharmony_ci		ret = remove_device_files(qib_super, dd);
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	return ret;
5898c2ecf20Sopenharmony_ci}
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_cistatic struct file_system_type qibfs_fs_type = {
5928c2ecf20Sopenharmony_ci	.owner =        THIS_MODULE,
5938c2ecf20Sopenharmony_ci	.name =         "ipathfs",
5948c2ecf20Sopenharmony_ci	.init_fs_context = qibfs_init_fs_context,
5958c2ecf20Sopenharmony_ci	.kill_sb =      qibfs_kill_super,
5968c2ecf20Sopenharmony_ci};
5978c2ecf20Sopenharmony_ciMODULE_ALIAS_FS("ipathfs");
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ciint __init qib_init_qibfs(void)
6008c2ecf20Sopenharmony_ci{
6018c2ecf20Sopenharmony_ci	return register_filesystem(&qibfs_fs_type);
6028c2ecf20Sopenharmony_ci}
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ciint __exit qib_exit_qibfs(void)
6058c2ecf20Sopenharmony_ci{
6068c2ecf20Sopenharmony_ci	return unregister_filesystem(&qibfs_fs_type);
6078c2ecf20Sopenharmony_ci}
608