Viewing: obd_mod_rpcs_test.c

// SPDX-License-Identifier: GPL-2.0

/*
 * Copyright (c) 2025, Amazon and/or its affiliates. All rights reserved.
 * Use is subject to license terms.
 *
 */

/*
 * This file is part of Lustre, http://www.lustre.org/
 *
 * Simple OBD device mock test for LU-18826.
 *
 * Author: Lijing Chen <lijinc@amazon.com>
 *
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/pid.h>
#include <linux/sched/signal.h>
#include <linux/delay.h>
#include <linux/wait.h>
#include <linux/bitmap.h>

#include <obd_class.h>
#include <lustre_net.h>

struct test_ctx {
	struct client_obd *cli;
	struct obd_import *imp;
	struct obd_device *obd;
	unsigned int old_flags;
	int __user *old_set_child_tid;
};

static void setup_test_context(struct test_ctx *ctx)
{
	int i;

	OBD_ALLOC_PTR(ctx->cli);
	OBD_ALLOC_PTR(ctx->imp);
	OBD_ALLOC_PTR(ctx->obd);

	init_waitqueue_head(&ctx->cli->cl_mod_rpcs_waitq);
	ctx->cli->cl_max_mod_rpcs_in_flight = 0;
	ctx->cli->cl_mod_rpcs_in_flight = 0;

	/* Initialize cl_mod_rpcs_hist */
	spin_lock_init(&ctx->cli->cl_mod_rpcs_hist.oh_lock);
	for (i = 0; i < OBD_HIST_MAX; i++)
		ctx->cli->cl_mod_rpcs_hist.oh_buckets[i] = 0;

	/* Initialize bitmap */
	OBD_ALLOC(ctx->cli->cl_mod_tag_bitmap,
		  BITS_TO_LONGS(OBD_MAX_RIF_MAX) * sizeof(long));

	/* Set up import and obd structures */
	ctx->imp->imp_obd = ctx->obd;
	ctx->cli->cl_import = ctx->imp;
	snprintf(ctx->obd->obd_name, sizeof(ctx->obd->obd_name), "mock_obd");

	/* Save current state */
	ctx->old_flags = current->flags;
	ctx->old_set_child_tid = current->set_child_tid;

	/* Set set_child_tid to NULL for testing */
	current->set_child_tid = NULL;
}

static void cleanup_test_context(struct test_ctx *ctx)
{
	OBD_FREE(ctx->cli->cl_mod_tag_bitmap,
		 BITS_TO_LONGS(OBD_MAX_RIF_MAX) * sizeof(long));
	OBD_FREE_PTR(ctx->obd);
	OBD_FREE_PTR(ctx->imp);
	OBD_FREE_PTR(ctx->cli);

	/* Restore original state */
	current->flags = ctx->old_flags;
	current->set_child_tid = ctx->old_set_child_tid;
}

static void test_kthread_flags(void)
{
	struct test_ctx ctx;
	__u32 opc = MDS_STATFS;
	__u16 ret;

	pr_info("\n=== Starting test case 1: kthread flags ===\n");

	setup_test_context(&ctx);

	/* Set kthread flags */
	current->flags = PF_KTHREAD|PF_MEMALLOC|PF_NOFREEZE|PF_FORKNOEXEC;

	pr_info("Test 1: Current flags: 0x%x\n", current->flags);
	pr_info("Test 1: set_child_tid: %p\n", current->set_child_tid);

	/* Simulate cl_mod_rpcs_in_flight > cl_max_mod_rpcs_in_flight */
	test_and_set_bit(0, ctx.cli->cl_mod_tag_bitmap);
	test_and_set_bit(1, ctx.cli->cl_mod_tag_bitmap);
	ctx.cli->cl_mod_rpcs_in_flight = 1;
	ctx.cli->cl_close_rpcs_in_flight = 1;

	ret = obd_get_mod_rpc_slot(ctx.cli, opc);

	pr_info("Test 1: Return value (slot): %u\n", ret);
	pr_info("Test 1: RPCs in flight: %d\n", ctx.cli->cl_mod_rpcs_in_flight);

	cleanup_test_context(&ctx);

	pr_info("=== Finished test case 1 ===\n");
}

static void test_non_mem_alloc_flags(void)
{
	struct test_ctx ctx;
	__u32 opc = MDS_CLOSE;
	__u16 ret;

	pr_info("\n=== Starting test case 2: non mem_alloc flags ===\n");

	setup_test_context(&ctx);

	/* Set minimal flags */
	current->flags = PF_KTHREAD|PF_NOFREEZE|PF_FORKNOEXEC;

	pr_info("Test 2: Current flags: 0x%x\n", current->flags);
	pr_info("Test 2: set_child_tid: %p\n", current->set_child_tid);

	ret = obd_get_mod_rpc_slot(ctx.cli, opc);

	pr_info("Test 2: Return value (slot): %u\n", ret);
	pr_info("Test 2: RPCs in flight: %d\n", ctx.cli->cl_mod_rpcs_in_flight);

	cleanup_test_context(&ctx);

	pr_info("=== Finished test case 2 ===\n");
}

static int __init obd_mod_rpcs_test_init(void)
{
	/* Run test cases */
	test_kthread_flags();
	test_non_mem_alloc_flags();
	return 0;
}

static void __exit obd_mod_rpcs_test_exit(void)
{
	pr_info("Task Module: Unloading module\n");
}

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Lijing Chen <lijinc@amazon.com>");
MODULE_DESCRIPTION("Lustre OBD test module");
MODULE_VERSION(LUSTRE_VERSION_STRING);

module_init(obd_mod_rpcs_test_init);
module_exit(obd_mod_rpcs_test_exit);