18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * fs/kernfs/file.c - kernfs file implementation 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2001-3 Patrick Mochel 68c2ecf20Sopenharmony_ci * Copyright (c) 2007 SUSE Linux Products GmbH 78c2ecf20Sopenharmony_ci * Copyright (c) 2007, 2013 Tejun Heo <tj@kernel.org> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/fs.h> 118c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/poll.h> 148c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 158c2ecf20Sopenharmony_ci#include <linux/sched/mm.h> 168c2ecf20Sopenharmony_ci#include <linux/fsnotify.h> 178c2ecf20Sopenharmony_ci#include <linux/uio.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include "kernfs-internal.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* 228c2ecf20Sopenharmony_ci * There's one kernfs_open_file for each open file and one kernfs_open_node 238c2ecf20Sopenharmony_ci * for each kernfs_node with one or more open files. 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * kernfs_node->attr.open points to kernfs_open_node. attr.open is 268c2ecf20Sopenharmony_ci * protected by kernfs_open_node_lock. 278c2ecf20Sopenharmony_ci * 288c2ecf20Sopenharmony_ci * filp->private_data points to seq_file whose ->private points to 298c2ecf20Sopenharmony_ci * kernfs_open_file. kernfs_open_files are chained at 308c2ecf20Sopenharmony_ci * kernfs_open_node->files, which is protected by kernfs_open_file_mutex. 318c2ecf20Sopenharmony_ci */ 328c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(kernfs_open_node_lock); 338c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(kernfs_open_file_mutex); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistruct kernfs_open_node { 368c2ecf20Sopenharmony_ci atomic_t refcnt; 378c2ecf20Sopenharmony_ci atomic_t event; 388c2ecf20Sopenharmony_ci wait_queue_head_t poll; 398c2ecf20Sopenharmony_ci struct list_head files; /* goes through kernfs_open_file.list */ 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* 438c2ecf20Sopenharmony_ci * kernfs_notify() may be called from any context and bounces notifications 448c2ecf20Sopenharmony_ci * through a work item. To minimize space overhead in kernfs_node, the 458c2ecf20Sopenharmony_ci * pending queue is implemented as a singly linked list of kernfs_nodes. 468c2ecf20Sopenharmony_ci * The list is terminated with the self pointer so that whether a 478c2ecf20Sopenharmony_ci * kernfs_node is on the list or not can be determined by testing the next 488c2ecf20Sopenharmony_ci * pointer for NULL. 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_ci#define KERNFS_NOTIFY_EOL ((void *)&kernfs_notify_list) 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(kernfs_notify_lock); 538c2ecf20Sopenharmony_cistatic struct kernfs_node *kernfs_notify_list = KERNFS_NOTIFY_EOL; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic struct kernfs_open_file *kernfs_of(struct file *file) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci return ((struct seq_file *)file->private_data)->private; 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* 618c2ecf20Sopenharmony_ci * Determine the kernfs_ops for the given kernfs_node. This function must 628c2ecf20Sopenharmony_ci * be called while holding an active reference. 638c2ecf20Sopenharmony_ci */ 648c2ecf20Sopenharmony_cistatic const struct kernfs_ops *kernfs_ops(struct kernfs_node *kn) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci if (kn->flags & KERNFS_LOCKDEP) 678c2ecf20Sopenharmony_ci lockdep_assert_held(kn); 688c2ecf20Sopenharmony_ci return kn->attr.ops; 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* 728c2ecf20Sopenharmony_ci * As kernfs_seq_stop() is also called after kernfs_seq_start() or 738c2ecf20Sopenharmony_ci * kernfs_seq_next() failure, it needs to distinguish whether it's stopping 748c2ecf20Sopenharmony_ci * a seq_file iteration which is fully initialized with an active reference 758c2ecf20Sopenharmony_ci * or an aborted kernfs_seq_start() due to get_active failure. The 768c2ecf20Sopenharmony_ci * position pointer is the only context for each seq_file iteration and 778c2ecf20Sopenharmony_ci * thus the stop condition should be encoded in it. As the return value is 788c2ecf20Sopenharmony_ci * directly visible to userland, ERR_PTR(-ENODEV) is the only acceptable 798c2ecf20Sopenharmony_ci * choice to indicate get_active failure. 808c2ecf20Sopenharmony_ci * 818c2ecf20Sopenharmony_ci * Unfortunately, this is complicated due to the optional custom seq_file 828c2ecf20Sopenharmony_ci * operations which may return ERR_PTR(-ENODEV) too. kernfs_seq_stop() 838c2ecf20Sopenharmony_ci * can't distinguish whether ERR_PTR(-ENODEV) is from get_active failure or 848c2ecf20Sopenharmony_ci * custom seq_file operations and thus can't decide whether put_active 858c2ecf20Sopenharmony_ci * should be performed or not only on ERR_PTR(-ENODEV). 868c2ecf20Sopenharmony_ci * 878c2ecf20Sopenharmony_ci * This is worked around by factoring out the custom seq_stop() and 888c2ecf20Sopenharmony_ci * put_active part into kernfs_seq_stop_active(), skipping it from 898c2ecf20Sopenharmony_ci * kernfs_seq_stop() if ERR_PTR(-ENODEV) while invoking it directly after 908c2ecf20Sopenharmony_ci * custom seq_file operations fail with ERR_PTR(-ENODEV) - this ensures 918c2ecf20Sopenharmony_ci * that kernfs_seq_stop_active() is skipped only after get_active failure. 928c2ecf20Sopenharmony_ci */ 938c2ecf20Sopenharmony_cistatic void kernfs_seq_stop_active(struct seq_file *sf, void *v) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci struct kernfs_open_file *of = sf->private; 968c2ecf20Sopenharmony_ci const struct kernfs_ops *ops = kernfs_ops(of->kn); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (ops->seq_stop) 998c2ecf20Sopenharmony_ci ops->seq_stop(sf, v); 1008c2ecf20Sopenharmony_ci kernfs_put_active(of->kn); 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic void *kernfs_seq_start(struct seq_file *sf, loff_t *ppos) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci struct kernfs_open_file *of = sf->private; 1068c2ecf20Sopenharmony_ci const struct kernfs_ops *ops; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* 1098c2ecf20Sopenharmony_ci * @of->mutex nests outside active ref and is primarily to ensure that 1108c2ecf20Sopenharmony_ci * the ops aren't called concurrently for the same open file. 1118c2ecf20Sopenharmony_ci */ 1128c2ecf20Sopenharmony_ci mutex_lock(&of->mutex); 1138c2ecf20Sopenharmony_ci if (!kernfs_get_active(of->kn)) 1148c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci ops = kernfs_ops(of->kn); 1178c2ecf20Sopenharmony_ci if (ops->seq_start) { 1188c2ecf20Sopenharmony_ci void *next = ops->seq_start(sf, ppos); 1198c2ecf20Sopenharmony_ci /* see the comment above kernfs_seq_stop_active() */ 1208c2ecf20Sopenharmony_ci if (next == ERR_PTR(-ENODEV)) 1218c2ecf20Sopenharmony_ci kernfs_seq_stop_active(sf, next); 1228c2ecf20Sopenharmony_ci return next; 1238c2ecf20Sopenharmony_ci } else { 1248c2ecf20Sopenharmony_ci /* 1258c2ecf20Sopenharmony_ci * The same behavior and code as single_open(). Returns 1268c2ecf20Sopenharmony_ci * !NULL if pos is at the beginning; otherwise, NULL. 1278c2ecf20Sopenharmony_ci */ 1288c2ecf20Sopenharmony_ci return NULL + !*ppos; 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic void *kernfs_seq_next(struct seq_file *sf, void *v, loff_t *ppos) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci struct kernfs_open_file *of = sf->private; 1358c2ecf20Sopenharmony_ci const struct kernfs_ops *ops = kernfs_ops(of->kn); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (ops->seq_next) { 1388c2ecf20Sopenharmony_ci void *next = ops->seq_next(sf, v, ppos); 1398c2ecf20Sopenharmony_ci /* see the comment above kernfs_seq_stop_active() */ 1408c2ecf20Sopenharmony_ci if (next == ERR_PTR(-ENODEV)) 1418c2ecf20Sopenharmony_ci kernfs_seq_stop_active(sf, next); 1428c2ecf20Sopenharmony_ci return next; 1438c2ecf20Sopenharmony_ci } else { 1448c2ecf20Sopenharmony_ci /* 1458c2ecf20Sopenharmony_ci * The same behavior and code as single_open(), always 1468c2ecf20Sopenharmony_ci * terminate after the initial read. 1478c2ecf20Sopenharmony_ci */ 1488c2ecf20Sopenharmony_ci ++*ppos; 1498c2ecf20Sopenharmony_ci return NULL; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic void kernfs_seq_stop(struct seq_file *sf, void *v) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci struct kernfs_open_file *of = sf->private; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (v != ERR_PTR(-ENODEV)) 1588c2ecf20Sopenharmony_ci kernfs_seq_stop_active(sf, v); 1598c2ecf20Sopenharmony_ci mutex_unlock(&of->mutex); 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic int kernfs_seq_show(struct seq_file *sf, void *v) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci struct kernfs_open_file *of = sf->private; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci of->event = atomic_read(&of->kn->attr.open->event); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci return of->kn->attr.ops->seq_show(sf, v); 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic const struct seq_operations kernfs_seq_ops = { 1728c2ecf20Sopenharmony_ci .start = kernfs_seq_start, 1738c2ecf20Sopenharmony_ci .next = kernfs_seq_next, 1748c2ecf20Sopenharmony_ci .stop = kernfs_seq_stop, 1758c2ecf20Sopenharmony_ci .show = kernfs_seq_show, 1768c2ecf20Sopenharmony_ci}; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci/* 1798c2ecf20Sopenharmony_ci * As reading a bin file can have side-effects, the exact offset and bytes 1808c2ecf20Sopenharmony_ci * specified in read(2) call should be passed to the read callback making 1818c2ecf20Sopenharmony_ci * it difficult to use seq_file. Implement simplistic custom buffering for 1828c2ecf20Sopenharmony_ci * bin files. 1838c2ecf20Sopenharmony_ci */ 1848c2ecf20Sopenharmony_cistatic ssize_t kernfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci struct kernfs_open_file *of = kernfs_of(iocb->ki_filp); 1878c2ecf20Sopenharmony_ci ssize_t len = min_t(size_t, iov_iter_count(iter), PAGE_SIZE); 1888c2ecf20Sopenharmony_ci const struct kernfs_ops *ops; 1898c2ecf20Sopenharmony_ci char *buf; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci buf = of->prealloc_buf; 1928c2ecf20Sopenharmony_ci if (buf) 1938c2ecf20Sopenharmony_ci mutex_lock(&of->prealloc_mutex); 1948c2ecf20Sopenharmony_ci else 1958c2ecf20Sopenharmony_ci buf = kmalloc(len, GFP_KERNEL); 1968c2ecf20Sopenharmony_ci if (!buf) 1978c2ecf20Sopenharmony_ci return -ENOMEM; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci /* 2008c2ecf20Sopenharmony_ci * @of->mutex nests outside active ref and is used both to ensure that 2018c2ecf20Sopenharmony_ci * the ops aren't called concurrently for the same open file. 2028c2ecf20Sopenharmony_ci */ 2038c2ecf20Sopenharmony_ci mutex_lock(&of->mutex); 2048c2ecf20Sopenharmony_ci if (!kernfs_get_active(of->kn)) { 2058c2ecf20Sopenharmony_ci len = -ENODEV; 2068c2ecf20Sopenharmony_ci mutex_unlock(&of->mutex); 2078c2ecf20Sopenharmony_ci goto out_free; 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci of->event = atomic_read(&of->kn->attr.open->event); 2118c2ecf20Sopenharmony_ci ops = kernfs_ops(of->kn); 2128c2ecf20Sopenharmony_ci if (ops->read) 2138c2ecf20Sopenharmony_ci len = ops->read(of, buf, len, iocb->ki_pos); 2148c2ecf20Sopenharmony_ci else 2158c2ecf20Sopenharmony_ci len = -EINVAL; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci kernfs_put_active(of->kn); 2188c2ecf20Sopenharmony_ci mutex_unlock(&of->mutex); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci if (len < 0) 2218c2ecf20Sopenharmony_ci goto out_free; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci if (copy_to_iter(buf, len, iter) != len) { 2248c2ecf20Sopenharmony_ci len = -EFAULT; 2258c2ecf20Sopenharmony_ci goto out_free; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci iocb->ki_pos += len; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci out_free: 2318c2ecf20Sopenharmony_ci if (buf == of->prealloc_buf) 2328c2ecf20Sopenharmony_ci mutex_unlock(&of->prealloc_mutex); 2338c2ecf20Sopenharmony_ci else 2348c2ecf20Sopenharmony_ci kfree(buf); 2358c2ecf20Sopenharmony_ci return len; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic ssize_t kernfs_fop_read_iter(struct kiocb *iocb, struct iov_iter *iter) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci if (kernfs_of(iocb->ki_filp)->kn->flags & KERNFS_HAS_SEQ_SHOW) 2418c2ecf20Sopenharmony_ci return seq_read_iter(iocb, iter); 2428c2ecf20Sopenharmony_ci return kernfs_file_read_iter(iocb, iter); 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci/* 2468c2ecf20Sopenharmony_ci * Copy data in from userland and pass it to the matching kernfs write 2478c2ecf20Sopenharmony_ci * operation. 2488c2ecf20Sopenharmony_ci * 2498c2ecf20Sopenharmony_ci * There is no easy way for us to know if userspace is only doing a partial 2508c2ecf20Sopenharmony_ci * write, so we don't support them. We expect the entire buffer to come on 2518c2ecf20Sopenharmony_ci * the first write. Hint: if you're writing a value, first read the file, 2528c2ecf20Sopenharmony_ci * modify only the the value you're changing, then write entire buffer 2538c2ecf20Sopenharmony_ci * back. 2548c2ecf20Sopenharmony_ci */ 2558c2ecf20Sopenharmony_cistatic ssize_t kernfs_fop_write_iter(struct kiocb *iocb, struct iov_iter *iter) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci struct kernfs_open_file *of = kernfs_of(iocb->ki_filp); 2588c2ecf20Sopenharmony_ci ssize_t len = iov_iter_count(iter); 2598c2ecf20Sopenharmony_ci const struct kernfs_ops *ops; 2608c2ecf20Sopenharmony_ci char *buf; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci if (of->atomic_write_len) { 2638c2ecf20Sopenharmony_ci if (len > of->atomic_write_len) 2648c2ecf20Sopenharmony_ci return -E2BIG; 2658c2ecf20Sopenharmony_ci } else { 2668c2ecf20Sopenharmony_ci len = min_t(size_t, len, PAGE_SIZE); 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci buf = of->prealloc_buf; 2708c2ecf20Sopenharmony_ci if (buf) 2718c2ecf20Sopenharmony_ci mutex_lock(&of->prealloc_mutex); 2728c2ecf20Sopenharmony_ci else 2738c2ecf20Sopenharmony_ci buf = kmalloc(len + 1, GFP_KERNEL); 2748c2ecf20Sopenharmony_ci if (!buf) 2758c2ecf20Sopenharmony_ci return -ENOMEM; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (copy_from_iter(buf, len, iter) != len) { 2788c2ecf20Sopenharmony_ci len = -EFAULT; 2798c2ecf20Sopenharmony_ci goto out_free; 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci buf[len] = '\0'; /* guarantee string termination */ 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci /* 2848c2ecf20Sopenharmony_ci * @of->mutex nests outside active ref and is used both to ensure that 2858c2ecf20Sopenharmony_ci * the ops aren't called concurrently for the same open file. 2868c2ecf20Sopenharmony_ci */ 2878c2ecf20Sopenharmony_ci mutex_lock(&of->mutex); 2888c2ecf20Sopenharmony_ci if (!kernfs_get_active(of->kn)) { 2898c2ecf20Sopenharmony_ci mutex_unlock(&of->mutex); 2908c2ecf20Sopenharmony_ci len = -ENODEV; 2918c2ecf20Sopenharmony_ci goto out_free; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci ops = kernfs_ops(of->kn); 2958c2ecf20Sopenharmony_ci if (ops->write) 2968c2ecf20Sopenharmony_ci len = ops->write(of, buf, len, iocb->ki_pos); 2978c2ecf20Sopenharmony_ci else 2988c2ecf20Sopenharmony_ci len = -EINVAL; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci kernfs_put_active(of->kn); 3018c2ecf20Sopenharmony_ci mutex_unlock(&of->mutex); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci if (len > 0) 3048c2ecf20Sopenharmony_ci iocb->ki_pos += len; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ciout_free: 3078c2ecf20Sopenharmony_ci if (buf == of->prealloc_buf) 3088c2ecf20Sopenharmony_ci mutex_unlock(&of->prealloc_mutex); 3098c2ecf20Sopenharmony_ci else 3108c2ecf20Sopenharmony_ci kfree(buf); 3118c2ecf20Sopenharmony_ci return len; 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic void kernfs_vma_open(struct vm_area_struct *vma) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci struct file *file = vma->vm_file; 3178c2ecf20Sopenharmony_ci struct kernfs_open_file *of = kernfs_of(file); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci if (!of->vm_ops) 3208c2ecf20Sopenharmony_ci return; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if (!kernfs_get_active(of->kn)) 3238c2ecf20Sopenharmony_ci return; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (of->vm_ops->open) 3268c2ecf20Sopenharmony_ci of->vm_ops->open(vma); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci kernfs_put_active(of->kn); 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic vm_fault_t kernfs_vma_fault(struct vm_fault *vmf) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci struct file *file = vmf->vma->vm_file; 3348c2ecf20Sopenharmony_ci struct kernfs_open_file *of = kernfs_of(file); 3358c2ecf20Sopenharmony_ci vm_fault_t ret; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci if (!of->vm_ops) 3388c2ecf20Sopenharmony_ci return VM_FAULT_SIGBUS; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci if (!kernfs_get_active(of->kn)) 3418c2ecf20Sopenharmony_ci return VM_FAULT_SIGBUS; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci ret = VM_FAULT_SIGBUS; 3448c2ecf20Sopenharmony_ci if (of->vm_ops->fault) 3458c2ecf20Sopenharmony_ci ret = of->vm_ops->fault(vmf); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci kernfs_put_active(of->kn); 3488c2ecf20Sopenharmony_ci return ret; 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic vm_fault_t kernfs_vma_page_mkwrite(struct vm_fault *vmf) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci struct file *file = vmf->vma->vm_file; 3548c2ecf20Sopenharmony_ci struct kernfs_open_file *of = kernfs_of(file); 3558c2ecf20Sopenharmony_ci vm_fault_t ret; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci if (!of->vm_ops) 3588c2ecf20Sopenharmony_ci return VM_FAULT_SIGBUS; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (!kernfs_get_active(of->kn)) 3618c2ecf20Sopenharmony_ci return VM_FAULT_SIGBUS; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci ret = 0; 3648c2ecf20Sopenharmony_ci if (of->vm_ops->page_mkwrite) 3658c2ecf20Sopenharmony_ci ret = of->vm_ops->page_mkwrite(vmf); 3668c2ecf20Sopenharmony_ci else 3678c2ecf20Sopenharmony_ci file_update_time(file); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci kernfs_put_active(of->kn); 3708c2ecf20Sopenharmony_ci return ret; 3718c2ecf20Sopenharmony_ci} 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_cistatic int kernfs_vma_access(struct vm_area_struct *vma, unsigned long addr, 3748c2ecf20Sopenharmony_ci void *buf, int len, int write) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci struct file *file = vma->vm_file; 3778c2ecf20Sopenharmony_ci struct kernfs_open_file *of = kernfs_of(file); 3788c2ecf20Sopenharmony_ci int ret; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci if (!of->vm_ops) 3818c2ecf20Sopenharmony_ci return -EINVAL; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci if (!kernfs_get_active(of->kn)) 3848c2ecf20Sopenharmony_ci return -EINVAL; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci ret = -EINVAL; 3878c2ecf20Sopenharmony_ci if (of->vm_ops->access) 3888c2ecf20Sopenharmony_ci ret = of->vm_ops->access(vma, addr, buf, len, write); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci kernfs_put_active(of->kn); 3918c2ecf20Sopenharmony_ci return ret; 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci#ifdef CONFIG_NUMA 3958c2ecf20Sopenharmony_cistatic int kernfs_vma_set_policy(struct vm_area_struct *vma, 3968c2ecf20Sopenharmony_ci struct mempolicy *new) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci struct file *file = vma->vm_file; 3998c2ecf20Sopenharmony_ci struct kernfs_open_file *of = kernfs_of(file); 4008c2ecf20Sopenharmony_ci int ret; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci if (!of->vm_ops) 4038c2ecf20Sopenharmony_ci return 0; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci if (!kernfs_get_active(of->kn)) 4068c2ecf20Sopenharmony_ci return -EINVAL; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci ret = 0; 4098c2ecf20Sopenharmony_ci if (of->vm_ops->set_policy) 4108c2ecf20Sopenharmony_ci ret = of->vm_ops->set_policy(vma, new); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci kernfs_put_active(of->kn); 4138c2ecf20Sopenharmony_ci return ret; 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cistatic struct mempolicy *kernfs_vma_get_policy(struct vm_area_struct *vma, 4178c2ecf20Sopenharmony_ci unsigned long addr) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci struct file *file = vma->vm_file; 4208c2ecf20Sopenharmony_ci struct kernfs_open_file *of = kernfs_of(file); 4218c2ecf20Sopenharmony_ci struct mempolicy *pol; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci if (!of->vm_ops) 4248c2ecf20Sopenharmony_ci return vma->vm_policy; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci if (!kernfs_get_active(of->kn)) 4278c2ecf20Sopenharmony_ci return vma->vm_policy; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci pol = vma->vm_policy; 4308c2ecf20Sopenharmony_ci if (of->vm_ops->get_policy) 4318c2ecf20Sopenharmony_ci pol = of->vm_ops->get_policy(vma, addr); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci kernfs_put_active(of->kn); 4348c2ecf20Sopenharmony_ci return pol; 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci#endif 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_cistatic const struct vm_operations_struct kernfs_vm_ops = { 4408c2ecf20Sopenharmony_ci .open = kernfs_vma_open, 4418c2ecf20Sopenharmony_ci .fault = kernfs_vma_fault, 4428c2ecf20Sopenharmony_ci .page_mkwrite = kernfs_vma_page_mkwrite, 4438c2ecf20Sopenharmony_ci .access = kernfs_vma_access, 4448c2ecf20Sopenharmony_ci#ifdef CONFIG_NUMA 4458c2ecf20Sopenharmony_ci .set_policy = kernfs_vma_set_policy, 4468c2ecf20Sopenharmony_ci .get_policy = kernfs_vma_get_policy, 4478c2ecf20Sopenharmony_ci#endif 4488c2ecf20Sopenharmony_ci}; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_cistatic int kernfs_fop_mmap(struct file *file, struct vm_area_struct *vma) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci struct kernfs_open_file *of = kernfs_of(file); 4538c2ecf20Sopenharmony_ci const struct kernfs_ops *ops; 4548c2ecf20Sopenharmony_ci int rc; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci /* 4578c2ecf20Sopenharmony_ci * mmap path and of->mutex are prone to triggering spurious lockdep 4588c2ecf20Sopenharmony_ci * warnings and we don't want to add spurious locking dependency 4598c2ecf20Sopenharmony_ci * between the two. Check whether mmap is actually implemented 4608c2ecf20Sopenharmony_ci * without grabbing @of->mutex by testing HAS_MMAP flag. See the 4618c2ecf20Sopenharmony_ci * comment in kernfs_file_open() for more details. 4628c2ecf20Sopenharmony_ci */ 4638c2ecf20Sopenharmony_ci if (!(of->kn->flags & KERNFS_HAS_MMAP)) 4648c2ecf20Sopenharmony_ci return -ENODEV; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci mutex_lock(&of->mutex); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci rc = -ENODEV; 4698c2ecf20Sopenharmony_ci if (!kernfs_get_active(of->kn)) 4708c2ecf20Sopenharmony_ci goto out_unlock; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci ops = kernfs_ops(of->kn); 4738c2ecf20Sopenharmony_ci rc = ops->mmap(of, vma); 4748c2ecf20Sopenharmony_ci if (rc) 4758c2ecf20Sopenharmony_ci goto out_put; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci /* 4788c2ecf20Sopenharmony_ci * PowerPC's pci_mmap of legacy_mem uses shmem_zero_setup() 4798c2ecf20Sopenharmony_ci * to satisfy versions of X which crash if the mmap fails: that 4808c2ecf20Sopenharmony_ci * substitutes a new vm_file, and we don't then want bin_vm_ops. 4818c2ecf20Sopenharmony_ci */ 4828c2ecf20Sopenharmony_ci if (vma->vm_file != file) 4838c2ecf20Sopenharmony_ci goto out_put; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci rc = -EINVAL; 4868c2ecf20Sopenharmony_ci if (of->mmapped && of->vm_ops != vma->vm_ops) 4878c2ecf20Sopenharmony_ci goto out_put; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci /* 4908c2ecf20Sopenharmony_ci * It is not possible to successfully wrap close. 4918c2ecf20Sopenharmony_ci * So error if someone is trying to use close. 4928c2ecf20Sopenharmony_ci */ 4938c2ecf20Sopenharmony_ci rc = -EINVAL; 4948c2ecf20Sopenharmony_ci if (vma->vm_ops && vma->vm_ops->close) 4958c2ecf20Sopenharmony_ci goto out_put; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci rc = 0; 4988c2ecf20Sopenharmony_ci of->mmapped = true; 4998c2ecf20Sopenharmony_ci of->vm_ops = vma->vm_ops; 5008c2ecf20Sopenharmony_ci vma->vm_ops = &kernfs_vm_ops; 5018c2ecf20Sopenharmony_ciout_put: 5028c2ecf20Sopenharmony_ci kernfs_put_active(of->kn); 5038c2ecf20Sopenharmony_ciout_unlock: 5048c2ecf20Sopenharmony_ci mutex_unlock(&of->mutex); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci return rc; 5078c2ecf20Sopenharmony_ci} 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci/** 5108c2ecf20Sopenharmony_ci * kernfs_get_open_node - get or create kernfs_open_node 5118c2ecf20Sopenharmony_ci * @kn: target kernfs_node 5128c2ecf20Sopenharmony_ci * @of: kernfs_open_file for this instance of open 5138c2ecf20Sopenharmony_ci * 5148c2ecf20Sopenharmony_ci * If @kn->attr.open exists, increment its reference count; otherwise, 5158c2ecf20Sopenharmony_ci * create one. @of is chained to the files list. 5168c2ecf20Sopenharmony_ci * 5178c2ecf20Sopenharmony_ci * LOCKING: 5188c2ecf20Sopenharmony_ci * Kernel thread context (may sleep). 5198c2ecf20Sopenharmony_ci * 5208c2ecf20Sopenharmony_ci * RETURNS: 5218c2ecf20Sopenharmony_ci * 0 on success, -errno on failure. 5228c2ecf20Sopenharmony_ci */ 5238c2ecf20Sopenharmony_cistatic int kernfs_get_open_node(struct kernfs_node *kn, 5248c2ecf20Sopenharmony_ci struct kernfs_open_file *of) 5258c2ecf20Sopenharmony_ci{ 5268c2ecf20Sopenharmony_ci struct kernfs_open_node *on, *new_on = NULL; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci retry: 5298c2ecf20Sopenharmony_ci mutex_lock(&kernfs_open_file_mutex); 5308c2ecf20Sopenharmony_ci spin_lock_irq(&kernfs_open_node_lock); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci if (!kn->attr.open && new_on) { 5338c2ecf20Sopenharmony_ci kn->attr.open = new_on; 5348c2ecf20Sopenharmony_ci new_on = NULL; 5358c2ecf20Sopenharmony_ci } 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci on = kn->attr.open; 5388c2ecf20Sopenharmony_ci if (on) { 5398c2ecf20Sopenharmony_ci atomic_inc(&on->refcnt); 5408c2ecf20Sopenharmony_ci list_add_tail(&of->list, &on->files); 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci spin_unlock_irq(&kernfs_open_node_lock); 5448c2ecf20Sopenharmony_ci mutex_unlock(&kernfs_open_file_mutex); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci if (on) { 5478c2ecf20Sopenharmony_ci kfree(new_on); 5488c2ecf20Sopenharmony_ci return 0; 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci /* not there, initialize a new one and retry */ 5528c2ecf20Sopenharmony_ci new_on = kmalloc(sizeof(*new_on), GFP_KERNEL); 5538c2ecf20Sopenharmony_ci if (!new_on) 5548c2ecf20Sopenharmony_ci return -ENOMEM; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci atomic_set(&new_on->refcnt, 0); 5578c2ecf20Sopenharmony_ci atomic_set(&new_on->event, 1); 5588c2ecf20Sopenharmony_ci init_waitqueue_head(&new_on->poll); 5598c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&new_on->files); 5608c2ecf20Sopenharmony_ci goto retry; 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci/** 5648c2ecf20Sopenharmony_ci * kernfs_put_open_node - put kernfs_open_node 5658c2ecf20Sopenharmony_ci * @kn: target kernfs_nodet 5668c2ecf20Sopenharmony_ci * @of: associated kernfs_open_file 5678c2ecf20Sopenharmony_ci * 5688c2ecf20Sopenharmony_ci * Put @kn->attr.open and unlink @of from the files list. If 5698c2ecf20Sopenharmony_ci * reference count reaches zero, disassociate and free it. 5708c2ecf20Sopenharmony_ci * 5718c2ecf20Sopenharmony_ci * LOCKING: 5728c2ecf20Sopenharmony_ci * None. 5738c2ecf20Sopenharmony_ci */ 5748c2ecf20Sopenharmony_cistatic void kernfs_put_open_node(struct kernfs_node *kn, 5758c2ecf20Sopenharmony_ci struct kernfs_open_file *of) 5768c2ecf20Sopenharmony_ci{ 5778c2ecf20Sopenharmony_ci struct kernfs_open_node *on = kn->attr.open; 5788c2ecf20Sopenharmony_ci unsigned long flags; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci mutex_lock(&kernfs_open_file_mutex); 5818c2ecf20Sopenharmony_ci spin_lock_irqsave(&kernfs_open_node_lock, flags); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci if (of) 5848c2ecf20Sopenharmony_ci list_del(&of->list); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci if (atomic_dec_and_test(&on->refcnt)) 5878c2ecf20Sopenharmony_ci kn->attr.open = NULL; 5888c2ecf20Sopenharmony_ci else 5898c2ecf20Sopenharmony_ci on = NULL; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&kernfs_open_node_lock, flags); 5928c2ecf20Sopenharmony_ci mutex_unlock(&kernfs_open_file_mutex); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci kfree(on); 5958c2ecf20Sopenharmony_ci} 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_cistatic int kernfs_fop_open(struct inode *inode, struct file *file) 5988c2ecf20Sopenharmony_ci{ 5998c2ecf20Sopenharmony_ci struct kernfs_node *kn = inode->i_private; 6008c2ecf20Sopenharmony_ci struct kernfs_root *root = kernfs_root(kn); 6018c2ecf20Sopenharmony_ci const struct kernfs_ops *ops; 6028c2ecf20Sopenharmony_ci struct kernfs_open_file *of; 6038c2ecf20Sopenharmony_ci bool has_read, has_write, has_mmap; 6048c2ecf20Sopenharmony_ci int error = -EACCES; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci if (!kernfs_get_active(kn)) 6078c2ecf20Sopenharmony_ci return -ENODEV; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci ops = kernfs_ops(kn); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci has_read = ops->seq_show || ops->read || ops->mmap; 6128c2ecf20Sopenharmony_ci has_write = ops->write || ops->mmap; 6138c2ecf20Sopenharmony_ci has_mmap = ops->mmap; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci /* see the flag definition for details */ 6168c2ecf20Sopenharmony_ci if (root->flags & KERNFS_ROOT_EXTRA_OPEN_PERM_CHECK) { 6178c2ecf20Sopenharmony_ci if ((file->f_mode & FMODE_WRITE) && 6188c2ecf20Sopenharmony_ci (!(inode->i_mode & S_IWUGO) || !has_write)) 6198c2ecf20Sopenharmony_ci goto err_out; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci if ((file->f_mode & FMODE_READ) && 6228c2ecf20Sopenharmony_ci (!(inode->i_mode & S_IRUGO) || !has_read)) 6238c2ecf20Sopenharmony_ci goto err_out; 6248c2ecf20Sopenharmony_ci } 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci /* allocate a kernfs_open_file for the file */ 6278c2ecf20Sopenharmony_ci error = -ENOMEM; 6288c2ecf20Sopenharmony_ci of = kzalloc(sizeof(struct kernfs_open_file), GFP_KERNEL); 6298c2ecf20Sopenharmony_ci if (!of) 6308c2ecf20Sopenharmony_ci goto err_out; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci /* 6338c2ecf20Sopenharmony_ci * The following is done to give a different lockdep key to 6348c2ecf20Sopenharmony_ci * @of->mutex for files which implement mmap. This is a rather 6358c2ecf20Sopenharmony_ci * crude way to avoid false positive lockdep warning around 6368c2ecf20Sopenharmony_ci * mm->mmap_lock - mmap nests @of->mutex under mm->mmap_lock and 6378c2ecf20Sopenharmony_ci * reading /sys/block/sda/trace/act_mask grabs sr_mutex, under 6388c2ecf20Sopenharmony_ci * which mm->mmap_lock nests, while holding @of->mutex. As each 6398c2ecf20Sopenharmony_ci * open file has a separate mutex, it's okay as long as those don't 6408c2ecf20Sopenharmony_ci * happen on the same file. At this point, we can't easily give 6418c2ecf20Sopenharmony_ci * each file a separate locking class. Let's differentiate on 6428c2ecf20Sopenharmony_ci * whether the file has mmap or not for now. 6438c2ecf20Sopenharmony_ci * 6448c2ecf20Sopenharmony_ci * Both paths of the branch look the same. They're supposed to 6458c2ecf20Sopenharmony_ci * look that way and give @of->mutex different static lockdep keys. 6468c2ecf20Sopenharmony_ci */ 6478c2ecf20Sopenharmony_ci if (has_mmap) 6488c2ecf20Sopenharmony_ci mutex_init(&of->mutex); 6498c2ecf20Sopenharmony_ci else 6508c2ecf20Sopenharmony_ci mutex_init(&of->mutex); 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci of->kn = kn; 6538c2ecf20Sopenharmony_ci of->file = file; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci /* 6568c2ecf20Sopenharmony_ci * Write path needs to atomic_write_len outside active reference. 6578c2ecf20Sopenharmony_ci * Cache it in open_file. See kernfs_fop_write_iter() for details. 6588c2ecf20Sopenharmony_ci */ 6598c2ecf20Sopenharmony_ci of->atomic_write_len = ops->atomic_write_len; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci error = -EINVAL; 6628c2ecf20Sopenharmony_ci /* 6638c2ecf20Sopenharmony_ci * ->seq_show is incompatible with ->prealloc, 6648c2ecf20Sopenharmony_ci * as seq_read does its own allocation. 6658c2ecf20Sopenharmony_ci * ->read must be used instead. 6668c2ecf20Sopenharmony_ci */ 6678c2ecf20Sopenharmony_ci if (ops->prealloc && ops->seq_show) 6688c2ecf20Sopenharmony_ci goto err_free; 6698c2ecf20Sopenharmony_ci if (ops->prealloc) { 6708c2ecf20Sopenharmony_ci int len = of->atomic_write_len ?: PAGE_SIZE; 6718c2ecf20Sopenharmony_ci of->prealloc_buf = kmalloc(len + 1, GFP_KERNEL); 6728c2ecf20Sopenharmony_ci error = -ENOMEM; 6738c2ecf20Sopenharmony_ci if (!of->prealloc_buf) 6748c2ecf20Sopenharmony_ci goto err_free; 6758c2ecf20Sopenharmony_ci mutex_init(&of->prealloc_mutex); 6768c2ecf20Sopenharmony_ci } 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci /* 6798c2ecf20Sopenharmony_ci * Always instantiate seq_file even if read access doesn't use 6808c2ecf20Sopenharmony_ci * seq_file or is not requested. This unifies private data access 6818c2ecf20Sopenharmony_ci * and readable regular files are the vast majority anyway. 6828c2ecf20Sopenharmony_ci */ 6838c2ecf20Sopenharmony_ci if (ops->seq_show) 6848c2ecf20Sopenharmony_ci error = seq_open(file, &kernfs_seq_ops); 6858c2ecf20Sopenharmony_ci else 6868c2ecf20Sopenharmony_ci error = seq_open(file, NULL); 6878c2ecf20Sopenharmony_ci if (error) 6888c2ecf20Sopenharmony_ci goto err_free; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci of->seq_file = file->private_data; 6918c2ecf20Sopenharmony_ci of->seq_file->private = of; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci /* seq_file clears PWRITE unconditionally, restore it if WRITE */ 6948c2ecf20Sopenharmony_ci if (file->f_mode & FMODE_WRITE) 6958c2ecf20Sopenharmony_ci file->f_mode |= FMODE_PWRITE; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci /* make sure we have open node struct */ 6988c2ecf20Sopenharmony_ci error = kernfs_get_open_node(kn, of); 6998c2ecf20Sopenharmony_ci if (error) 7008c2ecf20Sopenharmony_ci goto err_seq_release; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci if (ops->open) { 7038c2ecf20Sopenharmony_ci /* nobody has access to @of yet, skip @of->mutex */ 7048c2ecf20Sopenharmony_ci error = ops->open(of); 7058c2ecf20Sopenharmony_ci if (error) 7068c2ecf20Sopenharmony_ci goto err_put_node; 7078c2ecf20Sopenharmony_ci } 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci /* open succeeded, put active references */ 7108c2ecf20Sopenharmony_ci kernfs_put_active(kn); 7118c2ecf20Sopenharmony_ci return 0; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_cierr_put_node: 7148c2ecf20Sopenharmony_ci kernfs_put_open_node(kn, of); 7158c2ecf20Sopenharmony_cierr_seq_release: 7168c2ecf20Sopenharmony_ci seq_release(inode, file); 7178c2ecf20Sopenharmony_cierr_free: 7188c2ecf20Sopenharmony_ci kfree(of->prealloc_buf); 7198c2ecf20Sopenharmony_ci kfree(of); 7208c2ecf20Sopenharmony_cierr_out: 7218c2ecf20Sopenharmony_ci kernfs_put_active(kn); 7228c2ecf20Sopenharmony_ci return error; 7238c2ecf20Sopenharmony_ci} 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci/* used from release/drain to ensure that ->release() is called exactly once */ 7268c2ecf20Sopenharmony_cistatic void kernfs_release_file(struct kernfs_node *kn, 7278c2ecf20Sopenharmony_ci struct kernfs_open_file *of) 7288c2ecf20Sopenharmony_ci{ 7298c2ecf20Sopenharmony_ci /* 7308c2ecf20Sopenharmony_ci * @of is guaranteed to have no other file operations in flight and 7318c2ecf20Sopenharmony_ci * we just want to synchronize release and drain paths. 7328c2ecf20Sopenharmony_ci * @kernfs_open_file_mutex is enough. @of->mutex can't be used 7338c2ecf20Sopenharmony_ci * here because drain path may be called from places which can 7348c2ecf20Sopenharmony_ci * cause circular dependency. 7358c2ecf20Sopenharmony_ci */ 7368c2ecf20Sopenharmony_ci lockdep_assert_held(&kernfs_open_file_mutex); 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci if (!of->released) { 7398c2ecf20Sopenharmony_ci /* 7408c2ecf20Sopenharmony_ci * A file is never detached without being released and we 7418c2ecf20Sopenharmony_ci * need to be able to release files which are deactivated 7428c2ecf20Sopenharmony_ci * and being drained. Don't use kernfs_ops(). 7438c2ecf20Sopenharmony_ci */ 7448c2ecf20Sopenharmony_ci kn->attr.ops->release(of); 7458c2ecf20Sopenharmony_ci of->released = true; 7468c2ecf20Sopenharmony_ci } 7478c2ecf20Sopenharmony_ci} 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_cistatic int kernfs_fop_release(struct inode *inode, struct file *filp) 7508c2ecf20Sopenharmony_ci{ 7518c2ecf20Sopenharmony_ci struct kernfs_node *kn = inode->i_private; 7528c2ecf20Sopenharmony_ci struct kernfs_open_file *of = kernfs_of(filp); 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci if (kn->flags & KERNFS_HAS_RELEASE) { 7558c2ecf20Sopenharmony_ci mutex_lock(&kernfs_open_file_mutex); 7568c2ecf20Sopenharmony_ci kernfs_release_file(kn, of); 7578c2ecf20Sopenharmony_ci mutex_unlock(&kernfs_open_file_mutex); 7588c2ecf20Sopenharmony_ci } 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci kernfs_put_open_node(kn, of); 7618c2ecf20Sopenharmony_ci seq_release(inode, filp); 7628c2ecf20Sopenharmony_ci kfree(of->prealloc_buf); 7638c2ecf20Sopenharmony_ci kfree(of); 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci return 0; 7668c2ecf20Sopenharmony_ci} 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_civoid kernfs_drain_open_files(struct kernfs_node *kn) 7698c2ecf20Sopenharmony_ci{ 7708c2ecf20Sopenharmony_ci struct kernfs_open_node *on; 7718c2ecf20Sopenharmony_ci struct kernfs_open_file *of; 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci if (!(kn->flags & (KERNFS_HAS_MMAP | KERNFS_HAS_RELEASE))) 7748c2ecf20Sopenharmony_ci return; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci spin_lock_irq(&kernfs_open_node_lock); 7778c2ecf20Sopenharmony_ci on = kn->attr.open; 7788c2ecf20Sopenharmony_ci if (on) 7798c2ecf20Sopenharmony_ci atomic_inc(&on->refcnt); 7808c2ecf20Sopenharmony_ci spin_unlock_irq(&kernfs_open_node_lock); 7818c2ecf20Sopenharmony_ci if (!on) 7828c2ecf20Sopenharmony_ci return; 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci mutex_lock(&kernfs_open_file_mutex); 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci list_for_each_entry(of, &on->files, list) { 7878c2ecf20Sopenharmony_ci struct inode *inode = file_inode(of->file); 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci if (kn->flags & KERNFS_HAS_MMAP) 7908c2ecf20Sopenharmony_ci unmap_mapping_range(inode->i_mapping, 0, 0, 1); 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci if (kn->flags & KERNFS_HAS_RELEASE) 7938c2ecf20Sopenharmony_ci kernfs_release_file(kn, of); 7948c2ecf20Sopenharmony_ci } 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci mutex_unlock(&kernfs_open_file_mutex); 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci kernfs_put_open_node(kn, NULL); 7998c2ecf20Sopenharmony_ci} 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci/* 8028c2ecf20Sopenharmony_ci * Kernfs attribute files are pollable. The idea is that you read 8038c2ecf20Sopenharmony_ci * the content and then you use 'poll' or 'select' to wait for 8048c2ecf20Sopenharmony_ci * the content to change. When the content changes (assuming the 8058c2ecf20Sopenharmony_ci * manager for the kobject supports notification), poll will 8068c2ecf20Sopenharmony_ci * return EPOLLERR|EPOLLPRI, and select will return the fd whether 8078c2ecf20Sopenharmony_ci * it is waiting for read, write, or exceptions. 8088c2ecf20Sopenharmony_ci * Once poll/select indicates that the value has changed, you 8098c2ecf20Sopenharmony_ci * need to close and re-open the file, or seek to 0 and read again. 8108c2ecf20Sopenharmony_ci * Reminder: this only works for attributes which actively support 8118c2ecf20Sopenharmony_ci * it, and it is not possible to test an attribute from userspace 8128c2ecf20Sopenharmony_ci * to see if it supports poll (Neither 'poll' nor 'select' return 8138c2ecf20Sopenharmony_ci * an appropriate error code). When in doubt, set a suitable timeout value. 8148c2ecf20Sopenharmony_ci */ 8158c2ecf20Sopenharmony_ci__poll_t kernfs_generic_poll(struct kernfs_open_file *of, poll_table *wait) 8168c2ecf20Sopenharmony_ci{ 8178c2ecf20Sopenharmony_ci struct kernfs_node *kn = kernfs_dentry_node(of->file->f_path.dentry); 8188c2ecf20Sopenharmony_ci struct kernfs_open_node *on = kn->attr.open; 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci poll_wait(of->file, &on->poll, wait); 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci if (of->event != atomic_read(&on->event)) 8238c2ecf20Sopenharmony_ci return DEFAULT_POLLMASK|EPOLLERR|EPOLLPRI; 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci return DEFAULT_POLLMASK; 8268c2ecf20Sopenharmony_ci} 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_cistatic __poll_t kernfs_fop_poll(struct file *filp, poll_table *wait) 8298c2ecf20Sopenharmony_ci{ 8308c2ecf20Sopenharmony_ci struct kernfs_open_file *of = kernfs_of(filp); 8318c2ecf20Sopenharmony_ci struct kernfs_node *kn = kernfs_dentry_node(filp->f_path.dentry); 8328c2ecf20Sopenharmony_ci __poll_t ret; 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci if (!kernfs_get_active(kn)) 8358c2ecf20Sopenharmony_ci return DEFAULT_POLLMASK|EPOLLERR|EPOLLPRI; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci if (kn->attr.ops->poll) 8388c2ecf20Sopenharmony_ci ret = kn->attr.ops->poll(of, wait); 8398c2ecf20Sopenharmony_ci else 8408c2ecf20Sopenharmony_ci ret = kernfs_generic_poll(of, wait); 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci kernfs_put_active(kn); 8438c2ecf20Sopenharmony_ci return ret; 8448c2ecf20Sopenharmony_ci} 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_cistatic void kernfs_notify_workfn(struct work_struct *work) 8478c2ecf20Sopenharmony_ci{ 8488c2ecf20Sopenharmony_ci struct kernfs_node *kn; 8498c2ecf20Sopenharmony_ci struct kernfs_super_info *info; 8508c2ecf20Sopenharmony_cirepeat: 8518c2ecf20Sopenharmony_ci /* pop one off the notify_list */ 8528c2ecf20Sopenharmony_ci spin_lock_irq(&kernfs_notify_lock); 8538c2ecf20Sopenharmony_ci kn = kernfs_notify_list; 8548c2ecf20Sopenharmony_ci if (kn == KERNFS_NOTIFY_EOL) { 8558c2ecf20Sopenharmony_ci spin_unlock_irq(&kernfs_notify_lock); 8568c2ecf20Sopenharmony_ci return; 8578c2ecf20Sopenharmony_ci } 8588c2ecf20Sopenharmony_ci kernfs_notify_list = kn->attr.notify_next; 8598c2ecf20Sopenharmony_ci kn->attr.notify_next = NULL; 8608c2ecf20Sopenharmony_ci spin_unlock_irq(&kernfs_notify_lock); 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci /* kick fsnotify */ 8638c2ecf20Sopenharmony_ci mutex_lock(&kernfs_mutex); 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci list_for_each_entry(info, &kernfs_root(kn)->supers, node) { 8668c2ecf20Sopenharmony_ci struct kernfs_node *parent; 8678c2ecf20Sopenharmony_ci struct inode *p_inode = NULL; 8688c2ecf20Sopenharmony_ci struct inode *inode; 8698c2ecf20Sopenharmony_ci struct qstr name; 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci /* 8728c2ecf20Sopenharmony_ci * We want fsnotify_modify() on @kn but as the 8738c2ecf20Sopenharmony_ci * modifications aren't originating from userland don't 8748c2ecf20Sopenharmony_ci * have the matching @file available. Look up the inodes 8758c2ecf20Sopenharmony_ci * and generate the events manually. 8768c2ecf20Sopenharmony_ci */ 8778c2ecf20Sopenharmony_ci inode = ilookup(info->sb, kernfs_ino(kn)); 8788c2ecf20Sopenharmony_ci if (!inode) 8798c2ecf20Sopenharmony_ci continue; 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci name = (struct qstr)QSTR_INIT(kn->name, strlen(kn->name)); 8828c2ecf20Sopenharmony_ci parent = kernfs_get_parent(kn); 8838c2ecf20Sopenharmony_ci if (parent) { 8848c2ecf20Sopenharmony_ci p_inode = ilookup(info->sb, kernfs_ino(parent)); 8858c2ecf20Sopenharmony_ci if (p_inode) { 8868c2ecf20Sopenharmony_ci fsnotify(FS_MODIFY | FS_EVENT_ON_CHILD, 8878c2ecf20Sopenharmony_ci inode, FSNOTIFY_EVENT_INODE, 8888c2ecf20Sopenharmony_ci p_inode, &name, inode, 0); 8898c2ecf20Sopenharmony_ci iput(p_inode); 8908c2ecf20Sopenharmony_ci } 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci kernfs_put(parent); 8938c2ecf20Sopenharmony_ci } 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci if (!p_inode) 8968c2ecf20Sopenharmony_ci fsnotify_inode(inode, FS_MODIFY); 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci iput(inode); 8998c2ecf20Sopenharmony_ci } 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci mutex_unlock(&kernfs_mutex); 9028c2ecf20Sopenharmony_ci kernfs_put(kn); 9038c2ecf20Sopenharmony_ci goto repeat; 9048c2ecf20Sopenharmony_ci} 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci/** 9078c2ecf20Sopenharmony_ci * kernfs_notify - notify a kernfs file 9088c2ecf20Sopenharmony_ci * @kn: file to notify 9098c2ecf20Sopenharmony_ci * 9108c2ecf20Sopenharmony_ci * Notify @kn such that poll(2) on @kn wakes up. Maybe be called from any 9118c2ecf20Sopenharmony_ci * context. 9128c2ecf20Sopenharmony_ci */ 9138c2ecf20Sopenharmony_civoid kernfs_notify(struct kernfs_node *kn) 9148c2ecf20Sopenharmony_ci{ 9158c2ecf20Sopenharmony_ci static DECLARE_WORK(kernfs_notify_work, kernfs_notify_workfn); 9168c2ecf20Sopenharmony_ci unsigned long flags; 9178c2ecf20Sopenharmony_ci struct kernfs_open_node *on; 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci if (WARN_ON(kernfs_type(kn) != KERNFS_FILE)) 9208c2ecf20Sopenharmony_ci return; 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci /* kick poll immediately */ 9238c2ecf20Sopenharmony_ci spin_lock_irqsave(&kernfs_open_node_lock, flags); 9248c2ecf20Sopenharmony_ci on = kn->attr.open; 9258c2ecf20Sopenharmony_ci if (on) { 9268c2ecf20Sopenharmony_ci atomic_inc(&on->event); 9278c2ecf20Sopenharmony_ci wake_up_interruptible(&on->poll); 9288c2ecf20Sopenharmony_ci } 9298c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&kernfs_open_node_lock, flags); 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci /* schedule work to kick fsnotify */ 9328c2ecf20Sopenharmony_ci spin_lock_irqsave(&kernfs_notify_lock, flags); 9338c2ecf20Sopenharmony_ci if (!kn->attr.notify_next) { 9348c2ecf20Sopenharmony_ci kernfs_get(kn); 9358c2ecf20Sopenharmony_ci kn->attr.notify_next = kernfs_notify_list; 9368c2ecf20Sopenharmony_ci kernfs_notify_list = kn; 9378c2ecf20Sopenharmony_ci schedule_work(&kernfs_notify_work); 9388c2ecf20Sopenharmony_ci } 9398c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&kernfs_notify_lock, flags); 9408c2ecf20Sopenharmony_ci} 9418c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(kernfs_notify); 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ciconst struct file_operations kernfs_file_fops = { 9448c2ecf20Sopenharmony_ci .read_iter = kernfs_fop_read_iter, 9458c2ecf20Sopenharmony_ci .write_iter = kernfs_fop_write_iter, 9468c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 9478c2ecf20Sopenharmony_ci .mmap = kernfs_fop_mmap, 9488c2ecf20Sopenharmony_ci .open = kernfs_fop_open, 9498c2ecf20Sopenharmony_ci .release = kernfs_fop_release, 9508c2ecf20Sopenharmony_ci .poll = kernfs_fop_poll, 9518c2ecf20Sopenharmony_ci .fsync = noop_fsync, 9528c2ecf20Sopenharmony_ci .splice_read = generic_file_splice_read, 9538c2ecf20Sopenharmony_ci .splice_write = iter_file_splice_write, 9548c2ecf20Sopenharmony_ci}; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci/** 9578c2ecf20Sopenharmony_ci * __kernfs_create_file - kernfs internal function to create a file 9588c2ecf20Sopenharmony_ci * @parent: directory to create the file in 9598c2ecf20Sopenharmony_ci * @name: name of the file 9608c2ecf20Sopenharmony_ci * @mode: mode of the file 9618c2ecf20Sopenharmony_ci * @uid: uid of the file 9628c2ecf20Sopenharmony_ci * @gid: gid of the file 9638c2ecf20Sopenharmony_ci * @size: size of the file 9648c2ecf20Sopenharmony_ci * @ops: kernfs operations for the file 9658c2ecf20Sopenharmony_ci * @priv: private data for the file 9668c2ecf20Sopenharmony_ci * @ns: optional namespace tag of the file 9678c2ecf20Sopenharmony_ci * @key: lockdep key for the file's active_ref, %NULL to disable lockdep 9688c2ecf20Sopenharmony_ci * 9698c2ecf20Sopenharmony_ci * Returns the created node on success, ERR_PTR() value on error. 9708c2ecf20Sopenharmony_ci */ 9718c2ecf20Sopenharmony_cistruct kernfs_node *__kernfs_create_file(struct kernfs_node *parent, 9728c2ecf20Sopenharmony_ci const char *name, 9738c2ecf20Sopenharmony_ci umode_t mode, kuid_t uid, kgid_t gid, 9748c2ecf20Sopenharmony_ci loff_t size, 9758c2ecf20Sopenharmony_ci const struct kernfs_ops *ops, 9768c2ecf20Sopenharmony_ci void *priv, const void *ns, 9778c2ecf20Sopenharmony_ci struct lock_class_key *key) 9788c2ecf20Sopenharmony_ci{ 9798c2ecf20Sopenharmony_ci struct kernfs_node *kn; 9808c2ecf20Sopenharmony_ci unsigned flags; 9818c2ecf20Sopenharmony_ci int rc; 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci flags = KERNFS_FILE; 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci kn = kernfs_new_node(parent, name, (mode & S_IALLUGO) | S_IFREG, 9868c2ecf20Sopenharmony_ci uid, gid, flags); 9878c2ecf20Sopenharmony_ci if (!kn) 9888c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci kn->attr.ops = ops; 9918c2ecf20Sopenharmony_ci kn->attr.size = size; 9928c2ecf20Sopenharmony_ci kn->ns = ns; 9938c2ecf20Sopenharmony_ci kn->priv = priv; 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_LOCK_ALLOC 9968c2ecf20Sopenharmony_ci if (key) { 9978c2ecf20Sopenharmony_ci lockdep_init_map(&kn->dep_map, "kn->active", key, 0); 9988c2ecf20Sopenharmony_ci kn->flags |= KERNFS_LOCKDEP; 9998c2ecf20Sopenharmony_ci } 10008c2ecf20Sopenharmony_ci#endif 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci /* 10038c2ecf20Sopenharmony_ci * kn->attr.ops is accesible only while holding active ref. We 10048c2ecf20Sopenharmony_ci * need to know whether some ops are implemented outside active 10058c2ecf20Sopenharmony_ci * ref. Cache their existence in flags. 10068c2ecf20Sopenharmony_ci */ 10078c2ecf20Sopenharmony_ci if (ops->seq_show) 10088c2ecf20Sopenharmony_ci kn->flags |= KERNFS_HAS_SEQ_SHOW; 10098c2ecf20Sopenharmony_ci if (ops->mmap) 10108c2ecf20Sopenharmony_ci kn->flags |= KERNFS_HAS_MMAP; 10118c2ecf20Sopenharmony_ci if (ops->release) 10128c2ecf20Sopenharmony_ci kn->flags |= KERNFS_HAS_RELEASE; 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci rc = kernfs_add_one(kn); 10158c2ecf20Sopenharmony_ci if (rc) { 10168c2ecf20Sopenharmony_ci kernfs_put(kn); 10178c2ecf20Sopenharmony_ci return ERR_PTR(rc); 10188c2ecf20Sopenharmony_ci } 10198c2ecf20Sopenharmony_ci return kn; 10208c2ecf20Sopenharmony_ci} 1021