mirror of
https://github.com/SCST-project/scst.git
synced 2026-10-22 06:02:33 +00:00
kunit: Test copy-manager updates and read-only admission
Expose private functions to a separate test object and stub discovery, registration and default-group lookup. Keep the actual update and admission paths unchanged. Check coalescing, failed inquiry, removal before retry and read-only LUN rejection using private state.
This commit is contained in:
+1
-1
@@ -60,7 +60,7 @@ validates the selection against the supplied `.ko` files.
|
||||
|
||||
| Module | Labels |
|
||||
| --- | --- |
|
||||
| `scst.ko` | `ALUA CDB EVENT LIB ORDER UNMAP` |
|
||||
| `scst.ko` | `ALUA CDB CM EVENT LIB ORDER UNMAP` |
|
||||
|
||||
The runner reads suite metadata from the modules and loads selected owners
|
||||
with their dependencies. Unselected suites are not counted as skipped.
|
||||
|
||||
@@ -311,7 +311,8 @@ scst_04_main="scst/src/scst_main.c scst/src/scst_priv.h \
|
||||
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_event_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 \
|
||||
scst/src/scst_targ_test.h"
|
||||
|
||||
@@ -0,0 +1,243 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
#include "../../scst_kunit.h"
|
||||
|
||||
#ifdef INSIDE_KERNEL_TREE
|
||||
#include <scst/scst.h>
|
||||
#else
|
||||
#include "scst.h"
|
||||
#endif
|
||||
#include "../../../scst_priv.h"
|
||||
#include "../../../scst_copy_mgr_test.h"
|
||||
|
||||
struct cm_fixture {
|
||||
struct scst_device dev;
|
||||
struct scst_acg acg;
|
||||
struct scst_acg other_acg;
|
||||
struct scst_acg_dev acg_dev;
|
||||
unsigned int registrations;
|
||||
int register_result;
|
||||
struct completion removing;
|
||||
unsigned int lun;
|
||||
unsigned int lookups;
|
||||
unsigned int designator_clears;
|
||||
unsigned int inquiries;
|
||||
int inquiry_result;
|
||||
};
|
||||
|
||||
static unsigned int cm_lookup_stub(const struct scst_device *dev)
|
||||
{
|
||||
struct kunit *test = kunit_get_current_test();
|
||||
struct cm_fixture *f = test->priv;
|
||||
|
||||
KUNIT_EXPECT_PTR_EQ(test, dev, &f->dev);
|
||||
KUNIT_EXPECT_TRUE(test, mutex_is_locked(&scst_mutex));
|
||||
lockdep_assert_held(&scst_mutex);
|
||||
f->lookups++;
|
||||
return f->lun;
|
||||
}
|
||||
|
||||
static void cm_clear_stub(struct scst_device *dev)
|
||||
{
|
||||
struct kunit *test = kunit_get_current_test();
|
||||
struct cm_fixture *f = test->priv;
|
||||
|
||||
KUNIT_EXPECT_PTR_EQ(test, dev, &f->dev);
|
||||
KUNIT_EXPECT_TRUE(test, mutex_is_locked(&scst_mutex));
|
||||
f->designator_clears++;
|
||||
}
|
||||
|
||||
static int cm_inquiry_stub(struct scst_device *dev, unsigned int lun,
|
||||
struct scst_cm_init_inq_priv *priv)
|
||||
{
|
||||
struct kunit *test = kunit_get_current_test();
|
||||
struct cm_fixture *f = test->priv;
|
||||
|
||||
KUNIT_EXPECT_PTR_EQ(test, dev, &f->dev);
|
||||
KUNIT_EXPECT_PTR_EQ(test, priv, NULL);
|
||||
KUNIT_EXPECT_EQ(test, lun, f->lun);
|
||||
KUNIT_EXPECT_GT(test, dev->block_count, 0);
|
||||
KUNIT_EXPECT_TRUE(test, mutex_is_locked(&scst_mutex));
|
||||
KUNIT_EXPECT_EQ(test, f->designator_clears, f->inquiries + 1);
|
||||
f->inquiries++;
|
||||
return f->inquiry_result;
|
||||
}
|
||||
|
||||
static int cm_test_init(struct kunit *test)
|
||||
{
|
||||
struct cm_fixture *f;
|
||||
|
||||
f = kunit_kzalloc(test, sizeof(*f), GFP_KERNEL);
|
||||
if (!f)
|
||||
return -ENOMEM;
|
||||
test->priv = f;
|
||||
f->dev.virt_name = "kunit-private-cm-device";
|
||||
spin_lock_init(&f->dev.dev_lock);
|
||||
INIT_LIST_HEAD(&f->dev.blocked_cmd_list);
|
||||
atomic_set(&f->dev.cm_update_req_cnt, 0);
|
||||
init_completion(&f->removing);
|
||||
f->lun = 9;
|
||||
kunit_activate_static_stub(test, scst_cm_get_lun, cm_lookup_stub);
|
||||
kunit_activate_static_stub(test, scst_cm_dev_free_designators, cm_clear_stub);
|
||||
kunit_activate_static_stub(test, scst_cm_send_init_inquiry, cm_inquiry_stub);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct cm_update_case {
|
||||
const char *name;
|
||||
unsigned int pending;
|
||||
int error;
|
||||
bool removing;
|
||||
int initial_blocks;
|
||||
};
|
||||
|
||||
static const struct cm_update_case cm_updates[] = {
|
||||
{ "single", 0 },
|
||||
{ "coalesce-two", 1 },
|
||||
{ "coalesce-many", 10 },
|
||||
{ "inquiry-enomem", 0, -ENOMEM },
|
||||
{ "inquiry-eio", 0, -EIO },
|
||||
{ "preserve-existing-block", 0, -ENOMEM, false, 1 },
|
||||
{ "unregistered-before-update", 0, 0, true },
|
||||
{ "unregistered-before-retry", 3, 0, true },
|
||||
{ "retry-enomem", 3, -ENOMEM },
|
||||
{ "retry-eio", 3, -EIO },
|
||||
};
|
||||
|
||||
static void cm_update_desc(const struct cm_update_case *p, char *desc)
|
||||
{
|
||||
strscpy(desc, p->name, KUNIT_PARAM_DESC_SIZE);
|
||||
}
|
||||
|
||||
KUNIT_ARRAY_PARAM(cm_update, cm_updates, cm_update_desc);
|
||||
|
||||
static void cm_update_test(struct kunit *test)
|
||||
{
|
||||
const struct cm_update_case *p = test->param_value;
|
||||
struct cm_fixture *f = test->priv;
|
||||
unsigned int i;
|
||||
|
||||
f->dev.block_count = p->initial_blocks;
|
||||
if (!p->pending) {
|
||||
f->inquiry_result = p->error;
|
||||
if (p->removing) {
|
||||
f->lun = SCST_MAX_LUN;
|
||||
f->dev.remove_completion = &f->removing;
|
||||
}
|
||||
}
|
||||
scst_cm_update_dev_start(&f->dev);
|
||||
if (p->pending) {
|
||||
KUNIT_ASSERT_EQ(test, f->inquiries, 1U);
|
||||
for (i = 0; i < p->pending; i++)
|
||||
scst_cm_update_dev_start(&f->dev);
|
||||
KUNIT_EXPECT_EQ(test, f->inquiries, 1U);
|
||||
KUNIT_EXPECT_EQ(test, atomic_read(&f->dev.cm_update_req_cnt), (int)p->pending + 1);
|
||||
/* The real inquiry completion unblocks before calling update_fini. */
|
||||
spin_lock_bh(&f->dev.dev_lock);
|
||||
scst_unblock_dev(&f->dev);
|
||||
spin_unlock_bh(&f->dev.dev_lock);
|
||||
f->inquiry_result = p->error;
|
||||
if (p->removing) {
|
||||
f->lun = SCST_MAX_LUN;
|
||||
f->dev.remove_completion = &f->removing;
|
||||
}
|
||||
scst_cm_update_dev_fini(&f->dev);
|
||||
}
|
||||
KUNIT_EXPECT_EQ(test, f->lookups, p->pending ? 2U : 1U);
|
||||
KUNIT_EXPECT_EQ(test, f->inquiries,
|
||||
(unsigned int)!p->removing + (unsigned int)!!p->pending);
|
||||
if (p->error || p->removing) {
|
||||
KUNIT_EXPECT_EQ(test, f->dev.block_count, p->initial_blocks);
|
||||
KUNIT_EXPECT_EQ(test, atomic_read(&f->dev.cm_update_req_cnt), 0);
|
||||
} else {
|
||||
KUNIT_EXPECT_EQ(test, atomic_read(&f->dev.cm_update_req_cnt), 1);
|
||||
spin_lock_bh(&f->dev.dev_lock);
|
||||
scst_unblock_dev(&f->dev);
|
||||
spin_unlock_bh(&f->dev.dev_lock);
|
||||
scst_cm_update_dev_fini(&f->dev);
|
||||
KUNIT_EXPECT_EQ(test, atomic_read(&f->dev.cm_update_req_cnt), 0);
|
||||
KUNIT_EXPECT_EQ(test, f->dev.block_count, p->initial_blocks);
|
||||
}
|
||||
KUNIT_EXPECT_EQ(test, f->designator_clears, f->inquiries);
|
||||
KUNIT_EXPECT_FALSE(test, completion_done(&f->removing));
|
||||
}
|
||||
|
||||
static struct scst_acg *cm_default_group(void)
|
||||
{
|
||||
struct cm_fixture *f = kunit_get_current_test()->priv;
|
||||
|
||||
return &f->acg;
|
||||
}
|
||||
|
||||
static int cm_register_stub(struct scst_device *dev, uint64_t lun)
|
||||
{
|
||||
struct kunit *test = kunit_get_current_test();
|
||||
struct cm_fixture *f = test->priv;
|
||||
|
||||
KUNIT_EXPECT_PTR_EQ(test, dev, &f->dev);
|
||||
KUNIT_EXPECT_EQ(test, lun, (u64)9);
|
||||
lockdep_assert_held(&scst_mutex);
|
||||
f->registrations++;
|
||||
return f->register_result;
|
||||
}
|
||||
|
||||
struct cm_lun_case {
|
||||
const char *name;
|
||||
bool foreign;
|
||||
bool lun_readonly;
|
||||
bool dev_readonly;
|
||||
int register_error;
|
||||
};
|
||||
|
||||
static const struct cm_lun_case cm_luns[] = {
|
||||
{ "foreign-writable", true },
|
||||
{ "foreign-readonly", true, true, true },
|
||||
{ "readonly-lun", false, true },
|
||||
{ "readonly-device", false, false, true },
|
||||
{ "readonly-both", false, true, true },
|
||||
{ "writable" },
|
||||
{ "register-enomem", false, false, false, -ENOMEM },
|
||||
{ "register-eio", false, false, false, -EIO },
|
||||
};
|
||||
|
||||
static void cm_lun_desc(const struct cm_lun_case *p, char *desc)
|
||||
{
|
||||
strscpy(desc, p->name, KUNIT_PARAM_DESC_SIZE);
|
||||
}
|
||||
|
||||
KUNIT_ARRAY_PARAM(cm_lun, cm_luns, cm_lun_desc);
|
||||
|
||||
static void cm_lun_test(struct kunit *test)
|
||||
{
|
||||
const struct cm_lun_case *p = test->param_value;
|
||||
struct cm_fixture *f = test->priv;
|
||||
unsigned int flags = SCST_ADD_LUN_GEN_UA | SCST_ADD_LUN_READ_ONLY;
|
||||
bool readonly = p->lun_readonly || p->dev_readonly;
|
||||
bool registered = !p->foreign && !readonly;
|
||||
int rc;
|
||||
|
||||
f->acg_dev.acg = p->foreign ? &f->other_acg : &f->acg;
|
||||
f->acg_dev.dev = &f->dev;
|
||||
f->acg_dev.acg_dev_rd_only = p->lun_readonly;
|
||||
f->dev.dev_rd_only = p->dev_readonly;
|
||||
f->register_result = p->register_error;
|
||||
kunit_activate_static_stub(test, scst_cm_default_acg, cm_default_group);
|
||||
kunit_activate_static_stub(test, scst_cm_dev_register, cm_register_stub);
|
||||
mutex_lock(&scst_mutex);
|
||||
rc = scst_cm_on_add_lun(&f->acg_dev, 9, &flags);
|
||||
mutex_unlock(&scst_mutex);
|
||||
KUNIT_EXPECT_EQ(test, rc, p->foreign ? 0 : (readonly ? -EINVAL : p->register_error));
|
||||
KUNIT_EXPECT_EQ(test, f->registrations, (unsigned int)registered);
|
||||
KUNIT_EXPECT_EQ(test, !!(flags & SCST_ADD_LUN_GEN_UA), !registered);
|
||||
KUNIT_EXPECT_TRUE(test, !!(flags & SCST_ADD_LUN_READ_ONLY));
|
||||
}
|
||||
|
||||
static struct kunit_case cm_cases[] = {
|
||||
KUNIT_CASE_PARAM(cm_update_test, cm_update_gen_params),
|
||||
KUNIT_CASE_PARAM(cm_lun_test, cm_lun_gen_params),
|
||||
{}
|
||||
};
|
||||
|
||||
scst_kunit_suite(cm_suite, "CM", "",
|
||||
.init = cm_test_init,
|
||||
.test_cases = cm_cases,
|
||||
);
|
||||
@@ -4,6 +4,7 @@
|
||||
scst-kunit-scst := \
|
||||
kunit/alua/suites/scst_kunit_alua.o \
|
||||
kunit/cdb/suites/scst_kunit_cdb.o \
|
||||
kunit/cm/suites/scst_kunit_cm.o \
|
||||
kunit/event/scst_event_fixture.o \
|
||||
kunit/event/suites/scst_kunit_event_import.o \
|
||||
kunit/event/suites/scst_kunit_event_delivery.o \
|
||||
|
||||
+38
-15
@@ -17,6 +17,15 @@
|
||||
#endif
|
||||
#include "scst_priv.h"
|
||||
#include "scst_pres.h"
|
||||
#include "scst_copy_mgr_test.h"
|
||||
|
||||
#ifndef SCST_KUNIT_ENABLED
|
||||
struct scst_cm_init_inq_priv;
|
||||
|
||||
static int scst_cm_send_init_inquiry(struct scst_device *dev, unsigned int unpacked_lun,
|
||||
struct scst_cm_init_inq_priv *priv);
|
||||
static void scst_cm_update_dev_fini(struct scst_device *dev);
|
||||
#endif
|
||||
|
||||
#undef DEFAULT_SYMBOL_NAMESPACE
|
||||
#define DEFAULT_SYMBOL_NAMESPACE SCST_NAMESPACE
|
||||
@@ -2238,9 +2247,6 @@ struct scst_cm_init_inq_priv {
|
||||
struct scst_device *dev;
|
||||
};
|
||||
|
||||
static int scst_cm_send_init_inquiry(struct scst_device *dev, unsigned int unpacked_lun,
|
||||
struct scst_cm_init_inq_priv *priv);
|
||||
|
||||
static void scst_cm_inq_retry_fn(struct scst_cmd *cmd)
|
||||
{
|
||||
struct scst_cm_init_inq_priv *priv = cmd->tgt_i_priv;
|
||||
@@ -2254,8 +2260,6 @@ static void scst_cm_inq_retry_fn(struct scst_cmd *cmd)
|
||||
TRACE_EXIT();
|
||||
}
|
||||
|
||||
static void scst_cm_update_dev_fini(struct scst_device *dev);
|
||||
|
||||
static void scst_cm_init_inq_finish(struct scst_cmd *cmd)
|
||||
{
|
||||
int length, page_len, off, rc;
|
||||
@@ -2380,14 +2384,17 @@ out:
|
||||
TRACE_EXIT();
|
||||
}
|
||||
|
||||
static int scst_cm_send_init_inquiry(struct scst_device *dev, unsigned int unpacked_lun,
|
||||
struct scst_cm_init_inq_priv *priv)
|
||||
SCST_KUNIT_VISIBLE
|
||||
int scst_cm_send_init_inquiry(struct scst_device *dev, unsigned int unpacked_lun,
|
||||
struct scst_cm_init_inq_priv *priv)
|
||||
{
|
||||
static const uint8_t inq_cdb[6] = { INQUIRY, 1, 0x83, 0x10, 0, 0 };
|
||||
__be64 lun;
|
||||
struct scst_cmd *cmd;
|
||||
int res = 0;
|
||||
|
||||
SCST_KUNIT_REDIRECT(scst_cm_send_init_inquiry, dev, unpacked_lun, priv);
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (WARN_ON_ONCE(!dev)) {
|
||||
@@ -2458,11 +2465,14 @@ static bool scst_cm_is_lun_free(unsigned int lun)
|
||||
}
|
||||
|
||||
/* scst_mutex supposed to be held */
|
||||
static unsigned int scst_cm_get_lun(const struct scst_device *dev)
|
||||
SCST_KUNIT_VISIBLE
|
||||
unsigned int scst_cm_get_lun(const struct scst_device *dev)
|
||||
{
|
||||
unsigned int res = SCST_MAX_LUN;
|
||||
int i;
|
||||
|
||||
SCST_KUNIT_REDIRECT(scst_cm_get_lun, dev);
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
rcu_read_lock();
|
||||
@@ -2506,11 +2516,14 @@ static uint64_t scst_cm_get_free_lun(void)
|
||||
return lun;
|
||||
}
|
||||
|
||||
static int scst_cm_dev_register(struct scst_device *dev, uint64_t lun)
|
||||
SCST_KUNIT_VISIBLE
|
||||
int scst_cm_dev_register(struct scst_device *dev, uint64_t lun)
|
||||
{
|
||||
int res;
|
||||
struct scst_acg_dev *acg_dev;
|
||||
|
||||
SCST_KUNIT_REDIRECT(scst_cm_dev_register, dev, lun);
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
lockdep_assert_held(&scst_mutex);
|
||||
@@ -2561,10 +2574,13 @@ out_err:
|
||||
goto out;
|
||||
}
|
||||
|
||||
static void scst_cm_dev_free_designators(struct scst_device *dev)
|
||||
SCST_KUNIT_VISIBLE
|
||||
void scst_cm_dev_free_designators(struct scst_device *dev)
|
||||
{
|
||||
struct scst_cm_desig *des, *t;
|
||||
|
||||
SCST_KUNIT_REDIRECT(scst_cm_dev_free_designators, dev);
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
mutex_lock(&scst_cm_mutex);
|
||||
@@ -2648,8 +2664,8 @@ out_unblock:
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
static void
|
||||
scst_cm_update_dev_start(struct scst_device *dev)
|
||||
SCST_KUNIT_VISIBLE
|
||||
void scst_cm_update_dev_start(struct scst_device *dev)
|
||||
{
|
||||
int update_req_cnt, rc;
|
||||
|
||||
@@ -2662,8 +2678,8 @@ scst_cm_update_dev_start(struct scst_device *dev)
|
||||
atomic_set(&dev->cm_update_req_cnt, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
scst_cm_update_dev_fini(struct scst_device *dev)
|
||||
SCST_KUNIT_VISIBLE
|
||||
void scst_cm_update_dev_fini(struct scst_device *dev)
|
||||
{
|
||||
int update_req_cnt, rc;
|
||||
|
||||
@@ -2765,6 +2781,13 @@ void scst_cm_on_del_acg(struct scst_acg *acg)
|
||||
/* Nothing to do */
|
||||
}
|
||||
|
||||
SCST_KUNIT_VISIBLE
|
||||
struct scst_acg *scst_cm_default_acg(void)
|
||||
{
|
||||
SCST_KUNIT_REDIRECT(scst_cm_default_acg);
|
||||
return scst_cm_tgt->default_acg;
|
||||
}
|
||||
|
||||
/* scst_mutex supposed to be held */
|
||||
int scst_cm_on_add_lun(struct scst_acg_dev *acg_dev, uint64_t lun, unsigned int *flags)
|
||||
{
|
||||
@@ -2774,7 +2797,7 @@ int scst_cm_on_add_lun(struct scst_acg_dev *acg_dev, uint64_t lun, unsigned int
|
||||
|
||||
lockdep_assert_held(&scst_mutex);
|
||||
|
||||
if (acg_dev->acg != scst_cm_tgt->default_acg)
|
||||
if (acg_dev->acg != scst_cm_default_acg())
|
||||
goto out;
|
||||
|
||||
if (acg_dev->acg_dev_rd_only || acg_dev->dev->dev_rd_only) {
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
#ifndef SCST_COPY_MGR_TEST_H
|
||||
#define SCST_COPY_MGR_TEST_H
|
||||
|
||||
#include "scst_test.h"
|
||||
|
||||
#ifdef SCST_KUNIT_ENABLED
|
||||
|
||||
struct scst_cm_init_inq_priv;
|
||||
|
||||
int scst_cm_send_init_inquiry(struct scst_device *dev, unsigned int unpacked_lun,
|
||||
struct scst_cm_init_inq_priv *priv);
|
||||
unsigned int scst_cm_get_lun(const struct scst_device *dev);
|
||||
int scst_cm_dev_register(struct scst_device *dev, uint64_t lun);
|
||||
void scst_cm_dev_free_designators(struct scst_device *dev);
|
||||
void scst_cm_update_dev_start(struct scst_device *dev);
|
||||
void scst_cm_update_dev_fini(struct scst_device *dev);
|
||||
struct scst_acg *scst_cm_default_acg(void);
|
||||
|
||||
#endif /* SCST_KUNIT_ENABLED */
|
||||
#endif /* SCST_COPY_MGR_TEST_H */
|
||||
Reference in New Issue
Block a user