The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/contrib/openzfs/module/os/linux/spl/spl-procfs-list.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*
    2  * CDDL HEADER START
    3  *
    4  * The contents of this file are subject to the terms of the
    5  * Common Development and Distribution License (the "License").
    6  * You may not use this file except in compliance with the License.
    7  *
    8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
    9  * or https://opensource.org/licenses/CDDL-1.0.
   10  * See the License for the specific language governing permissions
   11  * and limitations under the License.
   12  *
   13  * When distributing Covered Code, include this CDDL HEADER in each
   14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
   15  * If applicable, add the following below this CDDL HEADER, with the
   16  * fields enclosed by brackets "[]" replaced with your own identifying
   17  * information: Portions Copyright [yyyy] [name of copyright owner]
   18  *
   19  * CDDL HEADER END
   20  */
   21 /*
   22  * Copyright (c) 2018 by Delphix. All rights reserved.
   23  */
   24 
   25 #include <sys/list.h>
   26 #include <sys/procfs_list.h>
   27 #include <linux/proc_fs.h>
   28 #include <sys/mutex.h>
   29 
   30 /*
   31  * A procfs_list is a wrapper around a linked list which implements the seq_file
   32  * interface, allowing the contents of the list to be exposed through procfs.
   33  * The kernel already has some utilities to help implement the seq_file
   34  * interface for linked lists (seq_list_*), but they aren't appropriate for use
   35  * with lists that have many entries, because seq_list_start walks the list at
   36  * the start of each read syscall to find where it left off, so reading a file
   37  * ends up being quadratic in the number of entries in the list.
   38  *
   39  * This implementation avoids this penalty by maintaining a separate cursor into
   40  * the list per instance of the file that is open. It also maintains some extra
   41  * information in each node of the list to prevent reads of entries that have
   42  * been dropped from the list.
   43  *
   44  * Callers should only add elements to the list using procfs_list_add, which
   45  * adds an element to the tail of the list. Other operations can be performed
   46  * directly on the wrapped list using the normal list manipulation functions,
   47  * but elements should only be removed from the head of the list.
   48  */
   49 
   50 #define NODE_ID(procfs_list, obj) \
   51                 (((procfs_list_node_t *)(((char *)obj) + \
   52                 (procfs_list)->pl_node_offset))->pln_id)
   53 
   54 typedef struct procfs_list_cursor {
   55         procfs_list_t   *procfs_list;   /* List into which this cursor points */
   56         void            *cached_node;   /* Most recently accessed node */
   57         loff_t          cached_pos;     /* Position of cached_node */
   58 } procfs_list_cursor_t;
   59 
   60 static int
   61 procfs_list_seq_show(struct seq_file *f, void *p)
   62 {
   63         procfs_list_cursor_t *cursor = f->private;
   64         procfs_list_t *procfs_list = cursor->procfs_list;
   65 
   66         ASSERT(MUTEX_HELD(&procfs_list->pl_lock));
   67         if (p == SEQ_START_TOKEN) {
   68                 if (procfs_list->pl_show_header != NULL)
   69                         return (procfs_list->pl_show_header(f));
   70                 else
   71                         return (0);
   72         }
   73         return (procfs_list->pl_show(f, p));
   74 }
   75 
   76 static void *
   77 procfs_list_next_node(procfs_list_cursor_t *cursor, loff_t *pos)
   78 {
   79         void *next_node;
   80         procfs_list_t *procfs_list = cursor->procfs_list;
   81 
   82         if (cursor->cached_node == SEQ_START_TOKEN)
   83                 next_node = list_head(&procfs_list->pl_list);
   84         else
   85                 next_node = list_next(&procfs_list->pl_list,
   86                     cursor->cached_node);
   87 
   88         if (next_node != NULL) {
   89                 cursor->cached_node = next_node;
   90                 cursor->cached_pos = NODE_ID(procfs_list, cursor->cached_node);
   91                 *pos = cursor->cached_pos;
   92         } else {
   93                 /*
   94                  * seq_read() expects ->next() to update the position even
   95                  * when there are no more entries. Advance the position to
   96                  * prevent a warning from being logged.
   97                  */
   98                 cursor->cached_node = NULL;
   99                 cursor->cached_pos++;
  100                 *pos = cursor->cached_pos;
  101         }
  102 
  103         return (next_node);
  104 }
  105 
  106 static void *
  107 procfs_list_seq_start(struct seq_file *f, loff_t *pos)
  108 {
  109         procfs_list_cursor_t *cursor = f->private;
  110         procfs_list_t *procfs_list = cursor->procfs_list;
  111 
  112         mutex_enter(&procfs_list->pl_lock);
  113 
  114         if (*pos == 0) {
  115                 cursor->cached_node = SEQ_START_TOKEN;
  116                 cursor->cached_pos = 0;
  117                 return (SEQ_START_TOKEN);
  118         } else if (cursor->cached_node == NULL) {
  119                 return (NULL);
  120         }
  121 
  122         /*
  123          * Check if our cached pointer has become stale, which happens if the
  124          * the message where we left off has been dropped from the list since
  125          * the last read syscall completed.
  126          */
  127         void *oldest_node = list_head(&procfs_list->pl_list);
  128         if (cursor->cached_node != SEQ_START_TOKEN && (oldest_node == NULL ||
  129             NODE_ID(procfs_list, oldest_node) > cursor->cached_pos))
  130                 return (ERR_PTR(-EIO));
  131 
  132         /*
  133          * If it isn't starting from the beginning of the file, the seq_file
  134          * code will either pick up at the same position it visited last or the
  135          * following one.
  136          */
  137         if (*pos == cursor->cached_pos) {
  138                 return (cursor->cached_node);
  139         } else {
  140                 ASSERT3U(*pos, ==, cursor->cached_pos + 1);
  141                 return (procfs_list_next_node(cursor, pos));
  142         }
  143 }
  144 
  145 static void *
  146 procfs_list_seq_next(struct seq_file *f, void *p, loff_t *pos)
  147 {
  148         procfs_list_cursor_t *cursor = f->private;
  149         ASSERT(MUTEX_HELD(&cursor->procfs_list->pl_lock));
  150         return (procfs_list_next_node(cursor, pos));
  151 }
  152 
  153 static void
  154 procfs_list_seq_stop(struct seq_file *f, void *p)
  155 {
  156         procfs_list_cursor_t *cursor = f->private;
  157         procfs_list_t *procfs_list = cursor->procfs_list;
  158         mutex_exit(&procfs_list->pl_lock);
  159 }
  160 
  161 static const struct seq_operations procfs_list_seq_ops = {
  162         .show  = procfs_list_seq_show,
  163         .start = procfs_list_seq_start,
  164         .next  = procfs_list_seq_next,
  165         .stop  = procfs_list_seq_stop,
  166 };
  167 
  168 static int
  169 procfs_list_open(struct inode *inode, struct file *filp)
  170 {
  171         int rc = seq_open_private(filp, &procfs_list_seq_ops,
  172             sizeof (procfs_list_cursor_t));
  173         if (rc != 0)
  174                 return (rc);
  175 
  176         struct seq_file *f = filp->private_data;
  177         procfs_list_cursor_t *cursor = f->private;
  178         cursor->procfs_list = SPL_PDE_DATA(inode);
  179         cursor->cached_node = NULL;
  180         cursor->cached_pos = 0;
  181 
  182         return (0);
  183 }
  184 
  185 static ssize_t
  186 procfs_list_write(struct file *filp, const char __user *buf, size_t len,
  187     loff_t *ppos)
  188 {
  189         struct seq_file *f = filp->private_data;
  190         procfs_list_cursor_t *cursor = f->private;
  191         procfs_list_t *procfs_list = cursor->procfs_list;
  192         int rc;
  193 
  194         if (procfs_list->pl_clear != NULL &&
  195             (rc = procfs_list->pl_clear(procfs_list)) != 0)
  196                 return (-rc);
  197         return (len);
  198 }
  199 
  200 static const kstat_proc_op_t procfs_list_operations = {
  201 #ifdef HAVE_PROC_OPS_STRUCT
  202         .proc_open      = procfs_list_open,
  203         .proc_write     = procfs_list_write,
  204         .proc_read      = seq_read,
  205         .proc_lseek     = seq_lseek,
  206         .proc_release   = seq_release_private,
  207 #else
  208         .open           = procfs_list_open,
  209         .write          = procfs_list_write,
  210         .read           = seq_read,
  211         .llseek         = seq_lseek,
  212         .release        = seq_release_private,
  213 #endif
  214 };
  215 
  216 /*
  217  * Initialize a procfs_list and create a file for it in the proc filesystem
  218  * under the kstat namespace.
  219  */
  220 void
  221 procfs_list_install(const char *module,
  222     const char *submodule,
  223     const char *name,
  224     mode_t mode,
  225     procfs_list_t *procfs_list,
  226     int (*show)(struct seq_file *f, void *p),
  227     int (*show_header)(struct seq_file *f),
  228     int (*clear)(procfs_list_t *procfs_list),
  229     size_t procfs_list_node_off)
  230 {
  231         char *modulestr;
  232 
  233         if (submodule != NULL)
  234                 modulestr = kmem_asprintf("%s/%s", module, submodule);
  235         else
  236                 modulestr = kmem_asprintf("%s", module);
  237         mutex_init(&procfs_list->pl_lock, NULL, MUTEX_DEFAULT, NULL);
  238         list_create(&procfs_list->pl_list,
  239             procfs_list_node_off + sizeof (procfs_list_node_t),
  240             procfs_list_node_off + offsetof(procfs_list_node_t, pln_link));
  241         procfs_list->pl_next_id = 1; /* Save id 0 for SEQ_START_TOKEN */
  242         procfs_list->pl_show = show;
  243         procfs_list->pl_show_header = show_header;
  244         procfs_list->pl_clear = clear;
  245         procfs_list->pl_node_offset = procfs_list_node_off;
  246 
  247         kstat_proc_entry_init(&procfs_list->pl_kstat_entry, modulestr, name);
  248         kstat_proc_entry_install(&procfs_list->pl_kstat_entry, mode,
  249             &procfs_list_operations, procfs_list);
  250         kmem_strfree(modulestr);
  251 }
  252 EXPORT_SYMBOL(procfs_list_install);
  253 
  254 /* Remove the proc filesystem file corresponding to the given list */
  255 void
  256 procfs_list_uninstall(procfs_list_t *procfs_list)
  257 {
  258         kstat_proc_entry_delete(&procfs_list->pl_kstat_entry);
  259 }
  260 EXPORT_SYMBOL(procfs_list_uninstall);
  261 
  262 void
  263 procfs_list_destroy(procfs_list_t *procfs_list)
  264 {
  265         ASSERT(list_is_empty(&procfs_list->pl_list));
  266         list_destroy(&procfs_list->pl_list);
  267         mutex_destroy(&procfs_list->pl_lock);
  268 }
  269 EXPORT_SYMBOL(procfs_list_destroy);
  270 
  271 /*
  272  * Add a new node to the tail of the list. While the standard list manipulation
  273  * functions can be use for all other operation, adding elements to the list
  274  * should only be done using this helper so that the id of the new node is set
  275  * correctly.
  276  */
  277 void
  278 procfs_list_add(procfs_list_t *procfs_list, void *p)
  279 {
  280         ASSERT(MUTEX_HELD(&procfs_list->pl_lock));
  281         NODE_ID(procfs_list, p) = procfs_list->pl_next_id++;
  282         list_insert_tail(&procfs_list->pl_list, p);
  283 }
  284 EXPORT_SYMBOL(procfs_list_add);

Cache object: 32c96e7a750018b2e3544531c8abc30c


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.