162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright (c) 2012 Intel Corporation. All rights reserved.
362306a36Sopenharmony_ci * Copyright (c) 2006 - 2012 QLogic Corporation. All rights reserved.
462306a36Sopenharmony_ci * Copyright (c) 2006 PathScale, Inc. All rights reserved.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * This software is available to you under a choice of one of two
762306a36Sopenharmony_ci * licenses.  You may choose to be licensed under the terms of the GNU
862306a36Sopenharmony_ci * General Public License (GPL) Version 2, available from the file
962306a36Sopenharmony_ci * COPYING in the main directory of this source tree, or the
1062306a36Sopenharmony_ci * OpenIB.org BSD license below:
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci *     Redistribution and use in source and binary forms, with or
1362306a36Sopenharmony_ci *     without modification, are permitted provided that the following
1462306a36Sopenharmony_ci *     conditions are met:
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci *      - Redistributions of source code must retain the above
1762306a36Sopenharmony_ci *        copyright notice, this list of conditions and the following
1862306a36Sopenharmony_ci *        disclaimer.
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci *      - Redistributions in binary form must reproduce the above
2162306a36Sopenharmony_ci *        copyright notice, this list of conditions and the following
2262306a36Sopenharmony_ci *        disclaimer in the documentation and/or other materials
2362306a36Sopenharmony_ci *        provided with the distribution.
2462306a36Sopenharmony_ci *
2562306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
2662306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
2762306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
2862306a36Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
2962306a36Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
3062306a36Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
3162306a36Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3262306a36Sopenharmony_ci * SOFTWARE.
3362306a36Sopenharmony_ci */
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#include <linux/fs.h>
3662306a36Sopenharmony_ci#include <linux/fs_context.h>
3762306a36Sopenharmony_ci#include <linux/mount.h>
3862306a36Sopenharmony_ci#include <linux/pagemap.h>
3962306a36Sopenharmony_ci#include <linux/init.h>
4062306a36Sopenharmony_ci#include <linux/namei.h>
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#include "qib.h"
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci#define QIBFS_MAGIC 0x726a77
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic struct super_block *qib_super;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#define private2dd(file) (file_inode(file)->i_private)
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic int qibfs_mknod(struct inode *dir, struct dentry *dentry,
5162306a36Sopenharmony_ci		       umode_t mode, const struct file_operations *fops,
5262306a36Sopenharmony_ci		       void *data)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	int error;
5562306a36Sopenharmony_ci	struct inode *inode = new_inode(dir->i_sb);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	if (!inode) {
5862306a36Sopenharmony_ci		error = -EPERM;
5962306a36Sopenharmony_ci		goto bail;
6062306a36Sopenharmony_ci	}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	inode->i_ino = get_next_ino();
6362306a36Sopenharmony_ci	inode->i_mode = mode;
6462306a36Sopenharmony_ci	inode->i_uid = GLOBAL_ROOT_UID;
6562306a36Sopenharmony_ci	inode->i_gid = GLOBAL_ROOT_GID;
6662306a36Sopenharmony_ci	inode->i_blocks = 0;
6762306a36Sopenharmony_ci	inode->i_atime = inode_set_ctime_current(inode);
6862306a36Sopenharmony_ci	inode->i_mtime = inode->i_atime;
6962306a36Sopenharmony_ci	inode->i_private = data;
7062306a36Sopenharmony_ci	if (S_ISDIR(mode)) {
7162306a36Sopenharmony_ci		inode->i_op = &simple_dir_inode_operations;
7262306a36Sopenharmony_ci		inc_nlink(inode);
7362306a36Sopenharmony_ci		inc_nlink(dir);
7462306a36Sopenharmony_ci	}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	inode->i_fop = fops;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	d_instantiate(dentry, inode);
7962306a36Sopenharmony_ci	error = 0;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cibail:
8262306a36Sopenharmony_ci	return error;
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic int create_file(const char *name, umode_t mode,
8662306a36Sopenharmony_ci		       struct dentry *parent, struct dentry **dentry,
8762306a36Sopenharmony_ci		       const struct file_operations *fops, void *data)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	int error;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	inode_lock(d_inode(parent));
9262306a36Sopenharmony_ci	*dentry = lookup_one_len(name, parent, strlen(name));
9362306a36Sopenharmony_ci	if (!IS_ERR(*dentry))
9462306a36Sopenharmony_ci		error = qibfs_mknod(d_inode(parent), *dentry,
9562306a36Sopenharmony_ci				    mode, fops, data);
9662306a36Sopenharmony_ci	else
9762306a36Sopenharmony_ci		error = PTR_ERR(*dentry);
9862306a36Sopenharmony_ci	inode_unlock(d_inode(parent));
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	return error;
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic ssize_t driver_stats_read(struct file *file, char __user *buf,
10462306a36Sopenharmony_ci				 size_t count, loff_t *ppos)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	qib_stats.sps_ints = qib_sps_ints();
10762306a36Sopenharmony_ci	return simple_read_from_buffer(buf, count, ppos, &qib_stats,
10862306a36Sopenharmony_ci				       sizeof(qib_stats));
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci/*
11262306a36Sopenharmony_ci * driver stats field names, one line per stat, single string.  Used by
11362306a36Sopenharmony_ci * programs like ipathstats to print the stats in a way which works for
11462306a36Sopenharmony_ci * different versions of drivers, without changing program source.
11562306a36Sopenharmony_ci * if qlogic_ib_stats changes, this needs to change.  Names need to be
11662306a36Sopenharmony_ci * 12 chars or less (w/o newline), for proper display by ipathstats utility.
11762306a36Sopenharmony_ci */
11862306a36Sopenharmony_cistatic const char qib_statnames[] =
11962306a36Sopenharmony_ci	"KernIntr\n"
12062306a36Sopenharmony_ci	"ErrorIntr\n"
12162306a36Sopenharmony_ci	"Tx_Errs\n"
12262306a36Sopenharmony_ci	"Rcv_Errs\n"
12362306a36Sopenharmony_ci	"H/W_Errs\n"
12462306a36Sopenharmony_ci	"NoPIOBufs\n"
12562306a36Sopenharmony_ci	"CtxtsOpen\n"
12662306a36Sopenharmony_ci	"RcvLen_Errs\n"
12762306a36Sopenharmony_ci	"EgrBufFull\n"
12862306a36Sopenharmony_ci	"EgrHdrFull\n"
12962306a36Sopenharmony_ci	;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic ssize_t driver_names_read(struct file *file, char __user *buf,
13262306a36Sopenharmony_ci				 size_t count, loff_t *ppos)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	return simple_read_from_buffer(buf, count, ppos, qib_statnames,
13562306a36Sopenharmony_ci		sizeof(qib_statnames) - 1); /* no null */
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic const struct file_operations driver_ops[] = {
13962306a36Sopenharmony_ci	{ .read = driver_stats_read, .llseek = generic_file_llseek, },
14062306a36Sopenharmony_ci	{ .read = driver_names_read, .llseek = generic_file_llseek, },
14162306a36Sopenharmony_ci};
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci/* read the per-device counters */
14462306a36Sopenharmony_cistatic ssize_t dev_counters_read(struct file *file, char __user *buf,
14562306a36Sopenharmony_ci				 size_t count, loff_t *ppos)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	u64 *counters;
14862306a36Sopenharmony_ci	size_t avail;
14962306a36Sopenharmony_ci	struct qib_devdata *dd = private2dd(file);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	avail = dd->f_read_cntrs(dd, *ppos, NULL, &counters);
15262306a36Sopenharmony_ci	return simple_read_from_buffer(buf, count, ppos, counters, avail);
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci/* read the per-device counters */
15662306a36Sopenharmony_cistatic ssize_t dev_names_read(struct file *file, char __user *buf,
15762306a36Sopenharmony_ci			      size_t count, loff_t *ppos)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	char *names;
16062306a36Sopenharmony_ci	size_t avail;
16162306a36Sopenharmony_ci	struct qib_devdata *dd = private2dd(file);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	avail = dd->f_read_cntrs(dd, *ppos, &names, NULL);
16462306a36Sopenharmony_ci	return simple_read_from_buffer(buf, count, ppos, names, avail);
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cistatic const struct file_operations cntr_ops[] = {
16862306a36Sopenharmony_ci	{ .read = dev_counters_read, .llseek = generic_file_llseek, },
16962306a36Sopenharmony_ci	{ .read = dev_names_read, .llseek = generic_file_llseek, },
17062306a36Sopenharmony_ci};
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci/*
17362306a36Sopenharmony_ci * Could use file_inode(file)->i_ino to figure out which file,
17462306a36Sopenharmony_ci * instead of separate routine for each, but for now, this works...
17562306a36Sopenharmony_ci */
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci/* read the per-port names (same for each port) */
17862306a36Sopenharmony_cistatic ssize_t portnames_read(struct file *file, char __user *buf,
17962306a36Sopenharmony_ci			      size_t count, loff_t *ppos)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	char *names;
18262306a36Sopenharmony_ci	size_t avail;
18362306a36Sopenharmony_ci	struct qib_devdata *dd = private2dd(file);
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	avail = dd->f_read_portcntrs(dd, *ppos, 0, &names, NULL);
18662306a36Sopenharmony_ci	return simple_read_from_buffer(buf, count, ppos, names, avail);
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci/* read the per-port counters for port 1 (pidx 0) */
19062306a36Sopenharmony_cistatic ssize_t portcntrs_1_read(struct file *file, char __user *buf,
19162306a36Sopenharmony_ci				size_t count, loff_t *ppos)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	u64 *counters;
19462306a36Sopenharmony_ci	size_t avail;
19562306a36Sopenharmony_ci	struct qib_devdata *dd = private2dd(file);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	avail = dd->f_read_portcntrs(dd, *ppos, 0, NULL, &counters);
19862306a36Sopenharmony_ci	return simple_read_from_buffer(buf, count, ppos, counters, avail);
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci/* read the per-port counters for port 2 (pidx 1) */
20262306a36Sopenharmony_cistatic ssize_t portcntrs_2_read(struct file *file, char __user *buf,
20362306a36Sopenharmony_ci				size_t count, loff_t *ppos)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	u64 *counters;
20662306a36Sopenharmony_ci	size_t avail;
20762306a36Sopenharmony_ci	struct qib_devdata *dd = private2dd(file);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	avail = dd->f_read_portcntrs(dd, *ppos, 1, NULL, &counters);
21062306a36Sopenharmony_ci	return simple_read_from_buffer(buf, count, ppos, counters, avail);
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic const struct file_operations portcntr_ops[] = {
21462306a36Sopenharmony_ci	{ .read = portnames_read, .llseek = generic_file_llseek, },
21562306a36Sopenharmony_ci	{ .read = portcntrs_1_read, .llseek = generic_file_llseek, },
21662306a36Sopenharmony_ci	{ .read = portcntrs_2_read, .llseek = generic_file_llseek, },
21762306a36Sopenharmony_ci};
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci/*
22062306a36Sopenharmony_ci * read the per-port QSFP data for port 1 (pidx 0)
22162306a36Sopenharmony_ci */
22262306a36Sopenharmony_cistatic ssize_t qsfp_1_read(struct file *file, char __user *buf,
22362306a36Sopenharmony_ci			   size_t count, loff_t *ppos)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	struct qib_devdata *dd = private2dd(file);
22662306a36Sopenharmony_ci	char *tmp;
22762306a36Sopenharmony_ci	int ret;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	tmp = kmalloc(PAGE_SIZE, GFP_KERNEL);
23062306a36Sopenharmony_ci	if (!tmp)
23162306a36Sopenharmony_ci		return -ENOMEM;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	ret = qib_qsfp_dump(dd->pport, tmp, PAGE_SIZE);
23462306a36Sopenharmony_ci	if (ret > 0)
23562306a36Sopenharmony_ci		ret = simple_read_from_buffer(buf, count, ppos, tmp, ret);
23662306a36Sopenharmony_ci	kfree(tmp);
23762306a36Sopenharmony_ci	return ret;
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci/*
24162306a36Sopenharmony_ci * read the per-port QSFP data for port 2 (pidx 1)
24262306a36Sopenharmony_ci */
24362306a36Sopenharmony_cistatic ssize_t qsfp_2_read(struct file *file, char __user *buf,
24462306a36Sopenharmony_ci			   size_t count, loff_t *ppos)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	struct qib_devdata *dd = private2dd(file);
24762306a36Sopenharmony_ci	char *tmp;
24862306a36Sopenharmony_ci	int ret;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	if (dd->num_pports < 2)
25162306a36Sopenharmony_ci		return -ENODEV;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	tmp = kmalloc(PAGE_SIZE, GFP_KERNEL);
25462306a36Sopenharmony_ci	if (!tmp)
25562306a36Sopenharmony_ci		return -ENOMEM;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	ret = qib_qsfp_dump(dd->pport + 1, tmp, PAGE_SIZE);
25862306a36Sopenharmony_ci	if (ret > 0)
25962306a36Sopenharmony_ci		ret = simple_read_from_buffer(buf, count, ppos, tmp, ret);
26062306a36Sopenharmony_ci	kfree(tmp);
26162306a36Sopenharmony_ci	return ret;
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic const struct file_operations qsfp_ops[] = {
26562306a36Sopenharmony_ci	{ .read = qsfp_1_read, .llseek = generic_file_llseek, },
26662306a36Sopenharmony_ci	{ .read = qsfp_2_read, .llseek = generic_file_llseek, },
26762306a36Sopenharmony_ci};
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_cistatic ssize_t flash_read(struct file *file, char __user *buf,
27062306a36Sopenharmony_ci			  size_t count, loff_t *ppos)
27162306a36Sopenharmony_ci{
27262306a36Sopenharmony_ci	struct qib_devdata *dd;
27362306a36Sopenharmony_ci	ssize_t ret;
27462306a36Sopenharmony_ci	loff_t pos;
27562306a36Sopenharmony_ci	char *tmp;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	pos = *ppos;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	if (pos < 0) {
28062306a36Sopenharmony_ci		ret = -EINVAL;
28162306a36Sopenharmony_ci		goto bail;
28262306a36Sopenharmony_ci	}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	if (pos >= sizeof(struct qib_flash)) {
28562306a36Sopenharmony_ci		ret = 0;
28662306a36Sopenharmony_ci		goto bail;
28762306a36Sopenharmony_ci	}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	if (count > sizeof(struct qib_flash) - pos)
29062306a36Sopenharmony_ci		count = sizeof(struct qib_flash) - pos;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	tmp = kmalloc(count, GFP_KERNEL);
29362306a36Sopenharmony_ci	if (!tmp) {
29462306a36Sopenharmony_ci		ret = -ENOMEM;
29562306a36Sopenharmony_ci		goto bail;
29662306a36Sopenharmony_ci	}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	dd = private2dd(file);
29962306a36Sopenharmony_ci	if (qib_eeprom_read(dd, pos, tmp, count)) {
30062306a36Sopenharmony_ci		qib_dev_err(dd, "failed to read from flash\n");
30162306a36Sopenharmony_ci		ret = -ENXIO;
30262306a36Sopenharmony_ci		goto bail_tmp;
30362306a36Sopenharmony_ci	}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	if (copy_to_user(buf, tmp, count)) {
30662306a36Sopenharmony_ci		ret = -EFAULT;
30762306a36Sopenharmony_ci		goto bail_tmp;
30862306a36Sopenharmony_ci	}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	*ppos = pos + count;
31162306a36Sopenharmony_ci	ret = count;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_cibail_tmp:
31462306a36Sopenharmony_ci	kfree(tmp);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_cibail:
31762306a36Sopenharmony_ci	return ret;
31862306a36Sopenharmony_ci}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cistatic ssize_t flash_write(struct file *file, const char __user *buf,
32162306a36Sopenharmony_ci			   size_t count, loff_t *ppos)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	struct qib_devdata *dd;
32462306a36Sopenharmony_ci	ssize_t ret;
32562306a36Sopenharmony_ci	loff_t pos;
32662306a36Sopenharmony_ci	char *tmp;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	pos = *ppos;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	if (pos != 0 || count != sizeof(struct qib_flash))
33162306a36Sopenharmony_ci		return -EINVAL;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	tmp = memdup_user(buf, count);
33462306a36Sopenharmony_ci	if (IS_ERR(tmp))
33562306a36Sopenharmony_ci		return PTR_ERR(tmp);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	dd = private2dd(file);
33862306a36Sopenharmony_ci	if (qib_eeprom_write(dd, pos, tmp, count)) {
33962306a36Sopenharmony_ci		ret = -ENXIO;
34062306a36Sopenharmony_ci		qib_dev_err(dd, "failed to write to flash\n");
34162306a36Sopenharmony_ci		goto bail_tmp;
34262306a36Sopenharmony_ci	}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	*ppos = pos + count;
34562306a36Sopenharmony_ci	ret = count;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cibail_tmp:
34862306a36Sopenharmony_ci	kfree(tmp);
34962306a36Sopenharmony_ci	return ret;
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_cistatic const struct file_operations flash_ops = {
35362306a36Sopenharmony_ci	.read = flash_read,
35462306a36Sopenharmony_ci	.write = flash_write,
35562306a36Sopenharmony_ci	.llseek = default_llseek,
35662306a36Sopenharmony_ci};
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_cistatic int add_cntr_files(struct super_block *sb, struct qib_devdata *dd)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	struct dentry *dir, *tmp;
36162306a36Sopenharmony_ci	char unit[10];
36262306a36Sopenharmony_ci	int ret, i;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	/* create the per-unit directory */
36562306a36Sopenharmony_ci	snprintf(unit, sizeof(unit), "%u", dd->unit);
36662306a36Sopenharmony_ci	ret = create_file(unit, S_IFDIR|S_IRUGO|S_IXUGO, sb->s_root, &dir,
36762306a36Sopenharmony_ci			  &simple_dir_operations, dd);
36862306a36Sopenharmony_ci	if (ret) {
36962306a36Sopenharmony_ci		pr_err("create_file(%s) failed: %d\n", unit, ret);
37062306a36Sopenharmony_ci		goto bail;
37162306a36Sopenharmony_ci	}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	/* create the files in the new directory */
37462306a36Sopenharmony_ci	ret = create_file("counters", S_IFREG|S_IRUGO, dir, &tmp,
37562306a36Sopenharmony_ci			  &cntr_ops[0], dd);
37662306a36Sopenharmony_ci	if (ret) {
37762306a36Sopenharmony_ci		pr_err("create_file(%s/counters) failed: %d\n",
37862306a36Sopenharmony_ci		       unit, ret);
37962306a36Sopenharmony_ci		goto bail;
38062306a36Sopenharmony_ci	}
38162306a36Sopenharmony_ci	ret = create_file("counter_names", S_IFREG|S_IRUGO, dir, &tmp,
38262306a36Sopenharmony_ci			  &cntr_ops[1], dd);
38362306a36Sopenharmony_ci	if (ret) {
38462306a36Sopenharmony_ci		pr_err("create_file(%s/counter_names) failed: %d\n",
38562306a36Sopenharmony_ci		       unit, ret);
38662306a36Sopenharmony_ci		goto bail;
38762306a36Sopenharmony_ci	}
38862306a36Sopenharmony_ci	ret = create_file("portcounter_names", S_IFREG|S_IRUGO, dir, &tmp,
38962306a36Sopenharmony_ci			  &portcntr_ops[0], dd);
39062306a36Sopenharmony_ci	if (ret) {
39162306a36Sopenharmony_ci		pr_err("create_file(%s/%s) failed: %d\n",
39262306a36Sopenharmony_ci		       unit, "portcounter_names", ret);
39362306a36Sopenharmony_ci		goto bail;
39462306a36Sopenharmony_ci	}
39562306a36Sopenharmony_ci	for (i = 1; i <= dd->num_pports; i++) {
39662306a36Sopenharmony_ci		char fname[24];
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci		sprintf(fname, "port%dcounters", i);
39962306a36Sopenharmony_ci		/* create the files in the new directory */
40062306a36Sopenharmony_ci		ret = create_file(fname, S_IFREG|S_IRUGO, dir, &tmp,
40162306a36Sopenharmony_ci				  &portcntr_ops[i], dd);
40262306a36Sopenharmony_ci		if (ret) {
40362306a36Sopenharmony_ci			pr_err("create_file(%s/%s) failed: %d\n",
40462306a36Sopenharmony_ci				unit, fname, ret);
40562306a36Sopenharmony_ci			goto bail;
40662306a36Sopenharmony_ci		}
40762306a36Sopenharmony_ci		if (!(dd->flags & QIB_HAS_QSFP))
40862306a36Sopenharmony_ci			continue;
40962306a36Sopenharmony_ci		sprintf(fname, "qsfp%d", i);
41062306a36Sopenharmony_ci		ret = create_file(fname, S_IFREG|S_IRUGO, dir, &tmp,
41162306a36Sopenharmony_ci				  &qsfp_ops[i - 1], dd);
41262306a36Sopenharmony_ci		if (ret) {
41362306a36Sopenharmony_ci			pr_err("create_file(%s/%s) failed: %d\n",
41462306a36Sopenharmony_ci				unit, fname, ret);
41562306a36Sopenharmony_ci			goto bail;
41662306a36Sopenharmony_ci		}
41762306a36Sopenharmony_ci	}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	ret = create_file("flash", S_IFREG|S_IWUSR|S_IRUGO, dir, &tmp,
42062306a36Sopenharmony_ci			  &flash_ops, dd);
42162306a36Sopenharmony_ci	if (ret)
42262306a36Sopenharmony_ci		pr_err("create_file(%s/flash) failed: %d\n",
42362306a36Sopenharmony_ci			unit, ret);
42462306a36Sopenharmony_cibail:
42562306a36Sopenharmony_ci	return ret;
42662306a36Sopenharmony_ci}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_cistatic int remove_device_files(struct super_block *sb,
42962306a36Sopenharmony_ci			       struct qib_devdata *dd)
43062306a36Sopenharmony_ci{
43162306a36Sopenharmony_ci	struct dentry *dir;
43262306a36Sopenharmony_ci	char unit[10];
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	snprintf(unit, sizeof(unit), "%u", dd->unit);
43562306a36Sopenharmony_ci	dir = lookup_one_len_unlocked(unit, sb->s_root, strlen(unit));
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	if (IS_ERR(dir)) {
43862306a36Sopenharmony_ci		pr_err("Lookup of %s failed\n", unit);
43962306a36Sopenharmony_ci		return PTR_ERR(dir);
44062306a36Sopenharmony_ci	}
44162306a36Sopenharmony_ci	simple_recursive_removal(dir, NULL);
44262306a36Sopenharmony_ci	return 0;
44362306a36Sopenharmony_ci}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci/*
44662306a36Sopenharmony_ci * This fills everything in when the fs is mounted, to handle umount/mount
44762306a36Sopenharmony_ci * after device init.  The direct add_cntr_files() call handles adding
44862306a36Sopenharmony_ci * them from the init code, when the fs is already mounted.
44962306a36Sopenharmony_ci */
45062306a36Sopenharmony_cistatic int qibfs_fill_super(struct super_block *sb, struct fs_context *fc)
45162306a36Sopenharmony_ci{
45262306a36Sopenharmony_ci	struct qib_devdata *dd;
45362306a36Sopenharmony_ci	unsigned long index;
45462306a36Sopenharmony_ci	int ret;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	static const struct tree_descr files[] = {
45762306a36Sopenharmony_ci		[2] = {"driver_stats", &driver_ops[0], S_IRUGO},
45862306a36Sopenharmony_ci		[3] = {"driver_stats_names", &driver_ops[1], S_IRUGO},
45962306a36Sopenharmony_ci		{""},
46062306a36Sopenharmony_ci	};
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	ret = simple_fill_super(sb, QIBFS_MAGIC, files);
46362306a36Sopenharmony_ci	if (ret) {
46462306a36Sopenharmony_ci		pr_err("simple_fill_super failed: %d\n", ret);
46562306a36Sopenharmony_ci		goto bail;
46662306a36Sopenharmony_ci	}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	xa_for_each(&qib_dev_table, index, dd) {
46962306a36Sopenharmony_ci		ret = add_cntr_files(sb, dd);
47062306a36Sopenharmony_ci		if (ret)
47162306a36Sopenharmony_ci			goto bail;
47262306a36Sopenharmony_ci	}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_cibail:
47562306a36Sopenharmony_ci	return ret;
47662306a36Sopenharmony_ci}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_cistatic int qibfs_get_tree(struct fs_context *fc)
47962306a36Sopenharmony_ci{
48062306a36Sopenharmony_ci	int ret = get_tree_single(fc, qibfs_fill_super);
48162306a36Sopenharmony_ci	if (ret == 0)
48262306a36Sopenharmony_ci		qib_super = fc->root->d_sb;
48362306a36Sopenharmony_ci	return ret;
48462306a36Sopenharmony_ci}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_cistatic const struct fs_context_operations qibfs_context_ops = {
48762306a36Sopenharmony_ci	.get_tree	= qibfs_get_tree,
48862306a36Sopenharmony_ci};
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_cistatic int qibfs_init_fs_context(struct fs_context *fc)
49162306a36Sopenharmony_ci{
49262306a36Sopenharmony_ci	fc->ops = &qibfs_context_ops;
49362306a36Sopenharmony_ci	return 0;
49462306a36Sopenharmony_ci}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_cistatic void qibfs_kill_super(struct super_block *s)
49762306a36Sopenharmony_ci{
49862306a36Sopenharmony_ci	kill_litter_super(s);
49962306a36Sopenharmony_ci	qib_super = NULL;
50062306a36Sopenharmony_ci}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ciint qibfs_add(struct qib_devdata *dd)
50362306a36Sopenharmony_ci{
50462306a36Sopenharmony_ci	int ret;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	/*
50762306a36Sopenharmony_ci	 * On first unit initialized, qib_super will not yet exist
50862306a36Sopenharmony_ci	 * because nobody has yet tried to mount the filesystem, so
50962306a36Sopenharmony_ci	 * we can't consider that to be an error; if an error occurs
51062306a36Sopenharmony_ci	 * during the mount, that will get a complaint, so this is OK.
51162306a36Sopenharmony_ci	 * add_cntr_files() for all units is done at mount from
51262306a36Sopenharmony_ci	 * qibfs_fill_super(), so one way or another, everything works.
51362306a36Sopenharmony_ci	 */
51462306a36Sopenharmony_ci	if (qib_super == NULL)
51562306a36Sopenharmony_ci		ret = 0;
51662306a36Sopenharmony_ci	else
51762306a36Sopenharmony_ci		ret = add_cntr_files(qib_super, dd);
51862306a36Sopenharmony_ci	return ret;
51962306a36Sopenharmony_ci}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ciint qibfs_remove(struct qib_devdata *dd)
52262306a36Sopenharmony_ci{
52362306a36Sopenharmony_ci	int ret = 0;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	if (qib_super)
52662306a36Sopenharmony_ci		ret = remove_device_files(qib_super, dd);
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	return ret;
52962306a36Sopenharmony_ci}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_cistatic struct file_system_type qibfs_fs_type = {
53262306a36Sopenharmony_ci	.owner =        THIS_MODULE,
53362306a36Sopenharmony_ci	.name =         "ipathfs",
53462306a36Sopenharmony_ci	.init_fs_context = qibfs_init_fs_context,
53562306a36Sopenharmony_ci	.kill_sb =      qibfs_kill_super,
53662306a36Sopenharmony_ci};
53762306a36Sopenharmony_ciMODULE_ALIAS_FS("ipathfs");
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ciint __init qib_init_qibfs(void)
54062306a36Sopenharmony_ci{
54162306a36Sopenharmony_ci	return register_filesystem(&qibfs_fs_type);
54262306a36Sopenharmony_ci}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ciint __exit qib_exit_qibfs(void)
54562306a36Sopenharmony_ci{
54662306a36Sopenharmony_ci	return unregister_filesystem(&qibfs_fs_type);
54762306a36Sopenharmony_ci}
548