mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-24 19:28:53 +03:00
OpenZFS restructuring - move platform specific sources
Move platform specific Linux source under module/os/linux/
and update the build system accordingly. Additional code
restructuring will follow to make the common code fully
portable.
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Reviewed-by: Igor Kozhukhov <igor@dilos.org>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Matthew Macy <mmacy@FreeBSD.org>
Closes #9206
This commit is contained in:
committed by
Brian Behlendorf
parent
870e7a52c1
commit
bced7e3aaa
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2018 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <sys/list.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/procfs_list.h>
|
||||
#include <linux/proc_fs.h>
|
||||
|
||||
/*
|
||||
* A procfs_list is a wrapper around a linked list which implements the seq_file
|
||||
* interface, allowing the contents of the list to be exposed through procfs.
|
||||
* The kernel already has some utilities to help implement the seq_file
|
||||
* interface for linked lists (seq_list_*), but they aren't appropriate for use
|
||||
* with lists that have many entries, because seq_list_start walks the list at
|
||||
* the start of each read syscall to find where it left off, so reading a file
|
||||
* ends up being quadratic in the number of entries in the list.
|
||||
*
|
||||
* This implementation avoids this penalty by maintaining a separate cursor into
|
||||
* the list per instance of the file that is open. It also maintains some extra
|
||||
* information in each node of the list to prevent reads of entries that have
|
||||
* been dropped from the list.
|
||||
*
|
||||
* Callers should only add elements to the list using procfs_list_add, which
|
||||
* adds an element to the tail of the list. Other operations can be performed
|
||||
* directly on the wrapped list using the normal list manipulation functions,
|
||||
* but elements should only be removed from the head of the list.
|
||||
*/
|
||||
|
||||
#define NODE_ID(procfs_list, obj) \
|
||||
(((procfs_list_node_t *)(((char *)obj) + \
|
||||
(procfs_list)->pl_node_offset))->pln_id)
|
||||
|
||||
typedef struct procfs_list_cursor {
|
||||
procfs_list_t *procfs_list; /* List into which this cursor points */
|
||||
void *cached_node; /* Most recently accessed node */
|
||||
loff_t cached_pos; /* Position of cached_node */
|
||||
} procfs_list_cursor_t;
|
||||
|
||||
static int
|
||||
procfs_list_seq_show(struct seq_file *f, void *p)
|
||||
{
|
||||
procfs_list_cursor_t *cursor = f->private;
|
||||
procfs_list_t *procfs_list = cursor->procfs_list;
|
||||
|
||||
ASSERT(MUTEX_HELD(&procfs_list->pl_lock));
|
||||
if (p == SEQ_START_TOKEN) {
|
||||
if (procfs_list->pl_show_header != NULL)
|
||||
return (procfs_list->pl_show_header(f));
|
||||
else
|
||||
return (0);
|
||||
}
|
||||
return (procfs_list->pl_show(f, p));
|
||||
}
|
||||
|
||||
static void *
|
||||
procfs_list_next_node(procfs_list_cursor_t *cursor, loff_t *pos)
|
||||
{
|
||||
void *next_node;
|
||||
procfs_list_t *procfs_list = cursor->procfs_list;
|
||||
|
||||
if (cursor->cached_node == SEQ_START_TOKEN)
|
||||
next_node = list_head(&procfs_list->pl_list);
|
||||
else
|
||||
next_node = list_next(&procfs_list->pl_list,
|
||||
cursor->cached_node);
|
||||
|
||||
if (next_node != NULL) {
|
||||
cursor->cached_node = next_node;
|
||||
cursor->cached_pos = NODE_ID(procfs_list, cursor->cached_node);
|
||||
*pos = cursor->cached_pos;
|
||||
}
|
||||
return (next_node);
|
||||
}
|
||||
|
||||
static void *
|
||||
procfs_list_seq_start(struct seq_file *f, loff_t *pos)
|
||||
{
|
||||
procfs_list_cursor_t *cursor = f->private;
|
||||
procfs_list_t *procfs_list = cursor->procfs_list;
|
||||
|
||||
mutex_enter(&procfs_list->pl_lock);
|
||||
|
||||
if (*pos == 0) {
|
||||
cursor->cached_node = SEQ_START_TOKEN;
|
||||
cursor->cached_pos = 0;
|
||||
return (SEQ_START_TOKEN);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if our cached pointer has become stale, which happens if the
|
||||
* the message where we left off has been dropped from the list since
|
||||
* the last read syscall completed.
|
||||
*/
|
||||
void *oldest_node = list_head(&procfs_list->pl_list);
|
||||
if (cursor->cached_node != SEQ_START_TOKEN && (oldest_node == NULL ||
|
||||
NODE_ID(procfs_list, oldest_node) > cursor->cached_pos))
|
||||
return (ERR_PTR(-EIO));
|
||||
|
||||
/*
|
||||
* If it isn't starting from the beginning of the file, the seq_file
|
||||
* code will either pick up at the same position it visited last or the
|
||||
* following one.
|
||||
*/
|
||||
if (*pos == cursor->cached_pos) {
|
||||
return (cursor->cached_node);
|
||||
} else {
|
||||
ASSERT3U(*pos, ==, cursor->cached_pos + 1);
|
||||
return (procfs_list_next_node(cursor, pos));
|
||||
}
|
||||
}
|
||||
|
||||
static void *
|
||||
procfs_list_seq_next(struct seq_file *f, void *p, loff_t *pos)
|
||||
{
|
||||
procfs_list_cursor_t *cursor = f->private;
|
||||
ASSERT(MUTEX_HELD(&cursor->procfs_list->pl_lock));
|
||||
return (procfs_list_next_node(cursor, pos));
|
||||
}
|
||||
|
||||
static void
|
||||
procfs_list_seq_stop(struct seq_file *f, void *p)
|
||||
{
|
||||
procfs_list_cursor_t *cursor = f->private;
|
||||
procfs_list_t *procfs_list = cursor->procfs_list;
|
||||
mutex_exit(&procfs_list->pl_lock);
|
||||
}
|
||||
|
||||
static struct seq_operations procfs_list_seq_ops = {
|
||||
.show = procfs_list_seq_show,
|
||||
.start = procfs_list_seq_start,
|
||||
.next = procfs_list_seq_next,
|
||||
.stop = procfs_list_seq_stop,
|
||||
};
|
||||
|
||||
static int
|
||||
procfs_list_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
int rc = seq_open_private(filp, &procfs_list_seq_ops,
|
||||
sizeof (procfs_list_cursor_t));
|
||||
if (rc != 0)
|
||||
return (rc);
|
||||
|
||||
struct seq_file *f = filp->private_data;
|
||||
procfs_list_cursor_t *cursor = f->private;
|
||||
cursor->procfs_list = PDE_DATA(inode);
|
||||
cursor->cached_node = NULL;
|
||||
cursor->cached_pos = 0;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
procfs_list_write(struct file *filp, const char __user *buf, size_t len,
|
||||
loff_t *ppos)
|
||||
{
|
||||
struct seq_file *f = filp->private_data;
|
||||
procfs_list_cursor_t *cursor = f->private;
|
||||
procfs_list_t *procfs_list = cursor->procfs_list;
|
||||
int rc;
|
||||
|
||||
if (procfs_list->pl_clear != NULL &&
|
||||
(rc = procfs_list->pl_clear(procfs_list)) != 0)
|
||||
return (-rc);
|
||||
return (len);
|
||||
}
|
||||
|
||||
static struct file_operations procfs_list_operations = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = procfs_list_open,
|
||||
.write = procfs_list_write,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release_private,
|
||||
};
|
||||
|
||||
/*
|
||||
* Initialize a procfs_list and create a file for it in the proc filesystem
|
||||
* under the kstat namespace.
|
||||
*/
|
||||
void
|
||||
procfs_list_install(const char *module,
|
||||
const char *name,
|
||||
mode_t mode,
|
||||
procfs_list_t *procfs_list,
|
||||
int (*show)(struct seq_file *f, void *p),
|
||||
int (*show_header)(struct seq_file *f),
|
||||
int (*clear)(procfs_list_t *procfs_list),
|
||||
size_t procfs_list_node_off)
|
||||
{
|
||||
mutex_init(&procfs_list->pl_lock, NULL, MUTEX_DEFAULT, NULL);
|
||||
list_create(&procfs_list->pl_list,
|
||||
procfs_list_node_off + sizeof (procfs_list_node_t),
|
||||
procfs_list_node_off + offsetof(procfs_list_node_t, pln_link));
|
||||
procfs_list->pl_next_id = 1; /* Save id 0 for SEQ_START_TOKEN */
|
||||
procfs_list->pl_show = show;
|
||||
procfs_list->pl_show_header = show_header;
|
||||
procfs_list->pl_clear = clear;
|
||||
procfs_list->pl_node_offset = procfs_list_node_off;
|
||||
|
||||
kstat_proc_entry_init(&procfs_list->pl_kstat_entry, module, name);
|
||||
kstat_proc_entry_install(&procfs_list->pl_kstat_entry, mode,
|
||||
&procfs_list_operations, procfs_list);
|
||||
}
|
||||
EXPORT_SYMBOL(procfs_list_install);
|
||||
|
||||
/* Remove the proc filesystem file corresponding to the given list */
|
||||
void
|
||||
procfs_list_uninstall(procfs_list_t *procfs_list)
|
||||
{
|
||||
kstat_proc_entry_delete(&procfs_list->pl_kstat_entry);
|
||||
}
|
||||
EXPORT_SYMBOL(procfs_list_uninstall);
|
||||
|
||||
void
|
||||
procfs_list_destroy(procfs_list_t *procfs_list)
|
||||
{
|
||||
ASSERT(list_is_empty(&procfs_list->pl_list));
|
||||
list_destroy(&procfs_list->pl_list);
|
||||
mutex_destroy(&procfs_list->pl_lock);
|
||||
}
|
||||
EXPORT_SYMBOL(procfs_list_destroy);
|
||||
|
||||
/*
|
||||
* Add a new node to the tail of the list. While the standard list manipulation
|
||||
* functions can be use for all other operation, adding elements to the list
|
||||
* should only be done using this helper so that the id of the new node is set
|
||||
* correctly.
|
||||
*/
|
||||
void
|
||||
procfs_list_add(procfs_list_t *procfs_list, void *p)
|
||||
{
|
||||
ASSERT(MUTEX_HELD(&procfs_list->pl_lock));
|
||||
NODE_ID(procfs_list, p) = procfs_list->pl_next_id++;
|
||||
list_insert_tail(&procfs_list->pl_list, p);
|
||||
}
|
||||
EXPORT_SYMBOL(procfs_list_add);
|
||||
Reference in New Issue
Block a user