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