/* * SPDX-FileCopyrightText: Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: MIT * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #ifndef _NV_PROCFS_UTILS_H #define _NV_PROCFS_UTILS_H #include "conftest.h" #ifdef CONFIG_PROC_FS #include #include /* * Allow procfs to create file to exercise error forwarding. * This is supported by CRAY platforms. */ #if defined(CONFIG_CRAY_XT) #define EXERCISE_ERROR_FORWARDING NV_TRUE #else #define EXERCISE_ERROR_FORWARDING NV_FALSE #endif #define IS_EXERCISE_ERROR_FORWARDING_ENABLED() (EXERCISE_ERROR_FORWARDING) #if defined(NV_PROC_OPS_PRESENT) typedef struct proc_ops nv_proc_ops_t; #define NV_PROC_OPS_SET_OWNER() #define NV_PROC_OPS_OPEN proc_open #define NV_PROC_OPS_READ proc_read #define NV_PROC_OPS_WRITE proc_write #define NV_PROC_OPS_LSEEK proc_lseek #define NV_PROC_OPS_RELEASE proc_release #else typedef struct file_operations nv_proc_ops_t; #define NV_PROC_OPS_SET_OWNER() .owner = THIS_MODULE, #define NV_PROC_OPS_OPEN open #define NV_PROC_OPS_READ read #define NV_PROC_OPS_WRITE write #define NV_PROC_OPS_LSEEK llseek #define NV_PROC_OPS_RELEASE release #endif #define NV_CREATE_PROC_FILE(filename,parent,__name,__data) \ ({ \ struct proc_dir_entry *__entry; \ int mode = (S_IFREG | S_IRUGO); \ const nv_proc_ops_t *fops = &nv_procfs_##__name##_fops; \ if (fops->NV_PROC_OPS_WRITE != 0) \ mode |= S_IWUSR; \ __entry = proc_create_data(filename, mode, parent, fops, __data);\ __entry; \ }) /* * proc_mkdir_mode exists in Linux 2.6.9, but isn't exported until Linux 3.0. * Use the older interface instead unless the newer interface is necessary. */ #if defined(NV_PROC_REMOVE_PRESENT) # define NV_PROC_MKDIR_MODE(name, mode, parent) \ proc_mkdir_mode(name, mode, parent) #else # define NV_PROC_MKDIR_MODE(name, mode, parent) \ ({ \ struct proc_dir_entry *__entry; \ __entry = create_proc_entry(name, mode, parent); \ __entry; \ }) #endif #define NV_CREATE_PROC_DIR(name,parent) \ ({ \ struct proc_dir_entry *__entry; \ int mode = (S_IFDIR | S_IRUGO | S_IXUGO); \ __entry = NV_PROC_MKDIR_MODE(name, mode, parent); \ __entry; \ }) #if defined(NV_PDE_DATA_LOWER_CASE_PRESENT) #define NV_PDE_DATA(inode) pde_data(inode) #else #define NV_PDE_DATA(inode) PDE_DATA(inode) #endif #if defined(NV_PROC_REMOVE_PRESENT) # define NV_REMOVE_PROC_ENTRY(entry) \ proc_remove(entry); #else # define NV_REMOVE_PROC_ENTRY(entry) \ remove_proc_entry(entry->name, entry->parent); #endif void nv_procfs_unregister_all(struct proc_dir_entry *entry, struct proc_dir_entry *delimiter); #define NV_DEFINE_SINGLE_PROCFS_FILE_HELPER(name, lock) \ static int nv_procfs_open_##name( \ struct inode *inode, \ struct file *filep \ ) \ { \ int ret; \ ret = single_open(filep, nv_procfs_read_##name, \ NV_PDE_DATA(inode)); \ if (ret < 0) \ { \ return ret; \ } \ ret = nv_down_read_interruptible(&lock); \ if (ret < 0) \ { \ single_release(inode, filep); \ } \ return ret; \ } \ \ static int nv_procfs_release_##name( \ struct inode *inode, \ struct file *filep \ ) \ { \ up_read(&lock); \ return single_release(inode, filep); \ } #define NV_DEFINE_SINGLE_PROCFS_FILE_READ_ONLY(name, lock) \ NV_DEFINE_SINGLE_PROCFS_FILE_HELPER(name, lock) \ \ static const nv_proc_ops_t nv_procfs_##name##_fops = { \ NV_PROC_OPS_SET_OWNER() \ .NV_PROC_OPS_OPEN = nv_procfs_open_##name, \ .NV_PROC_OPS_READ = seq_read, \ .NV_PROC_OPS_LSEEK = seq_lseek, \ .NV_PROC_OPS_RELEASE = nv_procfs_release_##name, \ }; #define NV_DEFINE_SINGLE_PROCFS_FILE_READ_WRITE(name, lock, \ write_callback) \ NV_DEFINE_SINGLE_PROCFS_FILE_HELPER(name, lock) \ \ static ssize_t nv_procfs_write_##name( \ struct file *file, \ const char __user *buf, \ size_t size, \ loff_t *ppos \ ) \ { \ ssize_t ret; \ struct seq_file *s; \ \ s = file->private_data; \ if (s == NULL) \ { \ return -EIO; \ } \ \ ret = write_callback(s, buf + *ppos, size - *ppos); \ if (ret == 0) \ { \ /* avoid infinite loop */ \ ret = -EIO; \ } \ return ret; \ } \ \ static const nv_proc_ops_t nv_procfs_##name##_fops = { \ NV_PROC_OPS_SET_OWNER() \ .NV_PROC_OPS_OPEN = nv_procfs_open_##name, \ .NV_PROC_OPS_READ = seq_read, \ .NV_PROC_OPS_WRITE = nv_procfs_write_##name, \ .NV_PROC_OPS_LSEEK = seq_lseek, \ .NV_PROC_OPS_RELEASE = nv_procfs_release_##name, \ }; #define NV_DEFINE_SINGLE_PROCFS_FILE_READ_ONLY_WITHOUT_LOCK(name) \ static int nv_procfs_open_##name( \ struct inode *inode, \ struct file *filep \ ) \ { \ int ret; \ ret = single_open(filep, nv_procfs_read_##name, \ NV_PDE_DATA(inode)); \ return ret; \ } \ \ static int nv_procfs_release_##name( \ struct inode *inode, \ struct file *filep \ ) \ { \ return single_release(inode, filep); \ } \ \ static const nv_proc_ops_t nv_procfs_##name##_fops = { \ NV_PROC_OPS_SET_OWNER() \ .NV_PROC_OPS_OPEN = nv_procfs_open_##name, \ .NV_PROC_OPS_READ = seq_read, \ .NV_PROC_OPS_LSEEK = seq_lseek, \ .NV_PROC_OPS_RELEASE = nv_procfs_release_##name, \ }; #endif /* CONFIG_PROC_FS */ #endif /* _NV_PROCFS_UTILS_H */