mirror of
https://github.com/SCST-project/scst.git
synced 2026-10-22 14:12:33 +00:00
scst_dlm: Preserve membership when directory enumeration fails
The saved Cppcheck report identifies the lost directory-read result: scst/src/scst_dlm.c:784: style: Variable 'ret' is reassigned a value before the old one has been used. [redundantAssignment] scst_dlm_update_nodeids overwrites the result of iterate_dir or vfs_readdir with -ENOMEM. An empty or partial listing after a read error can therefore replace the existing membership and report success. Check the initial allocation and return directory errors before changing node IDs, participant count or the local node ID. Isolate directory access behind a task-scoped KUnit stub while retaining both kernel APIs. Cover EIO, EACCES and ENOMEM with an existing membership and verify its array ownership and contents survive. Include the private test header in generated kernel patches as well as out-of-tree builds.
This commit is contained in:
@@ -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 \
|
||||
|
||||
@@ -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),
|
||||
|
||||
+38
-21
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
Reference in New Issue
Block a user