2cbb06b561
Originally when the ARC prune callback was introduced the idea was
to register a single callback for the ZPL. The ARC could invoke this
call back if it needed the ZPL to drop dentries, inodes, or other
cache objects which might be pinning buffers in the ARC. The ZPL
would iterate over all ZFS super blocks and perform the reclaim.
For the most part this design has worked well but due to limitations
in 2.6.35 and earlier kernels there were some problems. This patch
is designed to address those issues.
1) iterate_supers_type() is not provided by all kernels which makes
it impossible to safely iterate over all zpl_fs_type filesystems in
a single callback. The most straight forward and portable way to
resolve this is to register a callback per-filesystem during mount.
The arc_*_prune_callback() functions have always supported multiple
callbacks so this is functionally a very small change.
2) Commit 050d22b
removed the non-portable shrink_dcache_memory()
and shrink_icache_memory() functions and didn't replace them with
equivalent functionality. This meant that for Linux 3.1 and older
kernels the ARC had no mechanism to drop dentries and inodes from
the caches if needed. This patch adds that missing functionality
by calling shrink_dcache_parent() to release dentries which may be
pinning inodes. This will result in all unused cache entries being
dropped which is a bit heavy handed but it's the only interface
available for old kernels.
3) A zpl_drop_inode() callback is registered for kernels older than
2.6.35 which do not support the .evict_inode callback. This ensures
that when the last reference on an inode is dropped it is immediately
removed from the cache. If this isn't done than inode can end up on
the global unused LRU with no mechanism available to ZFS to drop them.
Since the ARC buffers are not dropped the hottest inodes can still
be recreated without performing disk IO.
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Pavel Snajdr <snajpa@snajpa.net>
Issue #3160
190 lines
5.6 KiB
C
190 lines
5.6 KiB
C
/*
|
|
* 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) 2011, Lawrence Livermore National Security, LLC.
|
|
*/
|
|
|
|
#ifndef _SYS_ZPL_H
|
|
#define _SYS_ZPL_H
|
|
|
|
#include <sys/vfs.h>
|
|
#include <linux/aio.h>
|
|
#include <linux/dcache_compat.h>
|
|
#include <linux/exportfs.h>
|
|
#include <linux/falloc.h>
|
|
#include <linux/file_compat.h>
|
|
#include <linux/task_io_accounting_ops.h>
|
|
#include <linux/vfs_compat.h>
|
|
#include <linux/writeback.h>
|
|
#include <linux/xattr_compat.h>
|
|
|
|
/* zpl_inode.c */
|
|
extern void zpl_vap_init(vattr_t *vap, struct inode *dir,
|
|
zpl_umode_t mode, cred_t *cr);
|
|
|
|
extern const struct inode_operations zpl_inode_operations;
|
|
extern const struct inode_operations zpl_dir_inode_operations;
|
|
extern const struct inode_operations zpl_symlink_inode_operations;
|
|
extern const struct inode_operations zpl_special_inode_operations;
|
|
extern dentry_operations_t zpl_dentry_operations;
|
|
|
|
/* zpl_file.c */
|
|
extern ssize_t zpl_read_common(struct inode *ip, const char *buf,
|
|
size_t len, loff_t *ppos, uio_seg_t segment, int flags,
|
|
cred_t *cr);
|
|
extern ssize_t zpl_write_common(struct inode *ip, const char *buf,
|
|
size_t len, loff_t *ppos, uio_seg_t segment, int flags,
|
|
cred_t *cr);
|
|
#if defined(HAVE_FILE_FALLOCATE) || defined(HAVE_INODE_FALLOCATE)
|
|
extern long zpl_fallocate_common(struct inode *ip, int mode,
|
|
loff_t offset, loff_t len);
|
|
#endif /* defined(HAVE_FILE_FALLOCATE) || defined(HAVE_INODE_FALLOCATE) */
|
|
|
|
extern const struct address_space_operations zpl_address_space_operations;
|
|
extern const struct file_operations zpl_file_operations;
|
|
extern const struct file_operations zpl_dir_file_operations;
|
|
|
|
/* zpl_super.c */
|
|
extern void zpl_prune_sb(int64_t nr_to_scan, void *arg);
|
|
|
|
typedef struct zpl_mount_data {
|
|
const char *z_osname; /* Dataset name */
|
|
void *z_data; /* Mount options string */
|
|
} zpl_mount_data_t;
|
|
|
|
extern const struct super_operations zpl_super_operations;
|
|
extern const struct export_operations zpl_export_operations;
|
|
extern struct file_system_type zpl_fs_type;
|
|
|
|
/* zpl_xattr.c */
|
|
extern ssize_t zpl_xattr_list(struct dentry *dentry, char *buf, size_t size);
|
|
extern int zpl_xattr_security_init(struct inode *ip, struct inode *dip,
|
|
const struct qstr *qstr);
|
|
#if defined(CONFIG_FS_POSIX_ACL)
|
|
extern int zpl_set_acl(struct inode *ip, int type, struct posix_acl *acl);
|
|
extern struct posix_acl *zpl_get_acl(struct inode *ip, int type);
|
|
#if !defined(HAVE_GET_ACL)
|
|
#if defined(HAVE_CHECK_ACL_WITH_FLAGS)
|
|
extern int zpl_check_acl(struct inode *inode, int mask, unsigned int flags);
|
|
#elif defined(HAVE_CHECK_ACL)
|
|
extern int zpl_check_acl(struct inode *inode, int mask);
|
|
#elif defined(HAVE_PERMISSION_WITH_NAMEIDATA)
|
|
extern int zpl_permission(struct inode *ip, int mask, struct nameidata *nd);
|
|
#elif defined(HAVE_PERMISSION)
|
|
extern int zpl_permission(struct inode *ip, int mask);
|
|
#endif /* HAVE_CHECK_ACL | HAVE_PERMISSION */
|
|
#endif /* HAVE_GET_ACL */
|
|
|
|
extern int zpl_init_acl(struct inode *ip, struct inode *dir);
|
|
extern int zpl_chmod_acl(struct inode *ip);
|
|
#else
|
|
static inline int
|
|
zpl_init_acl(struct inode *ip, struct inode *dir)
|
|
{
|
|
return (0);
|
|
}
|
|
|
|
static inline int
|
|
zpl_chmod_acl(struct inode *ip)
|
|
{
|
|
return (0);
|
|
}
|
|
#endif /* CONFIG_FS_POSIX_ACL */
|
|
|
|
extern xattr_handler_t *zpl_xattr_handlers[];
|
|
|
|
/* zpl_ctldir.c */
|
|
extern const struct file_operations zpl_fops_root;
|
|
extern const struct inode_operations zpl_ops_root;
|
|
|
|
extern const struct file_operations zpl_fops_snapdir;
|
|
extern const struct inode_operations zpl_ops_snapdir;
|
|
#ifdef HAVE_AUTOMOUNT
|
|
extern const struct dentry_operations zpl_dops_snapdirs;
|
|
#else
|
|
extern const struct inode_operations zpl_ops_snapdirs;
|
|
#endif /* HAVE_AUTOMOUNT */
|
|
|
|
extern const struct file_operations zpl_fops_shares;
|
|
extern const struct inode_operations zpl_ops_shares;
|
|
|
|
#ifdef HAVE_VFS_ITERATE
|
|
|
|
#define DIR_CONTEXT_INIT(_dirent, _actor, _pos) { \
|
|
.actor = _actor, \
|
|
.pos = _pos, \
|
|
}
|
|
|
|
#else
|
|
|
|
typedef struct dir_context {
|
|
void *dirent;
|
|
const filldir_t actor;
|
|
loff_t pos;
|
|
} dir_context_t;
|
|
|
|
#define DIR_CONTEXT_INIT(_dirent, _actor, _pos) { \
|
|
.dirent = _dirent, \
|
|
.actor = _actor, \
|
|
.pos = _pos, \
|
|
}
|
|
|
|
static inline bool
|
|
dir_emit(struct dir_context *ctx, const char *name, int namelen,
|
|
uint64_t ino, unsigned type)
|
|
{
|
|
return (ctx->actor(ctx->dirent, name, namelen, ctx->pos, ino, type)
|
|
== 0);
|
|
}
|
|
|
|
static inline bool
|
|
dir_emit_dot(struct file *file, struct dir_context *ctx)
|
|
{
|
|
return (ctx->actor(ctx->dirent, ".", 1, ctx->pos,
|
|
file->f_path.dentry->d_inode->i_ino, DT_DIR) == 0);
|
|
}
|
|
|
|
static inline bool
|
|
dir_emit_dotdot(struct file *file, struct dir_context *ctx)
|
|
{
|
|
return (ctx->actor(ctx->dirent, "..", 2, ctx->pos,
|
|
parent_ino(file->f_path.dentry), DT_DIR) == 0);
|
|
}
|
|
|
|
static inline bool
|
|
dir_emit_dots(struct file *file, struct dir_context *ctx)
|
|
{
|
|
if (ctx->pos == 0) {
|
|
if (!dir_emit_dot(file, ctx))
|
|
return (false);
|
|
ctx->pos = 1;
|
|
}
|
|
if (ctx->pos == 1) {
|
|
if (!dir_emit_dotdot(file, ctx))
|
|
return (false);
|
|
ctx->pos = 2;
|
|
}
|
|
return (true);
|
|
}
|
|
#endif /* HAVE_VFS_ITERATE */
|
|
|
|
#endif /* _SYS_ZPL_H */
|