diff --git a/scripts/kernel/generate-kernel-patch b/scripts/kernel/generate-kernel-patch index 12374e86b..6f649d861 100755 --- a/scripts/kernel/generate-kernel-patch +++ b/scripts/kernel/generate-kernel-patch @@ -314,6 +314,7 @@ scst/src/scst_copy_mgr.c scst/src/scst_dlm.c scst/src/scst_dlm.h \ scst/src/scst_event.c scst/src/scst_event_priv.h scst/src/scst_no_dlm.c \ scst/src/scst_test.h \ scst/src/scst_event_test.h \ +scst/src/scst_dlm_test.h \ scst/src/scst_copy_mgr_test.h" scst_05_targ="scst/src/scst_targ.c scst/src/scst_local_cmd.c \ scst/src/scst_local_cmd.h \ diff --git a/scst/src/kunit/lib/suites/scst_kunit_lib.c b/scst/src/kunit/lib/suites/scst_kunit_lib.c index 0747cc9f3..0d910c1d6 100644 --- a/scst/src/kunit/lib/suites/scst_kunit_lib.c +++ b/scst/src/kunit/lib/suites/scst_kunit_lib.c @@ -8,6 +8,8 @@ #endif #include "../../../scst_lib_test.h" +#include "../../../scst_dlm.h" +#include "../../../scst_dlm_test.h" struct scst_port_id_fixture { unsigned int free_id; @@ -137,7 +139,69 @@ static void scst_empty_explicit_sg_buffers(struct kunit *test) KUNIT_EXPECT_NULL(test, buf); } +#if (defined(CONFIG_DLM) || defined(CONFIG_DLM_MODULE)) && !defined(CONFIG_SCST_NO_DLM) +static int scst_dlm_read_nodeids_error(char **entries) +{ + struct kunit *test = kunit_get_current_test(); + const int *error = test->priv; + + /* An empty partial result must not replace an established membership. */ + *entries = *error == -ENOMEM ? NULL : kzalloc(1, GFP_KERNEL); + KUNIT_EXPECT_TRUE(test, *error == -ENOMEM || *entries); + return *error; +} + +static void scst_dlm_directory_error_preserves_membership(struct kunit *test) +{ + static const int errors[] = { -EIO, -EACCES, -ENOMEM }; + struct scst_pr_dlm_data *dlm; + u32 *nodeids; + unsigned int i; + int error; + + dlm = kunit_kzalloc(test, sizeof(*dlm), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dlm); + mutex_init(&dlm->ls_mutex); + kunit_activate_static_stub(test, scst_dlm_read_nodeids, scst_dlm_read_nodeids_error); + for (i = 0; i < ARRAY_SIZE(errors); i++) { + int ret; + + /* The production update owns and may reallocate this array. */ + nodeids = kmalloc_array(2, sizeof(*nodeids), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, nodeids); + nodeids[0] = 17; + nodeids[1] = 23; + dlm->nodeid = nodeids; + dlm->participants = 2; + dlm->local_nodeid = 17; + error = errors[i]; + test->priv = &error; + + mutex_lock(&dlm->ls_mutex); + ret = scst_dlm_update_nodeids(dlm); + mutex_unlock(&dlm->ls_mutex); + KUNIT_EXPECT_EQ(test, ret, errors[i]); + KUNIT_EXPECT_PTR_EQ(test, dlm->nodeid, nodeids); + KUNIT_EXPECT_EQ(test, dlm->participants, 2); + KUNIT_EXPECT_EQ(test, dlm->local_nodeid, 17U); + if (dlm->nodeid == nodeids) { + KUNIT_EXPECT_EQ(test, nodeids[0], 17U); + KUNIT_EXPECT_EQ(test, nodeids[1], 23U); + } + /* Also tolerate the old bug freeing/replacing nodeids. */ + kfree(dlm->nodeid); + dlm->nodeid = NULL; + } + kunit_deactivate_static_stub(test, scst_dlm_read_nodeids); + test->priv = NULL; + mutex_destroy(&dlm->ls_mutex); +} +#endif + static struct kunit_case scst_lib_cases[] = { +#if (defined(CONFIG_DLM) || defined(CONFIG_DLM_MODULE)) && !defined(CONFIG_SCST_NO_DLM) + KUNIT_CASE(scst_dlm_directory_error_preserves_membership), +#endif KUNIT_CASE(scst_port_id_boundaries), KUNIT_CASE(scst_lun_flat_encoding), KUNIT_CASE(scst_explicit_sg_buffers), diff --git a/scst/src/scst_dlm.c b/scst/src/scst_dlm.c index 42cbc547d..127eed628 100644 --- a/scst/src/scst_dlm.c +++ b/scst/src/scst_dlm.c @@ -29,6 +29,7 @@ #include "scst_priv.h" #include "scst_pres.h" #include "scst_dlm.h" +#include "scst_dlm_test.h" #if (defined(CONFIG_DLM) || defined(CONFIG_DLM_MODULE)) && \ !defined(CONFIG_SCST_NO_DLM) @@ -744,43 +745,59 @@ out: #endif } -/* - * scst_dlm_update_nodeids - Update the Corosync node ID array pr_dlm->nodeid[] - */ -static int scst_dlm_update_nodeids(struct scst_pr_dlm_data *pr_dlm) +static const char scst_dlm_comms_dir[] = "/sys/kernel/config/dlm/cluster/comms"; + +/* The caller owns *entries even when reading the directory fails. */ +SCST_KUNIT_VISIBLE +int scst_dlm_read_nodeids(char **entries) { - static const char comms_dir[] = "/sys/kernel/config/dlm/cluster/comms"; struct file *comms; - char *p, *entries = kzalloc(1, GFP_KERNEL); - unsigned long nodeid; - uint32_t *new; - int i, ret, num_nodes; - char path[256], buf[64]; + int ret; - lockdep_assert_held(&pr_dlm->ls_mutex); + SCST_KUNIT_REDIRECT(scst_dlm_read_nodeids, entries); - num_nodes = 0; + *entries = kzalloc(1, GFP_KERNEL); + if (!*entries) + return -ENOMEM; - comms = filp_open(comms_dir, 0, 0400); - if (IS_ERR(comms)) { - ret = PTR_ERR(comms); - goto out; - } + comms = filp_open(scst_dlm_comms_dir, 0, 0400); + if (IS_ERR(comms)) + return PTR_ERR(comms); #ifndef HAVE_ITERATE_DIR - ret = vfs_readdir(comms, scst_dlm_filldir, &entries); + ret = vfs_readdir(comms, scst_dlm_filldir, entries); #else { struct scst_dlm_readdir_context ctx = { .ctx = { .actor = scst_dlm_filldir, }, - .entries = entries, + .entries = *entries, }; ret = iterate_dir(comms, &ctx.ctx); - entries = ctx.entries; + *entries = ctx.entries; } #endif filp_close(comms, NULL); + return ret; +} + +/* + * scst_dlm_update_nodeids - Update the Corosync node ID array pr_dlm->nodeid[] + */ +SCST_KUNIT_VISIBLE +int scst_dlm_update_nodeids(struct scst_pr_dlm_data *pr_dlm) +{ + char *p, *entries = NULL; + unsigned long nodeid; + u32 *new; + int i, ret, num_nodes = 0; + char path[256], buf[64]; + + lockdep_assert_held(&pr_dlm->ls_mutex); + + ret = scst_dlm_read_nodeids(&entries); + if (ret < 0) + goto out; ret = -ENOMEM; if (!entries) goto out; @@ -796,7 +813,7 @@ static int scst_dlm_update_nodeids(struct scst_pr_dlm_data *pr_dlm) ret = kstrtoul(p, 0, &nodeid); if (WARN_ON_ONCE(ret < 0)) continue; - snprintf(path, sizeof(path), "%s/%s/local", comms_dir, p); + snprintf(path, sizeof(path), "%s/%s/local", scst_dlm_comms_dir, p); if (scst_read_file(path, buf, sizeof(buf)) >= 0 && strcmp(buf, "1\n") == 0) pr_dlm->local_nodeid = nodeid; diff --git a/scst/src/scst_dlm_test.h b/scst/src/scst_dlm_test.h new file mode 100644 index 000000000..806d14ba2 --- /dev/null +++ b/scst/src/scst_dlm_test.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef SCST_DLM_TEST_H +#define SCST_DLM_TEST_H + +#include "scst_test.h" + +#ifdef SCST_KUNIT_ENABLED +struct scst_pr_dlm_data; + +int scst_dlm_read_nodeids(char **entries); +int scst_dlm_update_nodeids(struct scst_pr_dlm_data *pr_dlm); +#endif + +#endif /* SCST_DLM_TEST_H */