mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-21 11:13:30 +00:00
MFV r242729 (mm):
Illumos r13840:97fd5cdf328a: 3145 single-copy arc 3212 ztest: race condition between vdev_online() and spa_vdev_remove() Illumos r13849:3468a95b27cd: 3258 ztest's use of file descriptors is unstable
This commit is contained in:
commit
a9b09a3f3c
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=242845
@ -121,8 +121,8 @@
|
|||||||
#include <sys/fs/zfs.h>
|
#include <sys/fs/zfs.h>
|
||||||
#include <libnvpair.h>
|
#include <libnvpair.h>
|
||||||
|
|
||||||
#define ZTEST_FD_DATA 3
|
static int ztest_fd_data = -1;
|
||||||
#define ZTEST_FD_RAND 4
|
static int ztest_fd_rand = -1;
|
||||||
|
|
||||||
typedef struct ztest_shared_hdr {
|
typedef struct ztest_shared_hdr {
|
||||||
uint64_t zh_hdr_size;
|
uint64_t zh_hdr_size;
|
||||||
@ -710,14 +710,17 @@ process_options(int argc, char **argv)
|
|||||||
UINT64_MAX >> 2);
|
UINT64_MAX >> 2);
|
||||||
|
|
||||||
if (strlen(altdir) > 0) {
|
if (strlen(altdir) > 0) {
|
||||||
char cmd[MAXNAMELEN];
|
char *cmd;
|
||||||
char realaltdir[MAXNAMELEN];
|
char *realaltdir;
|
||||||
char *bin;
|
char *bin;
|
||||||
char *ztest;
|
char *ztest;
|
||||||
char *isa;
|
char *isa;
|
||||||
int isalen;
|
int isalen;
|
||||||
|
|
||||||
(void) realpath(getexecname(), cmd);
|
cmd = umem_alloc(MAXPATHLEN, UMEM_NOFAIL);
|
||||||
|
realaltdir = umem_alloc(MAXPATHLEN, UMEM_NOFAIL);
|
||||||
|
|
||||||
|
VERIFY(NULL != realpath(getexecname(), cmd));
|
||||||
if (0 != access(altdir, F_OK)) {
|
if (0 != access(altdir, F_OK)) {
|
||||||
ztest_dump_core = B_FALSE;
|
ztest_dump_core = B_FALSE;
|
||||||
fatal(B_TRUE, "invalid alternate ztest path: %s",
|
fatal(B_TRUE, "invalid alternate ztest path: %s",
|
||||||
@ -748,6 +751,9 @@ process_options(int argc, char **argv)
|
|||||||
fatal(B_TRUE, "invalid alternate lib directory %s",
|
fatal(B_TRUE, "invalid alternate lib directory %s",
|
||||||
zo->zo_alt_libpath);
|
zo->zo_alt_libpath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
umem_free(cmd, MAXPATHLEN);
|
||||||
|
umem_free(realaltdir, MAXPATHLEN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -764,10 +770,12 @@ ztest_random(uint64_t range)
|
|||||||
{
|
{
|
||||||
uint64_t r;
|
uint64_t r;
|
||||||
|
|
||||||
|
ASSERT3S(ztest_fd_rand, >=, 0);
|
||||||
|
|
||||||
if (range == 0)
|
if (range == 0)
|
||||||
return (0);
|
return (0);
|
||||||
|
|
||||||
if (read(ZTEST_FD_RAND, &r, sizeof (r)) != sizeof (r))
|
if (read(ztest_fd_rand, &r, sizeof (r)) != sizeof (r))
|
||||||
fatal(1, "short read from /dev/urandom");
|
fatal(1, "short read from /dev/urandom");
|
||||||
|
|
||||||
return (r % range);
|
return (r % range);
|
||||||
@ -4703,7 +4711,18 @@ ztest_fault_inject(ztest_ds_t *zd, uint64_t id)
|
|||||||
if (islog)
|
if (islog)
|
||||||
(void) rw_unlock(&ztest_name_lock);
|
(void) rw_unlock(&ztest_name_lock);
|
||||||
} else {
|
} else {
|
||||||
|
/*
|
||||||
|
* Ideally we would like to be able to randomly
|
||||||
|
* call vdev_[on|off]line without holding locks
|
||||||
|
* to force unpredictable failures but the side
|
||||||
|
* effects of vdev_[on|off]line prevent us from
|
||||||
|
* doing so. We grab the ztest_vdev_lock here to
|
||||||
|
* prevent a race between injection testing and
|
||||||
|
* aux_vdev removal.
|
||||||
|
*/
|
||||||
|
VERIFY(mutex_lock(&ztest_vdev_lock) == 0);
|
||||||
(void) vdev_online(spa, guid0, 0, NULL);
|
(void) vdev_online(spa, guid0, 0, NULL);
|
||||||
|
VERIFY(mutex_unlock(&ztest_vdev_lock) == 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5660,29 +5679,16 @@ ztest_init(ztest_shared_t *zs)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
setup_fds(void)
|
setup_data_fd(void)
|
||||||
{
|
{
|
||||||
int fd;
|
static char ztest_name_data[] = "/tmp/ztest.data.XXXXXX";
|
||||||
#ifdef illumos
|
|
||||||
|
|
||||||
char *tmp = tempnam(NULL, NULL);
|
ztest_fd_data = mkstemp(ztest_name_data);
|
||||||
fd = open(tmp, O_RDWR | O_CREAT, 0700);
|
ASSERT3S(ztest_fd_data, >=, 0);
|
||||||
ASSERT3U(fd, ==, ZTEST_FD_DATA);
|
(void) unlink(ztest_name_data);
|
||||||
(void) unlink(tmp);
|
|
||||||
free(tmp);
|
|
||||||
#else
|
|
||||||
char tmp[MAXPATHLEN];
|
|
||||||
|
|
||||||
strlcpy(tmp, ztest_opts.zo_dir, MAXPATHLEN);
|
|
||||||
strlcat(tmp, "/ztest.XXXXXX", MAXPATHLEN);
|
|
||||||
fd = mkstemp(tmp);
|
|
||||||
ASSERT3U(fd, ==, ZTEST_FD_DATA);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
fd = open("/dev/urandom", O_RDONLY);
|
|
||||||
ASSERT3U(fd, ==, ZTEST_FD_RAND);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
shared_data_size(ztest_shared_hdr_t *hdr)
|
shared_data_size(ztest_shared_hdr_t *hdr)
|
||||||
{
|
{
|
||||||
@ -5703,15 +5709,11 @@ setup_hdr(void)
|
|||||||
int size;
|
int size;
|
||||||
ztest_shared_hdr_t *hdr;
|
ztest_shared_hdr_t *hdr;
|
||||||
|
|
||||||
#ifndef illumos
|
|
||||||
pwrite(ZTEST_FD_DATA, "", 1, 0);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
hdr = (void *)mmap(0, P2ROUNDUP(sizeof (*hdr), getpagesize()),
|
hdr = (void *)mmap(0, P2ROUNDUP(sizeof (*hdr), getpagesize()),
|
||||||
PROT_READ | PROT_WRITE, MAP_SHARED, ZTEST_FD_DATA, 0);
|
PROT_READ | PROT_WRITE, MAP_SHARED, ztest_fd_data, 0);
|
||||||
ASSERT(hdr != MAP_FAILED);
|
ASSERT(hdr != MAP_FAILED);
|
||||||
|
|
||||||
VERIFY3U(0, ==, ftruncate(ZTEST_FD_DATA, sizeof (ztest_shared_hdr_t)));
|
VERIFY3U(0, ==, ftruncate(ztest_fd_data, sizeof (ztest_shared_hdr_t)));
|
||||||
|
|
||||||
hdr->zh_hdr_size = sizeof (ztest_shared_hdr_t);
|
hdr->zh_hdr_size = sizeof (ztest_shared_hdr_t);
|
||||||
hdr->zh_opts_size = sizeof (ztest_shared_opts_t);
|
hdr->zh_opts_size = sizeof (ztest_shared_opts_t);
|
||||||
@ -5722,7 +5724,7 @@ setup_hdr(void)
|
|||||||
hdr->zh_ds_count = ztest_opts.zo_datasets;
|
hdr->zh_ds_count = ztest_opts.zo_datasets;
|
||||||
|
|
||||||
size = shared_data_size(hdr);
|
size = shared_data_size(hdr);
|
||||||
VERIFY3U(0, ==, ftruncate(ZTEST_FD_DATA, size));
|
VERIFY3U(0, ==, ftruncate(ztest_fd_data, size));
|
||||||
|
|
||||||
(void) munmap((caddr_t)hdr, P2ROUNDUP(sizeof (*hdr), getpagesize()));
|
(void) munmap((caddr_t)hdr, P2ROUNDUP(sizeof (*hdr), getpagesize()));
|
||||||
}
|
}
|
||||||
@ -5735,14 +5737,14 @@ setup_data(void)
|
|||||||
uint8_t *buf;
|
uint8_t *buf;
|
||||||
|
|
||||||
hdr = (void *)mmap(0, P2ROUNDUP(sizeof (*hdr), getpagesize()),
|
hdr = (void *)mmap(0, P2ROUNDUP(sizeof (*hdr), getpagesize()),
|
||||||
PROT_READ, MAP_SHARED, ZTEST_FD_DATA, 0);
|
PROT_READ, MAP_SHARED, ztest_fd_data, 0);
|
||||||
ASSERT(hdr != MAP_FAILED);
|
ASSERT(hdr != MAP_FAILED);
|
||||||
|
|
||||||
size = shared_data_size(hdr);
|
size = shared_data_size(hdr);
|
||||||
|
|
||||||
(void) munmap((caddr_t)hdr, P2ROUNDUP(sizeof (*hdr), getpagesize()));
|
(void) munmap((caddr_t)hdr, P2ROUNDUP(sizeof (*hdr), getpagesize()));
|
||||||
hdr = ztest_shared_hdr = (void *)mmap(0, P2ROUNDUP(size, getpagesize()),
|
hdr = ztest_shared_hdr = (void *)mmap(0, P2ROUNDUP(size, getpagesize()),
|
||||||
PROT_READ | PROT_WRITE, MAP_SHARED, ZTEST_FD_DATA, 0);
|
PROT_READ | PROT_WRITE, MAP_SHARED, ztest_fd_data, 0);
|
||||||
ASSERT(hdr != MAP_FAILED);
|
ASSERT(hdr != MAP_FAILED);
|
||||||
buf = (uint8_t *)hdr;
|
buf = (uint8_t *)hdr;
|
||||||
|
|
||||||
@ -5761,12 +5763,13 @@ exec_child(char *cmd, char *libpath, boolean_t ignorekill, int *statusp)
|
|||||||
{
|
{
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
int status;
|
int status;
|
||||||
char cmdbuf[MAXPATHLEN];
|
char *cmdbuf = NULL;
|
||||||
|
|
||||||
pid = fork();
|
pid = fork();
|
||||||
|
|
||||||
if (cmd == NULL) {
|
if (cmd == NULL) {
|
||||||
(void) strlcpy(cmdbuf, getexecname(), sizeof (cmdbuf));
|
cmdbuf = umem_alloc(MAXPATHLEN, UMEM_NOFAIL);
|
||||||
|
(void) strlcpy(cmdbuf, getexecname(), MAXPATHLEN);
|
||||||
cmd = cmdbuf;
|
cmd = cmdbuf;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5775,9 +5778,16 @@ exec_child(char *cmd, char *libpath, boolean_t ignorekill, int *statusp)
|
|||||||
|
|
||||||
if (pid == 0) { /* child */
|
if (pid == 0) { /* child */
|
||||||
char *emptyargv[2] = { cmd, NULL };
|
char *emptyargv[2] = { cmd, NULL };
|
||||||
|
char fd_data_str[12];
|
||||||
|
|
||||||
struct rlimit rl = { 1024, 1024 };
|
struct rlimit rl = { 1024, 1024 };
|
||||||
(void) setrlimit(RLIMIT_NOFILE, &rl);
|
(void) setrlimit(RLIMIT_NOFILE, &rl);
|
||||||
|
|
||||||
|
(void) close(ztest_fd_rand);
|
||||||
|
VERIFY3U(11, >=,
|
||||||
|
snprintf(fd_data_str, 12, "%d", ztest_fd_data));
|
||||||
|
VERIFY0(setenv("ZTEST_FD_DATA", fd_data_str, 1));
|
||||||
|
|
||||||
(void) enable_extended_FILE_stdio(-1, -1);
|
(void) enable_extended_FILE_stdio(-1, -1);
|
||||||
if (libpath != NULL)
|
if (libpath != NULL)
|
||||||
VERIFY(0 == setenv("LD_LIBRARY_PATH", libpath, 1));
|
VERIFY(0 == setenv("LD_LIBRARY_PATH", libpath, 1));
|
||||||
@ -5790,6 +5800,11 @@ exec_child(char *cmd, char *libpath, boolean_t ignorekill, int *statusp)
|
|||||||
fatal(B_TRUE, "exec failed: %s", cmd);
|
fatal(B_TRUE, "exec failed: %s", cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cmdbuf != NULL) {
|
||||||
|
umem_free(cmdbuf, MAXPATHLEN);
|
||||||
|
cmd = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
while (waitpid(pid, &status, 0) != pid)
|
while (waitpid(pid, &status, 0) != pid)
|
||||||
continue;
|
continue;
|
||||||
if (statusp != NULL)
|
if (statusp != NULL)
|
||||||
@ -5854,39 +5869,41 @@ main(int argc, char **argv)
|
|||||||
char timebuf[100];
|
char timebuf[100];
|
||||||
char numbuf[6];
|
char numbuf[6];
|
||||||
spa_t *spa;
|
spa_t *spa;
|
||||||
char cmd[MAXNAMELEN];
|
char *cmd;
|
||||||
boolean_t hasalt;
|
boolean_t hasalt;
|
||||||
|
char *fd_data_str = getenv("ZTEST_FD_DATA");
|
||||||
boolean_t ischild = (0 == lseek(ZTEST_FD_DATA, 0, SEEK_CUR));
|
|
||||||
ASSERT(ischild || errno == EBADF);
|
|
||||||
|
|
||||||
(void) setvbuf(stdout, NULL, _IOLBF, 0);
|
(void) setvbuf(stdout, NULL, _IOLBF, 0);
|
||||||
|
|
||||||
dprintf_setup(&argc, argv);
|
dprintf_setup(&argc, argv);
|
||||||
|
|
||||||
if (!ischild) {
|
ztest_fd_rand = open("/dev/urandom", O_RDONLY);
|
||||||
|
ASSERT3S(ztest_fd_rand, >=, 0);
|
||||||
|
|
||||||
|
if (!fd_data_str) {
|
||||||
process_options(argc, argv);
|
process_options(argc, argv);
|
||||||
|
|
||||||
setup_fds();
|
setup_data_fd();
|
||||||
setup_hdr();
|
setup_hdr();
|
||||||
setup_data();
|
setup_data();
|
||||||
bcopy(&ztest_opts, ztest_shared_opts,
|
bcopy(&ztest_opts, ztest_shared_opts,
|
||||||
sizeof (*ztest_shared_opts));
|
sizeof (*ztest_shared_opts));
|
||||||
} else {
|
} else {
|
||||||
|
ztest_fd_data = atoi(fd_data_str);
|
||||||
setup_data();
|
setup_data();
|
||||||
bcopy(ztest_shared_opts, &ztest_opts, sizeof (ztest_opts));
|
bcopy(ztest_shared_opts, &ztest_opts, sizeof (ztest_opts));
|
||||||
}
|
}
|
||||||
ASSERT3U(ztest_opts.zo_datasets, ==, ztest_shared_hdr->zh_ds_count);
|
ASSERT3U(ztest_opts.zo_datasets, ==, ztest_shared_hdr->zh_ds_count);
|
||||||
|
|
||||||
/* Override location of zpool.cache */
|
/* Override location of zpool.cache */
|
||||||
(void) asprintf((char **)&spa_config_path, "%s/zpool.cache",
|
VERIFY3U(asprintf((char **)&spa_config_path, "%s/zpool.cache",
|
||||||
ztest_opts.zo_dir);
|
ztest_opts.zo_dir), !=, -1);
|
||||||
|
|
||||||
ztest_ds = umem_alloc(ztest_opts.zo_datasets * sizeof (ztest_ds_t),
|
ztest_ds = umem_alloc(ztest_opts.zo_datasets * sizeof (ztest_ds_t),
|
||||||
UMEM_NOFAIL);
|
UMEM_NOFAIL);
|
||||||
zs = ztest_shared;
|
zs = ztest_shared;
|
||||||
|
|
||||||
if (ischild) {
|
if (fd_data_str) {
|
||||||
metaslab_gang_bang = ztest_opts.zo_metaslab_gang_bang;
|
metaslab_gang_bang = ztest_opts.zo_metaslab_gang_bang;
|
||||||
metaslab_df_alloc_threshold =
|
metaslab_df_alloc_threshold =
|
||||||
zs->zs_metaslab_df_alloc_threshold;
|
zs->zs_metaslab_df_alloc_threshold;
|
||||||
@ -5909,7 +5926,8 @@ main(int argc, char **argv)
|
|||||||
(u_longlong_t)ztest_opts.zo_time);
|
(u_longlong_t)ztest_opts.zo_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
(void) strlcpy(cmd, getexecname(), sizeof (cmd));
|
cmd = umem_alloc(MAXNAMELEN, UMEM_NOFAIL);
|
||||||
|
(void) strlcpy(cmd, getexecname(), MAXNAMELEN);
|
||||||
|
|
||||||
zs->zs_do_init = B_TRUE;
|
zs->zs_do_init = B_TRUE;
|
||||||
if (strlen(ztest_opts.zo_alt_ztest) != 0) {
|
if (strlen(ztest_opts.zo_alt_ztest) != 0) {
|
||||||
@ -6050,5 +6068,7 @@ main(int argc, char **argv)
|
|||||||
kills, iters - kills, (100.0 * kills) / MAX(1, iters));
|
kills, iters - kills, (100.0 * kills) / MAX(1, iters));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
umem_free(cmd, MAXNAMELEN);
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
@ -191,6 +191,7 @@ uint64_t zfs_arc_meta_limit = 0;
|
|||||||
int zfs_arc_grow_retry = 0;
|
int zfs_arc_grow_retry = 0;
|
||||||
int zfs_arc_shrink_shift = 0;
|
int zfs_arc_shrink_shift = 0;
|
||||||
int zfs_arc_p_min_shift = 0;
|
int zfs_arc_p_min_shift = 0;
|
||||||
|
int zfs_disable_dup_eviction = 0;
|
||||||
|
|
||||||
TUNABLE_QUAD("vfs.zfs.arc_max", &zfs_arc_max);
|
TUNABLE_QUAD("vfs.zfs.arc_max", &zfs_arc_max);
|
||||||
TUNABLE_QUAD("vfs.zfs.arc_min", &zfs_arc_min);
|
TUNABLE_QUAD("vfs.zfs.arc_min", &zfs_arc_min);
|
||||||
@ -321,7 +322,6 @@ typedef struct arc_stats {
|
|||||||
kstat_named_t arcstat_l2_io_error;
|
kstat_named_t arcstat_l2_io_error;
|
||||||
kstat_named_t arcstat_l2_size;
|
kstat_named_t arcstat_l2_size;
|
||||||
kstat_named_t arcstat_l2_hdr_size;
|
kstat_named_t arcstat_l2_hdr_size;
|
||||||
kstat_named_t arcstat_memory_throttle_count;
|
|
||||||
kstat_named_t arcstat_l2_write_trylock_fail;
|
kstat_named_t arcstat_l2_write_trylock_fail;
|
||||||
kstat_named_t arcstat_l2_write_passed_headroom;
|
kstat_named_t arcstat_l2_write_passed_headroom;
|
||||||
kstat_named_t arcstat_l2_write_spa_mismatch;
|
kstat_named_t arcstat_l2_write_spa_mismatch;
|
||||||
@ -334,6 +334,10 @@ typedef struct arc_stats {
|
|||||||
kstat_named_t arcstat_l2_write_buffer_bytes_scanned;
|
kstat_named_t arcstat_l2_write_buffer_bytes_scanned;
|
||||||
kstat_named_t arcstat_l2_write_buffer_list_iter;
|
kstat_named_t arcstat_l2_write_buffer_list_iter;
|
||||||
kstat_named_t arcstat_l2_write_buffer_list_null_iter;
|
kstat_named_t arcstat_l2_write_buffer_list_null_iter;
|
||||||
|
kstat_named_t arcstat_memory_throttle_count;
|
||||||
|
kstat_named_t arcstat_duplicate_buffers;
|
||||||
|
kstat_named_t arcstat_duplicate_buffers_size;
|
||||||
|
kstat_named_t arcstat_duplicate_reads;
|
||||||
} arc_stats_t;
|
} arc_stats_t;
|
||||||
|
|
||||||
static arc_stats_t arc_stats = {
|
static arc_stats_t arc_stats = {
|
||||||
@ -391,7 +395,6 @@ static arc_stats_t arc_stats = {
|
|||||||
{ "l2_io_error", KSTAT_DATA_UINT64 },
|
{ "l2_io_error", KSTAT_DATA_UINT64 },
|
||||||
{ "l2_size", KSTAT_DATA_UINT64 },
|
{ "l2_size", KSTAT_DATA_UINT64 },
|
||||||
{ "l2_hdr_size", KSTAT_DATA_UINT64 },
|
{ "l2_hdr_size", KSTAT_DATA_UINT64 },
|
||||||
{ "memory_throttle_count", KSTAT_DATA_UINT64 },
|
|
||||||
{ "l2_write_trylock_fail", KSTAT_DATA_UINT64 },
|
{ "l2_write_trylock_fail", KSTAT_DATA_UINT64 },
|
||||||
{ "l2_write_passed_headroom", KSTAT_DATA_UINT64 },
|
{ "l2_write_passed_headroom", KSTAT_DATA_UINT64 },
|
||||||
{ "l2_write_spa_mismatch", KSTAT_DATA_UINT64 },
|
{ "l2_write_spa_mismatch", KSTAT_DATA_UINT64 },
|
||||||
@ -403,7 +406,11 @@ static arc_stats_t arc_stats = {
|
|||||||
{ "l2_write_pios", KSTAT_DATA_UINT64 },
|
{ "l2_write_pios", KSTAT_DATA_UINT64 },
|
||||||
{ "l2_write_buffer_bytes_scanned", KSTAT_DATA_UINT64 },
|
{ "l2_write_buffer_bytes_scanned", KSTAT_DATA_UINT64 },
|
||||||
{ "l2_write_buffer_list_iter", KSTAT_DATA_UINT64 },
|
{ "l2_write_buffer_list_iter", KSTAT_DATA_UINT64 },
|
||||||
{ "l2_write_buffer_list_null_iter", KSTAT_DATA_UINT64 }
|
{ "l2_write_buffer_list_null_iter", KSTAT_DATA_UINT64 },
|
||||||
|
{ "memory_throttle_count", KSTAT_DATA_UINT64 },
|
||||||
|
{ "duplicate_buffers", KSTAT_DATA_UINT64 },
|
||||||
|
{ "duplicate_buffers_size", KSTAT_DATA_UINT64 },
|
||||||
|
{ "duplicate_reads", KSTAT_DATA_UINT64 }
|
||||||
};
|
};
|
||||||
|
|
||||||
#define ARCSTAT(stat) (arc_stats.stat.value.ui64)
|
#define ARCSTAT(stat) (arc_stats.stat.value.ui64)
|
||||||
@ -1518,6 +1525,17 @@ arc_buf_clone(arc_buf_t *from)
|
|||||||
hdr->b_buf = buf;
|
hdr->b_buf = buf;
|
||||||
arc_get_data_buf(buf);
|
arc_get_data_buf(buf);
|
||||||
bcopy(from->b_data, buf->b_data, size);
|
bcopy(from->b_data, buf->b_data, size);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This buffer already exists in the arc so create a duplicate
|
||||||
|
* copy for the caller. If the buffer is associated with user data
|
||||||
|
* then track the size and number of duplicates. These stats will be
|
||||||
|
* updated as duplicate buffers are created and destroyed.
|
||||||
|
*/
|
||||||
|
if (hdr->b_type == ARC_BUFC_DATA) {
|
||||||
|
ARCSTAT_BUMP(arcstat_duplicate_buffers);
|
||||||
|
ARCSTAT_INCR(arcstat_duplicate_buffers_size, size);
|
||||||
|
}
|
||||||
hdr->b_datacnt += 1;
|
hdr->b_datacnt += 1;
|
||||||
return (buf);
|
return (buf);
|
||||||
}
|
}
|
||||||
@ -1618,6 +1636,16 @@ arc_buf_destroy(arc_buf_t *buf, boolean_t recycle, boolean_t all)
|
|||||||
ASSERT3U(state->arcs_size, >=, size);
|
ASSERT3U(state->arcs_size, >=, size);
|
||||||
atomic_add_64(&state->arcs_size, -size);
|
atomic_add_64(&state->arcs_size, -size);
|
||||||
buf->b_data = NULL;
|
buf->b_data = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we're destroying a duplicate buffer make sure
|
||||||
|
* that the appropriate statistics are updated.
|
||||||
|
*/
|
||||||
|
if (buf->b_hdr->b_datacnt > 1 &&
|
||||||
|
buf->b_hdr->b_type == ARC_BUFC_DATA) {
|
||||||
|
ARCSTAT_BUMPDOWN(arcstat_duplicate_buffers);
|
||||||
|
ARCSTAT_INCR(arcstat_duplicate_buffers_size, -size);
|
||||||
|
}
|
||||||
ASSERT(buf->b_hdr->b_datacnt > 0);
|
ASSERT(buf->b_hdr->b_datacnt > 0);
|
||||||
buf->b_hdr->b_datacnt -= 1;
|
buf->b_hdr->b_datacnt -= 1;
|
||||||
}
|
}
|
||||||
@ -1801,6 +1829,48 @@ arc_buf_size(arc_buf_t *buf)
|
|||||||
return (buf->b_hdr->b_size);
|
return (buf->b_hdr->b_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called from the DMU to determine if the current buffer should be
|
||||||
|
* evicted. In order to ensure proper locking, the eviction must be initiated
|
||||||
|
* from the DMU. Return true if the buffer is associated with user data and
|
||||||
|
* duplicate buffers still exist.
|
||||||
|
*/
|
||||||
|
boolean_t
|
||||||
|
arc_buf_eviction_needed(arc_buf_t *buf)
|
||||||
|
{
|
||||||
|
arc_buf_hdr_t *hdr;
|
||||||
|
boolean_t evict_needed = B_FALSE;
|
||||||
|
|
||||||
|
if (zfs_disable_dup_eviction)
|
||||||
|
return (B_FALSE);
|
||||||
|
|
||||||
|
mutex_enter(&buf->b_evict_lock);
|
||||||
|
hdr = buf->b_hdr;
|
||||||
|
if (hdr == NULL) {
|
||||||
|
/*
|
||||||
|
* We are in arc_do_user_evicts(); let that function
|
||||||
|
* perform the eviction.
|
||||||
|
*/
|
||||||
|
ASSERT(buf->b_data == NULL);
|
||||||
|
mutex_exit(&buf->b_evict_lock);
|
||||||
|
return (B_FALSE);
|
||||||
|
} else if (buf->b_data == NULL) {
|
||||||
|
/*
|
||||||
|
* We have already been added to the arc eviction list;
|
||||||
|
* recommend eviction.
|
||||||
|
*/
|
||||||
|
ASSERT3P(hdr, ==, &arc_eviction_hdr);
|
||||||
|
mutex_exit(&buf->b_evict_lock);
|
||||||
|
return (B_TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hdr->b_datacnt > 1 && hdr->b_type == ARC_BUFC_DATA)
|
||||||
|
evict_needed = B_TRUE;
|
||||||
|
|
||||||
|
mutex_exit(&buf->b_evict_lock);
|
||||||
|
return (evict_needed);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Evict buffers from list until we've removed the specified number of
|
* Evict buffers from list until we've removed the specified number of
|
||||||
* bytes. Move the removed buffers to the appropriate evict state.
|
* bytes. Move the removed buffers to the appropriate evict state.
|
||||||
@ -2887,8 +2957,10 @@ arc_read_done(zio_t *zio)
|
|||||||
abuf = buf;
|
abuf = buf;
|
||||||
for (acb = callback_list; acb; acb = acb->acb_next) {
|
for (acb = callback_list; acb; acb = acb->acb_next) {
|
||||||
if (acb->acb_done) {
|
if (acb->acb_done) {
|
||||||
if (abuf == NULL)
|
if (abuf == NULL) {
|
||||||
|
ARCSTAT_BUMP(arcstat_duplicate_reads);
|
||||||
abuf = arc_buf_clone(buf);
|
abuf = arc_buf_clone(buf);
|
||||||
|
}
|
||||||
acb->acb_buf = abuf;
|
acb->acb_buf = abuf;
|
||||||
abuf = NULL;
|
abuf = NULL;
|
||||||
}
|
}
|
||||||
@ -3434,6 +3506,16 @@ arc_release(arc_buf_t *buf, void *tag)
|
|||||||
ASSERT3U(*size, >=, hdr->b_size);
|
ASSERT3U(*size, >=, hdr->b_size);
|
||||||
atomic_add_64(size, -hdr->b_size);
|
atomic_add_64(size, -hdr->b_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We're releasing a duplicate user data buffer, update
|
||||||
|
* our statistics accordingly.
|
||||||
|
*/
|
||||||
|
if (hdr->b_type == ARC_BUFC_DATA) {
|
||||||
|
ARCSTAT_BUMPDOWN(arcstat_duplicate_buffers);
|
||||||
|
ARCSTAT_INCR(arcstat_duplicate_buffers_size,
|
||||||
|
-hdr->b_size);
|
||||||
|
}
|
||||||
hdr->b_datacnt -= 1;
|
hdr->b_datacnt -= 1;
|
||||||
arc_cksum_verify(buf);
|
arc_cksum_verify(buf);
|
||||||
#ifdef illumos
|
#ifdef illumos
|
||||||
|
@ -2089,7 +2089,24 @@ dbuf_rele_and_unlock(dmu_buf_impl_t *db, void *tag)
|
|||||||
dbuf_evict(db);
|
dbuf_evict(db);
|
||||||
} else {
|
} else {
|
||||||
VERIFY(arc_buf_remove_ref(db->db_buf, db) == 0);
|
VERIFY(arc_buf_remove_ref(db->db_buf, db) == 0);
|
||||||
if (!DBUF_IS_CACHEABLE(db))
|
|
||||||
|
/*
|
||||||
|
* A dbuf will be eligible for eviction if either the
|
||||||
|
* 'primarycache' property is set or a duplicate
|
||||||
|
* copy of this buffer is already cached in the arc.
|
||||||
|
*
|
||||||
|
* In the case of the 'primarycache' a buffer
|
||||||
|
* is considered for eviction if it matches the
|
||||||
|
* criteria set in the property.
|
||||||
|
*
|
||||||
|
* To decide if our buffer is considered a
|
||||||
|
* duplicate, we must call into the arc to determine
|
||||||
|
* if multiple buffers are referencing the same
|
||||||
|
* block on-disk. If so, then we simply evict
|
||||||
|
* ourselves.
|
||||||
|
*/
|
||||||
|
if (!DBUF_IS_CACHEABLE(db) ||
|
||||||
|
arc_buf_eviction_needed(db->db_buf))
|
||||||
dbuf_clear(db);
|
dbuf_clear(db);
|
||||||
else
|
else
|
||||||
mutex_exit(&db->db_mtx);
|
mutex_exit(&db->db_mtx);
|
||||||
|
@ -99,6 +99,7 @@ int arc_released(arc_buf_t *buf);
|
|||||||
int arc_has_callback(arc_buf_t *buf);
|
int arc_has_callback(arc_buf_t *buf);
|
||||||
void arc_buf_freeze(arc_buf_t *buf);
|
void arc_buf_freeze(arc_buf_t *buf);
|
||||||
void arc_buf_thaw(arc_buf_t *buf);
|
void arc_buf_thaw(arc_buf_t *buf);
|
||||||
|
boolean_t arc_buf_eviction_needed(arc_buf_t *buf);
|
||||||
#ifdef ZFS_DEBUG
|
#ifdef ZFS_DEBUG
|
||||||
int arc_referenced(arc_buf_t *buf);
|
int arc_referenced(arc_buf_t *buf);
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user