diff --git a/LICENSE b/LICENSE index 63bb6f8..ae3132f 100644 --- a/LICENSE +++ b/LICENSE @@ -58,7 +58,7 @@ APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. -Copyright 2024 JackrabbitLabs-Releases +Copyright 2024 Jackrabbit-Founders-LLC Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index 0bd2755..020d7ec 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,87 @@ -# libmem-release +# Overview + +Libmem is a C library for managing the state of memory blocks in a Linux +system. This library provides a user or orchestration application the ability +to online / offline individual memory blocks of the system or specific blocks +within a CXL region. + +The library consists of a `libmem.h` header file and a `libmem.a` library archive. +The library also builds a CLI tool `mem` that allows the user user to +view and manipulate the state of system memory blocks from the command line. + +# Dependencies + +To compile, the library requires the following libraries to be installed in +addition to typical packages to build C programs (e.g. `make`) + +``` +daxctl uuid cxl +``` + +The `uuid` library can be installed from the system package manager: + +```bash +apt install uuid-dev +``` + +The `daxctl` and `libcxl` libraries included with current Linux distributions are +typically too old to support the features needed. Consequently, the user is +required to build the current version of the `ndctl` project and install the +manually built `daxctl` and `cxl` libraries. + +See [ndctl](https://github.com/pmem/ndctl) on build and installation +instructions. + +# Build + +To build simply type: + +```bash +make +``` + +To install to `/usr/local/*` locations type: + +```bash +make install +``` + +# CLI Usage + +The `mem` CLI tool can be used to display system memory block information: + +```bash +mem show info +``` + +To display the state of memory blocks in the system: + +```bash +mem list +``` + +To online system memory block 306 + +```bash +mem block online 306 +``` + +To offline a system memory block + +```bash +mem block offline 306 +``` + +To online memory block 2 of a CXL region: + +```bash +mem block online 2 region0 +``` + +To offline memory block 2 of a CXL region: + +```bash +mem block offline 2 region0 +``` + -Online and offline those memory blocks and regions \ No newline at end of file diff --git a/cli.c b/cli.c new file mode 100644 index 0000000..cd571bd --- /dev/null +++ b/cli.c @@ -0,0 +1,2282 @@ +/** + * @file cli.c + * + * @brief cli code file for memory management library + * + * @copyright Copyright (C) 2024 Jackrabbit Founders LLC. All rights reserved. + * + * @date Jul 2024 + * @author Barrett Edwards + * + */ + +/* INCLUDES ==================================================================*/ + +/* printf() + * fprintf() + * stderr + */ +#include + +/* atoi() + * calloc() + * free() + */ +#include + +/* strcmp() + */ +#include + + /* errno MACROS + */ + #include + +/* CLI_LOG_LEVEL + */ +#include + +/* cxl_new() + * cxl_region + * cxl_memdev + * cxl_decoder + */ +#include + +#include "options.h" + +#include "libmem.h" + +/* MACROS ====================================================================*/ + +#define CLI_LOG_LEVEL LOG_DEBUG +#define CLI_LOG_DST LMLD_SYSLOG +#define CLI_IG 4096 + +/* ENUMERATIONS ==============================================================*/ + +/* STRUCTS ===================================================================*/ + +struct cli_arg +{ + char *name; + char *args; + char *help; + int (*fn)(int argc, char **argv); +}; + +/* PROTOTYPES ================================================================*/ + +int cmd_blk_offline(int num, int start); +int cmd_blk_online(int num, int start); + +int cmd_info(); +int cmd_list(int online, int offline, char *region_name); + +int cmd_region_create(int granularity, int num, char **names); +int cmd_region_delete(char *name); +int cmd_region_disable(char *name); +int cmd_region_enable(char *name); +int cmd_region_daxmode(char *name); +int cmd_region_rammode(char *name); +int cmd_region_set_blk_state(char *name, int offset, int state); + +int cmd_set_blk_state(int index, int state); +int cmd_set_system_policy(int policy); + +int cmd_show_blk_device(int id); +int cmd_show_blk_isonline(int id); +int cmd_show_blk_isremovable(int id); +int cmd_show_blk_node(int id); +int cmd_show_blk_state(int id); +int cmd_show_blk_zones(int id); + +int cmd_show_blocks(int online, int offline, char *region_name); + +int cmd_show_capacity(int online, int offline, char *region_name, int human); + +int cmd_show_memdev_interleave_granulariy(char *name); //todo +int cmd_show_memdev_isavailable(char *name); +int cmd_show_memdevs(char *memdev_name, char *region_name, int human); + +int cmd_show_num_blocks(int online, int offline, char *region_name); +int cmd_show_num_devices(); +int cmd_show_num_regions(); + +int cmd_show_region_blk_state(char *name, int offset); + +int cmd_show_region_isenabled(char *name); + +int cmd_show_regions(char *name, int human); + +int cmd_show_system_blocksize(int human); +int cmd_show_system_policy(); + +/* GLOBAL VARIABLES ==========================================================*/ + +/* FUNCTIONS =================================================================*/ + +int cmd_blk_offline(int num, int start) +{ + int rv; + struct mem_ctx *ctx; + + rv = 1; + + // Validate Privileges + if ( getuid() != 0 ) + { + fprintf(stderr, "Error: Command must be run as root\n"); + rv = -EACCES; + goto end; + } + + // Get mem contex + rv = mem_new(&ctx); + if (rv != 0) + { + fprintf(stderr, "Error: Failed to obtain mem context: %d\n", rv); + rv = 1; + goto end; + } + mem_log_set_destination(ctx, CLI_LOG_DST, NULL); + mem_log_set_priority(ctx, CLI_LOG_LEVEL); + + for ( int i = start ; i < (start + num) ; i++) + { + if (mem_blkid_is_online(ctx, i) == 1) + { + // Online a memory block + rv = mem_blkid_offline(ctx, i); + if (rv != 0) + { + fprintf(stderr, "Error: Could not offline memory block %d. %d\n", i, rv); + goto err; + } + } + } + + rv = 0; + +err: + + mem_unref(ctx); + +end: + + return rv; +} + +int cmd_blk_online(int num, int start) +{ + int rv; + struct mem_ctx *ctx; + struct mem_blk *blk; + + rv = 1; + + // Validate Privileges + if ( getuid() != 0 ) + { + fprintf(stderr, "Error: Command must be run as root\n"); + rv = -EACCES; + goto end; + } + + // Get mem contex + rv = mem_new(&ctx); + if (rv != 0) + { + fprintf(stderr, "Error: Failed to obtain mem context: %d\n", rv); + rv = 1; + goto end; + } + mem_log_set_destination(ctx, CLI_LOG_DST, NULL); + mem_log_set_priority(ctx, CLI_LOG_LEVEL); + + // Online all blocks + if (num < 0) + { + mem_blk_foreach(ctx, blk) + if (!mem_blk_is_online(blk)) + mem_blk_online(blk); + } + else + { + for ( int i = start ; i < (start + num) ; i++) + { + if (!mem_blkid_is_online(ctx, i)) + { + // Online a memory block + rv = mem_blkid_online(ctx, i); + if (rv != 0) + { + fprintf(stderr, "Error: Could not online memory block %d. %d\n", i, rv); + goto err; + } + } + } + } + + rv = 0; + +err: + + mem_unref(ctx); + +end: + + return rv; +} + +int cmd_info() +{ + int rv; + struct mem_ctx *ctx; + + // Create mem context + rv = mem_new(&ctx); + if (rv != 0) + { + fprintf(stderr, "Error: Failed to obtain mem context: %d\n", rv); + rv = 1; + goto end; + } + mem_log_set_destination(ctx, CLI_LOG_DST, NULL); + mem_log_set_priority(ctx, CLI_LOG_LEVEL); + + printf("Memory Blocksize: %llu\n", mem_system_get_blocksize(ctx)); + printf("Auto Online Memory Policy: %s\n", mem_lmpl(mem_system_get_policy(ctx))); + printf("Number of Blocks: %d\n", mem_system_num_blocks(ctx)); + printf(" Number of Blocks online: %d\n", mem_system_num_blocks_online(ctx)); + printf(" Number of Blocks offline: %d\n", mem_system_num_blocks_offline(ctx)); + printf("Memory Capacity: %llu\n", mem_system_get_capacity(ctx)); + printf(" Memory Capacity online: %llu\n", mem_system_get_capacity_online(ctx)); + printf(" Memory Capacity offline: %llu\n", mem_system_get_capacity_offline(ctx)); + printf("Number of CXL regions: %d\n", mem_num_regions(ctx)); + printf("Number of CXL memdevs: %d\n", mem_num_memdevs(ctx)); + + mem_unref(ctx); + +end: + + return rv; +} + +int cmd_list(int online, int offline, char *region_name) +{ + int rv; + struct mem_blk *blk; + struct mem_ctx *ctx; + struct cxl_region *region; + unsigned long u; + + // Initialize variables + rv = 1; + + // Get mem context + rv = mem_new(&ctx); + if (rv != 0) + { + fprintf(stderr, "Error: Failed to obtain mem context: %d\n", rv); + rv = 1; + goto end; + } + mem_log_set_destination(ctx, CLI_LOG_DST, NULL); + mem_log_set_priority(ctx, CLI_LOG_LEVEL); + + printf("Index node online cxl_region zones\n"); + printf("----- ---- ------ ---------- -------------------\n"); + + mem_blk_foreach(ctx, blk) + { + if (online && !mem_blk_is_online(blk)) + continue; + + if (offline && mem_blk_is_online(blk)) + continue; + + region = mem_blk_get_region(blk); + + if (region == NULL && region_name != NULL) + continue; + + if (region != NULL && region_name != NULL && strcmp(region_name, cxl_region_get_devname(region)) ) + continue; + + printf("%-5d %-4d %-6d ", + mem_blk_get_id(blk), + mem_blk_get_node(blk), + mem_blk_is_online(blk)); + + if (region != NULL) + printf("%-10s ", cxl_region_get_devname(region)); + else + printf("%-10s ", "-"); + + // Print zones + u = mem_blk_get_zones(blk); + for ( int k = 0 ; k < LMZN_MAX ; k++) + { + if (u & (0x01 << k)) + printf("%s ", mem_lmzn(k)); + } + printf("\n"); + } + +end: + return rv; +} + +int cmd_region_create(int granularity, int num, char **names) +{ + int rv; + struct cxl_memdev **memdevs; + struct mem_ctx *ctx; + + // Initialize variables + rv = 1; + + // Validate Privileges + if ( getuid() != 0 ) + { + fprintf(stderr, "Error: Command must be run as root\n"); + rv = -EACCES; + goto end; + } + + // Validate Inputs + if (granularity == 0) + granularity = CLI_IG; + + if (!(granularity == 256 + || granularity == 512 + || granularity == 1024 + || granularity == 2048 + || granularity == 4096 + || granularity == 8192)) + { + fprintf(stderr, "Error: Invalid Interleave Granularity: %d\n", granularity); + rv = -EINVAL; + goto end; + } + + if (num < 0) + { + fprintf(stderr, "Error: Missing memdev[s]\n"); + rv = -EINVAL; + goto end; + } + + // Get mem context + rv = mem_new(&ctx); + if (rv != 0) + { + fprintf(stderr, "Error: Failed to obtain mem context: %d\n", rv); + rv = 1; + goto end; + } + mem_log_set_destination(ctx, CLI_LOG_DST, NULL); + mem_log_set_priority(ctx, CLI_LOG_LEVEL); + + // Use all memdevs + if (num == 0) + { + num = mem_num_memdevs(ctx); + memdevs = mem_get_memdevs(ctx); + } + else + { + // Allocate memory for the array of memdevs + memdevs = calloc(num, sizeof(*memdevs)); + if (memdevs == NULL) + { + fprintf(stderr, "Error: Could not allocate memory. %d - %s\n", errno, strerror(errno)); + rv = -ENOMEM; + goto err; + } + + // Loop through requested memory devices + for ( int i = 0 ; i < num ; i++ ) + { + memdevs[i] = mem_get_memdev(ctx, names[i]); + if (memdevs[i] == NULL) + { + fprintf(stderr, "Error: Could not obtain memdev: %s\n", names[i]); + rv = 1; + goto err; + } + } + } + + // Create the region + rv = mem_region_create(ctx, granularity, num, memdevs); + if (rv != 0) + { + fprintf(stderr, "Error: Could not create region: %d\n", rv); + rv = 1; + goto err; + } + + rv = 0; + +err: + + free(memdevs); + mem_unref(ctx); + +end: + + return rv; +} + +int cmd_region_daxmode(char *name) +{ + int rv; + struct mem_ctx *ctx; + struct cxl_region *region; + + // Initialize variables + rv = 1; + + // Validate Privileges + if ( getuid() != 0 ) + { + fprintf(stderr, "Error: Command must be run as root\n"); + rv = -EACCES; + goto end; + } + + // Validate Inputs + if (name == NULL) + { + fprintf(stderr, "Error: Missing region\n"); + rv = -EINVAL; + goto end; + } + + // Get mem context + rv = mem_new(&ctx); + if (rv != 0) + { + fprintf(stderr, "Error: Failed to obtain mem context: %d\n", rv); + rv = 1; + goto end; + } + mem_log_set_destination(ctx, CLI_LOG_DST, NULL); + mem_log_set_priority(ctx, CLI_LOG_LEVEL); + + // Get region + region = mem_get_region(ctx, name); + if (region == NULL) + { + fprintf(stderr, "Error: Could not obtain region: %s\n", name); + rv = 1; + goto err; + } + + // Enable devdax mode + rv = mem_region_daxmode(ctx, region); + if (rv != 0) + { + fprintf(stderr, "Error: Enable of devdax mode failed: %d\n", rv); + rv = 1; + goto err; + } + + rv = 0; + +err: + + mem_unref(ctx); + +end: + + return rv; +} + +int cmd_region_delete(char *name) +{ + int rv, num; + struct mem_ctx *ctx; + struct cxl_region *region, **regions; + + // Initialize variables + rv = 1; + + // Validate Privileges + if ( getuid() != 0 ) + { + fprintf(stderr, "Error: Command must be run as root\n"); + rv = -EACCES; + goto end; + } + + // Get mem context + rv = mem_new(&ctx); + if (rv != 0) + { + fprintf(stderr, "Error: Failed to obtain mem context: %d\n", rv); + rv = 1; + goto end; + } + mem_log_set_destination(ctx, CLI_LOG_DST, NULL); + mem_log_set_priority(ctx, CLI_LOG_LEVEL); + + // Delete all regions + if (name == NULL) + { + num = mem_num_regions(ctx); + regions = mem_get_regions(ctx); + char name[256]; + + for ( int i = 0 ; i < num ; i++) + { + // Delete region + strcpy(name, cxl_region_get_devname(regions[i])); + rv = mem_region_delete(ctx, regions[i]); + if (rv != 0) + { + fprintf(stderr, "Error: Could not delete region: %s\n", name); + rv = 1; + goto err; + } + } + } + else + { + // Get region + region = mem_get_region(ctx, name); + if (region == NULL) + { + fprintf(stderr, "Error: Could not obtain region: %s\n", name); + rv = 1; + goto err; + } + + // Delete region + rv = mem_region_delete(ctx, region); + if (rv != 0) + { + fprintf(stderr, "Error: Could not delete region: %s\n", name); + rv = 1; + goto err; + } + } + + rv = 0; + +err: + + mem_unref(ctx); + +end: + + return rv; +} + +int cmd_region_disable(char *name) +{ + int rv; + struct mem_ctx *ctx; + struct cxl_region *region; + + // Initialize variables + rv = 1; + + // Validate Privileges + if ( getuid() != 0 ) + { + fprintf(stderr, "Error: Command must be run as root\n"); + rv = -EACCES; + goto end; + } + + // Validate Inputs + if (name == NULL) + { + fprintf(stderr, "Error: Missing region\n"); + rv = -EINVAL; + goto end; + } + + // Get mem context + rv = mem_new(&ctx); + if (rv != 0) + { + fprintf(stderr, "Error: Failed to obtain mem context: %d\n", rv); + rv = 1; + goto end; + } + mem_log_set_destination(ctx, CLI_LOG_DST, NULL); + mem_log_set_priority(ctx, CLI_LOG_LEVEL); + + // Get region + region = mem_get_region(ctx, name); + if (region == NULL) + { + fprintf(stderr, "Error: Could not obtain region: %s\n", name); + rv = 1; + goto err; + } + + // Check if Region was enabled + rv = cxl_region_is_enabled(region); + if (rv == 0) + { + fprintf(stderr, "Region was already disabled\n"); + rv = 1; + goto err; + } + + // Disable Region + rv = cxl_region_disable(region); + if (rv != 0) + { + fprintf(stderr, "Error: Could not disable region: %d\n", rv); + rv = 1; + goto err; + } + + rv = 0; + +err: + + mem_unref(ctx); + +end: + + return rv; +} + +int cmd_region_enable(char *name) +{ + int rv; + struct mem_ctx *ctx; + struct cxl_region *region; + + // Initialize variables + rv = 1; + + // Validate Privileges + if ( getuid() != 0 ) + { + fprintf(stderr, "Error: Command must be run as root\n"); + rv = -EACCES; + goto end; + } + + // Validate Inputs + if (name == NULL) + { + fprintf(stderr, "Error: Missing region\n"); + rv = -EINVAL; + goto end; + } + + // Get mem context + rv = mem_new(&ctx); + if (rv != 0) + { + fprintf(stderr, "Error: Failed to obtain mem context: %d\n", rv); + rv = 1; + goto end; + } + mem_log_set_destination(ctx, CLI_LOG_DST, NULL); + mem_log_set_priority(ctx, CLI_LOG_LEVEL); + + // Get the region + region = mem_get_region(ctx, name); + if (region == NULL) + { + fprintf(stderr, "Error: Could not obtain region: %s\n", name); + rv = 1; + goto err; + } + + // Check if region is already enabled + rv = cxl_region_is_enabled(region); + if (rv == 1) + { + fprintf(stderr, "Region was already enabled\n"); + rv = 1; + goto err; + } + + // Enable the region + rv = cxl_region_enable(region); + if (rv != 0) + { + fprintf(stderr, "Error: Could not enable region: %d\n", rv); + rv = 1; + goto err; + } + + rv = 0; + +err: + + mem_unref(ctx); + +end: + + return rv; +} + +int cmd_region_rammode(char *name) +{ + int rv; + struct mem_ctx *ctx; + struct cxl_region *region; + + // Initialize variables + rv = 1; + + // Validate Privileges + if ( getuid() != 0 ) + { + fprintf(stderr, "Error: Command must be run as root\n"); + rv = -EACCES; + goto end; + } + + // Validate Inputs + if (name == NULL) + { + fprintf(stderr, "Error: Missing region\n"); + rv = -EINVAL; + goto end; + } + + // Get mem context + rv = mem_new(&ctx); + if (rv != 0) + { + fprintf(stderr, "Error: Failed to obtain mem context: %d\n", rv); + rv = 1; + goto end; + } + mem_log_set_destination(ctx, CLI_LOG_DST, NULL); + mem_log_set_priority(ctx, CLI_LOG_LEVEL); + + // Get Region + region = mem_get_region(ctx, name); + if (region == NULL) + { + fprintf(stderr, "Error: Could not obtain region: %s\n", name); + rv = 1; + goto err; + } + + // Enable Ram mode + rv = mem_region_rammode(ctx, region); + if (rv != 0) + { + fprintf(stderr, "Error: Enable of systemram mode failed: %d\n", rv); + rv = 1; + goto err; + } + + rv = 0; + +err: + + mem_unref(ctx); + +end: + + return rv; +} + +int cmd_region_set_blk_state(char *name, int offset, int state) +{ + int rv, num; + struct mem_ctx *ctx; + struct cxl_region *region; + + rv = 1; + + // Validate Privileges + if ( getuid() != 0 ) + { + fprintf(stderr, "Error: Command must be run as root\n"); + rv = -EACCES; + goto end; + } + + // Validate Inputs + if (name == NULL) + { + fprintf(stderr, "Error: Missing region\n"); + rv = -EINVAL; + goto end; + } + + if (offset < -1) + { + fprintf(stderr, "Error: Invalid index\n"); + rv = -EINVAL; + goto end; + } + + if (state <0 || state >= LMPL_MAX) + { + fprintf(stderr, "Error: Invalid state\n"); + rv = -EINVAL; + goto end; + } + + // Get mem contex + rv = mem_new(&ctx); + if (rv != 0) + { + fprintf(stderr, "Error: Failed to obtain mem context: %d\n", rv); + rv = 1; + goto end; + } + mem_log_set_destination(ctx, CLI_LOG_DST, NULL); + mem_log_set_priority(ctx, CLI_LOG_LEVEL); + + region = mem_get_region(ctx, name); + if (region == NULL) + { + fprintf(stderr, "Error: Could not obtain region\n"); + rv = 1; + goto err; + } + + if (offset == -1) + { + num = mem_region_num_blocks(ctx, region); + for ( int i = 0 ; i < num ; i++) + { + // Set the state + rv = mem_region_set_blk_state(ctx, region, i, state); + if (rv < 0) + { + fprintf(stderr, "Error: Could not set state of memory block %d in region %s: %d\n", i, cxl_region_get_devname(region), rv); + rv = 1; + goto err; + } + } + } + else + { + // Set the state + rv = mem_region_set_blk_state(ctx, region, offset, state); + if (rv < 0) + { + fprintf(stderr, "Error: Could not set state of memory block %d in region %s: %d\n", offset, cxl_region_get_devname(region), rv); + rv = 1; + goto err; + } + } + + rv = 0; + +err: + + mem_unref(ctx); + +end: + + return rv; +} + +int cmd_set_blk_state(int index, int state) +{ + int rv; + struct mem_ctx *ctx; + + rv = 1; + + // Validate Privileges + if ( getuid() != 0 ) + { + fprintf(stderr, "Error: Command must be run as root\n"); + rv = -EACCES; + goto end; + } + + // Validate Inputs + if (index < 0) + { + fprintf(stderr, "Error: Invalid index\n"); + rv = -EINVAL; + goto end; + } + + if (state < 0 || state >= LMPL_MAX) + { + fprintf(stderr, "Error: Invalid state\n"); + rv = -EINVAL; + goto end; + } + + // Get mem contex + rv = mem_new(&ctx); + if (rv != 0) + { + fprintf(stderr, "Error: Failed to obtain mem context: %d\n", rv); + rv = 1; + goto end; + } + mem_log_set_destination(ctx, CLI_LOG_DST, NULL); + mem_log_set_priority(ctx, CLI_LOG_LEVEL); + + // Set the state + rv = mem_blkid_set_state(ctx, index, state); + if (rv != 0) + { + fprintf(stderr, "Error: Could not set state of memory block. %d\n", rv); + rv = 1; + goto err; + } + + rv = 0; + +err: + + mem_unref(ctx); + +end: + + return rv; +} + +int cmd_set_system_policy(int policy) +{ + int rv; + struct mem_ctx *ctx; + + rv = 1; + + // Validate Privileges + if ( getuid() != 0 ) + { + fprintf(stderr, "Error: Command must be run as root\n"); + rv = -EACCES; + goto end; + } + + // Validate Inputs + if (policy < 0 || policy >= LMPL_MAX) + { + fprintf(stderr, "Error: Inavlid policy\n"); + rv = 1; + goto end; + } + + // Get mem contex + rv = mem_new(&ctx); + if (rv != 0) + { + fprintf(stderr, "Error: Failed to obtain mem context: %d\n", rv); + rv = 1; + goto end; + } + mem_log_set_destination(ctx, CLI_LOG_DST, NULL); + mem_log_set_priority(ctx, CLI_LOG_LEVEL); + + // Set the policy + rv = mem_system_set_policy(ctx, policy); + if (rv != 0) + { + fprintf(stderr, "Error: Could not set policy. %d\n", rv); + rv = 1; + goto err; + } + + rv = 0; + +err: + + mem_unref(ctx); + +end: + + return rv; +} + +int cmd_show_blk_device(int id) +{ + int rv; + struct mem_ctx *ctx; + + rv = 1; + + // Validate Inputs + if (id < 1) + { + fprintf(stderr, "Error: Missing block index\n"); + rv = -EINVAL; + goto end; + } + + // Get mem contex + rv = mem_new(&ctx); + if (rv != 0) + { + fprintf(stderr, "Error: Failed to obtain mem context: %d\n", rv); + rv = 1; + goto end; + } + mem_log_set_destination(ctx, CLI_LOG_DST, NULL); + mem_log_set_priority(ctx, CLI_LOG_LEVEL); + + // Get phys device + rv = mem_blkid_get_device(ctx, id); + if (rv < 0) + { + fprintf(stderr, "Error: Could not obtain phys_device. %d\n", rv); + rv = 1; + goto err; + } + + printf("%d\n", rv); + rv = 0; + +err: + + mem_unref(ctx); + +end: + + return rv; +} + +int cmd_show_blk_isonline(int id) +{ + int rv; + struct mem_ctx *ctx; + struct mem_blk *blk; + + rv = 1; + + // Validate Inputs + if (id < 1) + { + fprintf(stderr, "Error: Missing block index\n"); + rv = -EINVAL; + goto end; + } + + // Get mem contex + rv = mem_new(&ctx); + if (rv != 0) + { + fprintf(stderr, "Error: Failed to obtain mem context: %d\n", rv); + rv = 1; + goto end; + } + mem_log_set_destination(ctx, CLI_LOG_DST, NULL); + mem_log_set_priority(ctx, CLI_LOG_LEVEL); + + // Check if memory block is online + rv = -1; + mem_blk_foreach(ctx, blk) + if (id == mem_blk_get_id(blk)) + { + rv = mem_blk_is_online(blk); + break; + } + printf("%d\n", rv); + + rv = 0; + + mem_unref(ctx); + +end: + + return rv; +} + +int cmd_show_blk_isremovable(int id) +{ + int rv; + struct mem_ctx *ctx; + + rv = 1; + + // Validate Inputs + if (id < 1) + { + fprintf(stderr, "Error: Missing block index\n"); + rv = -EINVAL; + goto end; + } + + // Get mem contex + rv = mem_new(&ctx); + if (rv != 0) + { + fprintf(stderr, "Error: Failed to obtain mem context: %d\n", rv); + rv = 1; + goto end; + } + mem_log_set_destination(ctx, CLI_LOG_DST, NULL); + mem_log_set_priority(ctx, CLI_LOG_LEVEL); + + rv = mem_blkid_is_removable(ctx, id); + printf("%d\n", rv); + + rv = 0; + + mem_unref(ctx); + +end: + + return rv; +} + +int cmd_show_blk_node(int id) +{ + int rv; + struct mem_ctx *ctx; + + rv = 1; + + // Validate Inputs + if (id < 1) + { + fprintf(stderr, "Error: Missing block index\n"); + rv = -EINVAL; + goto end; + } + + // Get mem contex + rv = mem_new(&ctx); + if (rv != 0) + { + fprintf(stderr, "Error: Failed to obtain mem context: %d\n", rv); + rv = 1; + goto end; + } + mem_log_set_destination(ctx, CLI_LOG_DST, NULL); + mem_log_set_priority(ctx, CLI_LOG_LEVEL); + + rv = mem_blkid_get_node(ctx, id); + printf("%d\n", rv); + + rv = 0; + + mem_unref(ctx); + +end: + + return rv; +} + +int cmd_show_blk_state(int id) +{ + int rv; + struct mem_ctx *ctx; + + rv = 1; + + // Validate Inputs + if (id < 1) + { + fprintf(stderr, "Error: Missing block index\n"); + rv = -EINVAL; + goto end; + } + + // Get mem contex + rv = mem_new(&ctx); + if (rv != 0) + { + fprintf(stderr, "Error: Failed to obtain mem context: %d\n", rv); + rv = 1; + goto end; + } + mem_log_set_destination(ctx, CLI_LOG_DST, NULL); + mem_log_set_priority(ctx, CLI_LOG_LEVEL); + + rv = mem_blkid_get_state(ctx, id); + if (rv < 0) + { + fprintf(stderr, "Error: Could not obtain block state: %d\n", rv); + rv = 1; + goto err; + } + + printf("%s\n", mem_lmpl(rv)); + rv = 0; + +err: + + mem_unref(ctx); + +end: + + return rv; +} + +int cmd_show_blk_zones(int id) +{ + int i, rv; + unsigned long u; + struct mem_ctx *ctx; + + rv = 1; + + // Validate Inputs + if (id < 1) + { + fprintf(stderr, "Error: Missing block index\n"); + rv = -EINVAL; + goto end; + } + + // Get mem contex + rv = mem_new(&ctx); + if (rv != 0) + { + fprintf(stderr, "Error: Failed to obtain mem context: %d\n", rv); + rv = 1; + goto end; + } + mem_log_set_destination(ctx, CLI_LOG_DST, NULL); + mem_log_set_priority(ctx, CLI_LOG_LEVEL); + + u = mem_blkid_get_zones(ctx, id); + + for ( i = 0 ; i < LMZN_MAX ; i++ ) + { + if ((0x01 << i) & u) + printf("%s ", mem_lmzn(i)); + } + printf("\n"); + + rv = 0; + + mem_unref(ctx); + +end: + + return rv; +} + +int cmd_show_blocks(int online, int offline, char *region_name) +{ + int rv; + struct mem_ctx *ctx; + struct mem_blk *blk; + struct cxl_region *region; + + rv = 1; + + // Get mem contex + rv = mem_new(&ctx); + if (rv != 0) + { + fprintf(stderr, "Error: Failed to obtain mem context: %d\n", rv); + rv = 1; + goto end; + } + mem_log_set_destination(ctx, CLI_LOG_DST, NULL); + mem_log_set_priority(ctx, CLI_LOG_LEVEL); + + mem_blk_foreach(ctx, blk) + { + if (online && !mem_blk_is_online(blk)) + continue; + + if (offline && mem_blk_is_online(blk)) + continue; + + region = mem_blk_get_region(blk); + + if (region_name != NULL && region == NULL) + continue; + + if (region_name != NULL && region != NULL && strcmp(region_name, cxl_region_get_devname(region))) + continue; + + printf("%d\n", mem_blk_get_id(blk)); + } + + rv = 0; + + mem_unref(ctx); + +end: + + return rv; +} + +int cmd_show_capacity(int online, int offline, char *region_name, int human) +{ + unsigned long long size; + double d; + int i, rv; + struct mem_ctx *ctx; + struct cxl_region *region; + + i = 0; + + // Get mem contex + rv = mem_new(&ctx); + if (rv != 0) + { + fprintf(stderr, "Error: Failed to obtain mem context: %d\n", rv); + rv = 1; + goto end; + } + mem_log_set_destination(ctx, CLI_LOG_DST, NULL); + mem_log_set_priority(ctx, CLI_LOG_LEVEL); + + if (region_name == NULL) + { + if (online) + size = mem_system_get_capacity_online(ctx); + else if (offline) + size = mem_system_get_capacity_offline(ctx); + else + size = mem_system_get_capacity(ctx); + } + else + { + region = mem_get_region(ctx, region_name); + if (region == NULL) + { + fprintf(stderr, "Error: Could not obtain region: %s\n", region_name); + goto end; + } + + if (online) + size = mem_region_get_capacity_online(ctx, region); + else if (offline) + size = mem_region_get_capacity_offline(ctx, region); + else + size = mem_region_get_capacity(ctx, region); + } + + if (human) + { + char units[] = {' ', 'K', 'M', 'G', 'T'}; + d = (double) size; + while ((d > 1024) && i++ < 5) + d /= 1024; + printf("%0.2f %c\n", d, units[i]); + } + else + printf("%llu\n", size); + + rv = 0; + + mem_unref(ctx); + +end: + + return rv; +} + +int cmd_show_memdev_interleave_granulariy(char *name) +{ + int rv; + struct mem_ctx *ctx; + struct cxl_memdev *memdev; + + // Initialize variables + rv = 1; + ctx = NULL; + + // Validate Inputs + if (name == NULL) + { + fprintf(stderr, "Error: Missing memdev\n"); + rv = -EINVAL; + goto end; + } + + // Get mem context + rv = mem_new(&ctx); + if (rv != 0) + { + fprintf(stderr, "Error: Failed to obtain mem context: %d\n", rv); + rv = 1; + goto end; + } + mem_log_set_destination(ctx, CLI_LOG_DST, NULL); + mem_log_set_priority(ctx, CLI_LOG_LEVEL); + + // Get memdev + memdev = mem_get_memdev(ctx, name); + if (memdev == NULL) + { + fprintf(stderr, "Error: Could not obtain memdev: %s\n", name); + rv = 1; + goto err; + } + + rv = mem_memdev_get_interleave_granularity(ctx, memdev); + printf("%d\n", rv); + + rv = 0; + +err: + + mem_unref(ctx); + +end: + + return rv; +} + +int cmd_show_memdev_isavailable(char *name) +{ + int rv; + struct mem_ctx *ctx; + struct cxl_memdev *memdev; + + // Initialize variables + rv = 1; + + // Validate Inputs + if (name == NULL) + { + fprintf(stderr, "Error: Missing memdev\n"); + rv = -EINVAL; + goto end; + } + + // Get mem context + rv = mem_new(&ctx); + if (rv != 0) + { + fprintf(stderr, "Error: Failed to obtain mem context: %d\n", rv); + rv = 1; + goto end; + } + mem_log_set_destination(ctx, CLI_LOG_DST, NULL); + mem_log_set_priority(ctx, CLI_LOG_LEVEL); + + // Get memdev + memdev = mem_get_memdev(ctx, name); + if (memdev == NULL) + { + fprintf(stderr, "Error: Could not obtain memdev: %s\n", name); + rv = 1; + goto err; + } + + rv = mem_memdev_is_available(ctx, memdev); + printf("%d\n", rv); + + rv = 0; + +err: + + mem_unref(ctx); + +end: + + return rv; +} + +int cmd_show_memdevs(char *memdev_name, char *region_name, int human) +{ + int rv; + struct mem_ctx *ctx; + struct cxl_memdev *memdev; + struct cxl_endpoint *endpoint; + struct cxl_port *port; + struct cxl_decoder *decoder; + struct cxl_region *region; + int i, num; + struct cxl_memdev **array; + + rv = 1; + i = 0; + array = NULL; + + // Get mem context + rv = mem_new(&ctx); + if (rv != 0) + { + fprintf(stderr, "Error: Failed to obtain mem context: %d\n", rv); + rv = 1; + goto end; + } + mem_log_set_destination(ctx, CLI_LOG_DST, NULL); + mem_log_set_priority(ctx, CLI_LOG_LEVEL); + + // Get the number of memdevs + num = mem_num_memdevs(ctx); + + if (opts[CLOP_NUM].set) + { + printf("%d\n", num); + goto end; + } + + if (num <= 0) + { + rv = 0; + goto end; + } + + // Get a sorted list of memdevs + array = mem_get_memdevs(ctx); + if (array == NULL) + { + fprintf(stderr, "Error: Could not obtain list of memdevs\n"); + rv = 1; + goto err; + } + + printf("Name Enabled Mode Size Host Endpoint Decoder Region FW Version\n"); + printf("------ ------- ------ -------------- ------------ ----------- ----------- ------------ -------------------\n"); + for ( i = 0 ; i < num ; i++) + { + memdev = array[i]; + + if (memdev_name != NULL && strcmp(memdev_name, cxl_memdev_get_devname(memdev))) + continue; + + endpoint = cxl_memdev_get_endpoint(memdev); + port = cxl_endpoint_get_port(endpoint); + decoder = cxl_decoder_get_first(port); + region = cxl_decoder_get_region(decoder); + + int mode = cxl_decoder_get_mode(decoder); + unsigned long long size = cxl_memdev_get_ram_size(memdev); + const char *host = cxl_memdev_get_host(memdev); + const char *reg_name = NULL; + + if (region != NULL) + { + reg_name = cxl_region_get_devname(region); + } + else + reg_name ="-"; + + if (region_name != NULL && strcmp(reg_name, region_name)) + continue; + + printf("%-6s %7d %6s ", + cxl_memdev_get_devname(memdev), + cxl_memdev_is_enabled(memdev), + cxl_decoder_mode_name(mode)); + + if (human) + { + char units[] = {' ', 'K', 'M', 'G', 'T'}; + double d = (double) size; + int i = 0; + while ((d > 1024) && i++ < 5) + d /= 1024; + printf("%12.2f %c ", d, units[i]); + } + else + printf("%14llu ", size); + + printf("%12s %11s %11s %12s %-20s\n", + host, + cxl_endpoint_get_devname(endpoint), + cxl_decoder_get_devname(decoder), + reg_name, + cxl_memdev_get_firmware_verison(memdev) + ); + } + + // Free the sorted array of memdevs + if (array != NULL) + free(array); + + rv = 0; + +err: + + mem_unref(ctx); + +end: + + return rv; +} + +int cmd_show_num_blocks(int online, int offline, char *region_name) +{ + int rv, num; + struct mem_ctx *ctx; + struct cxl_region *region; + + // Initialize variables + num = 0; + + // Get mem contex + rv = mem_new(&ctx); + if (rv != 0) + { + fprintf(stderr, "Error: Failed to obtain mem context: %d\n", rv); + rv = 1; + goto end; + } + mem_log_set_destination(ctx, CLI_LOG_DST, NULL); + mem_log_set_priority(ctx, CLI_LOG_LEVEL); + + if (region_name != NULL) + { + region = mem_get_region(ctx, region_name); + if (region == NULL) + { + fprintf(stderr, "Error: Could not obtain region: %s\n", region_name); + rv = 1; + goto end; + } + + if (online) + num = mem_region_num_blocks_online(ctx, region); + else if (offline) + num = mem_region_num_blocks_offline(ctx, region); + else + num = mem_region_num_blocks(ctx, region); + } + else + { + if (online) + num = mem_system_num_blocks_online(ctx); + else if (offline) + num = mem_system_num_blocks_offline(ctx); + else + num = mem_system_num_blocks(ctx); + } + + printf("%d\n", num); + + rv = 0; + + mem_unref(ctx); + +end: + + return rv; +} + +int cmd_show_num_blocks_offline() +{ + struct mem_ctx *ctx; + int rv; + + // Get mem contex + rv = mem_new(&ctx); + if (rv != 0) + { + fprintf(stderr, "Error: Failed to obtain mem context: %d\n", rv); + rv = 1; + goto end; + } + mem_log_set_destination(ctx, CLI_LOG_DST, NULL); + mem_log_set_priority(ctx, CLI_LOG_LEVEL); + + printf("%d\n", mem_system_num_blocks_offline(ctx)); + + rv = 0; + + mem_unref(ctx); + +end: + + return rv; +} + +int cmd_show_num_blocks_online() +{ + struct mem_ctx *ctx; + int rv; + + // Get mem contex + rv = mem_new(&ctx); + if (rv != 0) + { + fprintf(stderr, "Error: Failed to obtain mem context: %d\n", rv); + rv = 1; + goto end; + } + mem_log_set_destination(ctx, CLI_LOG_DST, NULL); + mem_log_set_priority(ctx, CLI_LOG_LEVEL); + + printf("%d\n", mem_system_num_blocks_online(ctx)); + + rv = 0; + + mem_unref(ctx); + +end: + + return rv; +} + +int cmd_show_num_devices() +{ + int rv; + struct mem_ctx *ctx; + + rv = 1; + + // Get mem context + rv = mem_new(&ctx); + if (rv != 0) + { + fprintf(stderr, "Error: Failed to obtain mem context: %d\n", rv); + rv = 1; + goto end; + } + mem_log_set_destination(ctx, CLI_LOG_DST, NULL); + mem_log_set_priority(ctx, CLI_LOG_LEVEL); + + printf("%d\n", mem_num_memdevs(ctx)); + + rv = 0; + + mem_unref(ctx); + +end: + + return rv; +} + +int cmd_show_num_regions() +{ + int rv; + struct mem_ctx *ctx; + + rv = 1; + + // Get mem cntext + rv = mem_new(&ctx); + if (rv != 0) + { + fprintf(stderr, "Error: Failed to obtain mem context: %d\n", rv); + rv = 1; + goto end; + } + mem_log_set_destination(ctx, CLI_LOG_DST, NULL); + mem_log_set_priority(ctx, CLI_LOG_LEVEL); + + printf("%d\n", mem_num_regions(ctx)); + + rv = 0; + + mem_unref(ctx); + +end: + + return rv; +} + +int cmd_show_region_blk_state(char *name, int offset) +{ + int rv; + struct mem_ctx *ctx; + struct cxl_region *region; + + rv = 1; + + // Validate Inputs + if (name == NULL) + { + fprintf(stderr, "Error: Missing region\n"); + rv = -EINVAL; + goto end; + } + + // Validate Inputs + if (offset < 0) + { + fprintf(stderr, "Error: Missing block index\n"); + rv = -EINVAL; + goto end; + } + + // Get mem contex + rv = mem_new(&ctx); + if (rv != 0) + { + fprintf(stderr, "Error: Failed to obtain mem context: %d\n", rv); + rv = 1; + goto end; + } + mem_log_set_destination(ctx, CLI_LOG_DST, NULL); + mem_log_set_priority(ctx, CLI_LOG_LEVEL); + + region = mem_get_region(ctx, name); + if (region == NULL) + { + fprintf(stderr, "Error: Could not obtain region\n"); + rv = 1; + goto err; + } + + rv = mem_region_get_blk_state(ctx, region, offset); + if (rv < 0) + { + fprintf(stderr, "Error: Could not obtain state of block %d in region %s: %d\n", offset, cxl_region_get_devname(region), rv); + rv = 1; + goto err; + } + + printf("%s\n", mem_lmpl(rv)); + rv = 0; + +err: + + mem_unref(ctx); + +end: + + return rv; +} + +int cmd_show_region_isenabled(char *name) +{ + int rv; + struct mem_ctx *ctx; + struct cxl_region *region; + + rv = 1; + + // Validate Inputs + if (name == NULL) + { + fprintf(stderr, "Error: Missing region\n"); + rv = -EINVAL; + goto end; + } + + // Get mem context + rv = mem_new(&ctx); + if (rv != 0) + { + fprintf(stderr, "Error: Failed to obtain mem context: %d\n", rv); + rv = 1; + goto end; + } + mem_log_set_destination(ctx, CLI_LOG_DST, NULL); + mem_log_set_priority(ctx, CLI_LOG_LEVEL); + + region = mem_get_region(ctx, name); + if (region == NULL) + { + fprintf(stderr, "Error: Could not obtain region: %s\n", name); + rv = 1; + goto err; + } + + rv = cxl_region_is_enabled(region); + printf("%d\n", rv); + + rv = 0; + +err: + + mem_unref(ctx); + +end: + + return rv; +} + +int cmd_show_regions(char *name, int human) +{ + int rv; + struct mem_ctx *ctx; + struct cxl_decoder *d; + struct cxl_region **regions, *region; + struct cxl_memdev *memdev; + int i, j, num, num_regions; + + rv = 1; + + // Get mem context + rv = mem_new(&ctx); + if (rv != 0) + { + fprintf(stderr, "Error: Failed to obtain mem context: %d\n", rv); + rv = 1; + goto end; + } + mem_log_set_destination(ctx, CLI_LOG_DST, NULL); + mem_log_set_priority(ctx, CLI_LOG_LEVEL); + + num_regions = mem_num_regions(ctx); + regions = mem_get_regions(ctx); + + if (num_regions <= 0 || regions == NULL) + { + rv = 0; + goto err; + } + + printf("Name Enabled Dax Mode Size Ways Granularity Num Blocks Blocks Online Devices\n"); + printf("--------- ------- --- ------ -------------- ---- ----------- ---------- ------------- -------\n"); + + for ( i = 0 ; i < num_regions ; i++) + { + region = regions[i]; + + // Skip if a region name was provided and doesn't match this region name + if (name != NULL && strcmp(name, cxl_region_get_devname(region))) + continue; + + printf("%-9s %7d %3d %6s ", + cxl_region_get_devname(region), + cxl_region_is_enabled(region), + mem_region_is_daxmode(ctx, region), + cxl_decoder_mode_name(cxl_region_get_mode(region))); + + if (human) + { + char units[] = {' ', 'K', 'M', 'G', 'T'}; + double d = (double) cxl_region_get_size(region); + int i = 0; + while ((d > 1024) && i++ < 5) + d /= 1024; + printf("%12.2f %c ", d, units[i]); + } + else + printf("%14llu ", cxl_region_get_size(region)); + + printf("%4d %11d %10d %13d ", + cxl_region_get_interleave_ways(region), + cxl_region_get_interleave_granularity(region), + mem_region_num_blocks(ctx, region), + mem_region_num_blocks_online(ctx, region) + ); + + num = cxl_region_get_interleave_ways(region); + if (cxl_region_decode_is_committed(region)) + { + for ( j = 0 ; j < num ; j++) + { + d = cxl_region_get_target_decoder(region, j); + if (d == NULL) + { + printf("%d:%s ", j, "-"); + continue; + } + + memdev = cxl_decoder_get_memdev(d); + if (memdev != NULL) + printf("%d:%s ", j, cxl_memdev_get_devname(memdev)); + else + printf("%d:%s ", j, "-"); + } + } + else + printf(" -"); + printf("\n"); + } + + rv = 0; + +err: + + mem_unref(ctx); + +end: + + return rv; +} + +int cmd_show_system_blocksize(int human) +{ + int rv; + unsigned long long size; + struct mem_ctx *ctx; + + // Get mem contex + rv = mem_new(&ctx); + if (rv != 0) + { + fprintf(stderr, "Error: Failed to obtain mem context: %d\n", rv); + rv = 1; + goto end; + } + mem_log_set_destination(ctx, CLI_LOG_DST, NULL); + mem_log_set_priority(ctx, CLI_LOG_LEVEL); + + size = mem_system_get_blocksize(ctx); + + if (human) + { + char units[] = {' ', 'K', 'M', 'G', 'T'}; + double d = (double) size; + int i = 0; + while ((d > 1024) && i++ < 5) + d /= 1024; + printf("%0.2f %c\n", d, units[i]); + } + else + printf("%llu\n", size); + + rv = 0; + + mem_unref(ctx); + +end: + + return rv; +} + +int cmd_show_system_policy() +{ + struct mem_ctx *ctx; + int rv; + + // Get mem contex + rv = mem_new(&ctx); + if (rv != 0) + { + fprintf(stderr, "Error: Failed to obtain mem context: %d\n", rv); + rv = 1; + goto end; + } + mem_log_set_destination(ctx, CLI_LOG_DST, NULL); + mem_log_set_priority(ctx, CLI_LOG_LEVEL); + + rv = mem_system_get_policy(ctx); + printf("%s\n", mem_lmpl(rv)); + + rv = 0; + + mem_unref(ctx); + +end: + + return rv; +} + +int run() +{ + int rv; + + rv = 1; + + switch(opts[CLOP_CMD].val) + { + case CLCM_INFO: + rv = cmd_info(); + break; + + case CLCM_LIST: + rv = cmd_list(opts[CLOP_ONLINE].set, opts[CLOP_OFFLINE].set, opts[CLOP_REGION].str); + break; + + case CLCM_BLOCK_ONLINE: + if (opts[CLOP_ALL].set) + rv = cmd_blk_online(-1, 0); + else if (opts[CLOP_BLOCK].num == 0) + rv = cmd_blk_online(1, opts[CLOP_BLOCK].val); + else if (opts[CLOP_BLOCK].num >= 0) + rv = cmd_blk_online(opts[CLOP_BLOCK].num, *((int*)opts[CLOP_BLOCK].buf)); + break; + + case CLCM_BLOCK_OFFLINE: + if (opts[CLOP_BLOCK].num == 0) + rv = cmd_blk_offline(1, opts[CLOP_BLOCK].val); + else if (opts[CLOP_BLOCK].num > 0) + rv = cmd_blk_offline(opts[CLOP_BLOCK].num, *((int*)opts[CLOP_BLOCK].buf)); + break; + + case CLCM_REGION_CREATE: + if (opts[CLOP_ALL].set) + rv = cmd_region_create(opts[CLOP_GRANULARITY].u32, 0, NULL); + else + rv = cmd_region_create(opts[CLOP_GRANULARITY].u32, opts[CLOP_DEVICE].num, (char**)opts[CLOP_DEVICE].buf); + break; + + case CLCM_REGION_DAXMODE: + rv = cmd_region_daxmode(opts[CLOP_REGION].str); + break; + + case CLCM_REGION_RAMMODE: + rv = cmd_region_rammode(opts[CLOP_REGION].str); + break; + + case CLCM_REGION_DELETE: + if (opts[CLOP_ALL].set) + rv = cmd_region_delete(NULL); + else + rv = cmd_region_delete(opts[CLOP_REGION].str); + break; + + case CLCM_REGION_DISABLE: + rv = cmd_region_disable(opts[CLOP_REGION].str); + break; + + case CLCM_REGION_ENABLE: + rv = cmd_region_enable(opts[CLOP_REGION].str); + break; + + case CLCM_SET_BLOCK_STATE: + if (opts[CLOP_ALL].set && (opts[CLOP_ONLINE].set || opts[CLOP_MOVABLE].set)) + rv = cmd_blk_online(-1, 0); + else if (opts[CLOP_ONLINE].set || opts[CLOP_MOVABLE].set) + rv = cmd_set_blk_state(opts[CLOP_BLOCK].val, LMPL_MOVABLE); + else if (opts[CLOP_KERNEL].set) + rv = cmd_set_blk_state(opts[CLOP_BLOCK].val, LMPL_KERNEL); + else + rv = cmd_set_blk_state(opts[CLOP_BLOCK].val, LMPL_OFFLINE); + break; + + case CLCM_SET_REGION_BLOCK_STATE: + { + int mode; + + if (opts[CLOP_ONLINE].set || opts[CLOP_MOVABLE].set) + mode = LMPL_MOVABLE; + else if (opts[CLOP_KERNEL].set) + mode = LMPL_KERNEL; + else + mode = LMPL_OFFLINE; + + if (opts[CLOP_BLOCK].num > 0) + { + rv = 0; + int start = *((int*)opts[CLOP_BLOCK].buf); + for (int i = 0 ; i < (int) opts[CLOP_BLOCK].num ; i++) + rv += cmd_region_set_blk_state(opts[CLOP_REGION].str, start+i, mode); + } + else if (opts[CLOP_ALL].set) + { + rv = cmd_region_set_blk_state(opts[CLOP_REGION].str, -1, mode); + } + else + rv = cmd_region_set_blk_state(opts[CLOP_REGION].str, opts[CLOP_BLOCK].val, mode); + } + break; + + case CLCM_SET_SYSTEM_POLICY: + if (opts[CLOP_ONLINE].set) + rv = cmd_set_system_policy(LMPL_ONLINE); + else if (opts[CLOP_MOVABLE].set) + rv = cmd_set_system_policy(LMPL_MOVABLE); + else if (opts[CLOP_KERNEL].set) + rv = cmd_set_system_policy(LMPL_KERNEL); + else + rv = cmd_set_system_policy(LMPL_OFFLINE); + break; + + case CLCM_SHOW_BLK_ISONLINE: + rv = cmd_show_blk_isonline(opts[CLOP_BLOCK].val); + break; + + case CLCM_SHOW_BLK_ISREMOVABLE: + rv = cmd_show_blk_isremovable(opts[CLOP_BLOCK].val); + break; + + case CLCM_SHOW_BLK_NODE: + rv = cmd_show_blk_node(opts[CLOP_BLOCK].val); + break; + + case CLCM_SHOW_BLK_PHYSDEVICE: + rv = cmd_show_blk_node(opts[CLOP_BLOCK].val); + break; + + case CLCM_SHOW_BLK_STATE: + if (opts[CLOP_REGION].set) + rv = cmd_show_region_blk_state(opts[CLOP_REGION].str, opts[CLOP_BLOCK].val); + else + rv = cmd_show_blk_state(opts[CLOP_BLOCK].val); + break; + + case CLCM_SHOW_BLK_ZONES: + rv = cmd_show_blk_zones(opts[CLOP_BLOCK].val); + break; + + case CLCM_SHOW_BLOCKS: + rv = cmd_show_blocks(opts[CLOP_ONLINE].set, opts[CLOP_OFFLINE].set, opts[CLOP_REGION].str); + break; + + case CLCM_SHOW_CAPACITY: + rv = cmd_show_capacity(opts[CLOP_ONLINE].set, opts[CLOP_OFFLINE].set, opts[CLOP_REGION].str, opts[CLOP_HUMAN].set); + break; + + case CLCM_SHOW_DEVICES: + rv = cmd_show_memdevs(opts[CLOP_DEVICE].str, opts[CLOP_REGION].str, opts[CLOP_HUMAN].set); + break; + + case CLCM_SHOW_DEVICE_ISAVAILABLE: + rv = cmd_show_memdev_isavailable(opts[CLOP_DEVICE].str); + break; + + case CLCM_SHOW_DEVICE_INTERLEAVE_GRANULARITY: + rv = cmd_show_memdev_interleave_granulariy(opts[CLOP_DEVICE].str); + break; + + case CLCM_SHOW_REGIONS: + rv = cmd_show_regions(opts[CLOP_REGION].str, opts[CLOP_HUMAN].set); + break; + + case CLCM_SHOW_NUM_BLOCKS: + rv = cmd_show_num_blocks(opts[CLOP_ONLINE].set, opts[CLOP_OFFLINE].set, opts[CLOP_REGION].str); + break; + + case CLCM_SHOW_NUM_DEVICES: + rv = cmd_show_num_devices(); + break; + + case CLCM_SHOW_NUM_REGIONS: + rv = cmd_show_num_regions(); + break; + + case CLCM_SHOW_REGION_ISENABLED: + rv = cmd_show_region_isenabled(opts[CLOP_REGION].str); + break; + + case CLCM_SHOW_SYSTEM_BLOCKSIZE: + rv = cmd_show_system_blocksize(opts[CLOP_HUMAN].set);; + break; + + case CLCM_SHOW_SYSTEM_POLICY: + rv = cmd_show_system_policy(); + break; + + default: + rv = 1; + break; + } + + return rv; +} + +int main(int argc, char *argv[]) +{ + int rv; + + // Initialize variables + rv = 1; + + // Parse options + rv = options_parse(argc, argv); + if (rv != 0) + { + fprintf(stderr, "Error: Failed to parse command line parameters: %d\n", rv); + goto end; + } + + // Verify command was specified + if (opts[CLOP_CMD].set == 0) + { + fprintf(stderr, "Error: No command specified\n"); + rv = 1; + goto err; + } + + // Execute command + rv = run(); + if (rv != 0) + fprintf(stderr, "Error: Command failed: %d\n", rv); + +err: + + options_free(); + +end: + + return rv; +} + diff --git a/libmem.c b/libmem.c new file mode 100644 index 0000000..6cfd214 --- /dev/null +++ b/libmem.c @@ -0,0 +1,2562 @@ +/** + * @file libmem.c + * + * @brief Code file for memory management library + * + * @copyright Copyright (C) 2024 Jackrabbit Founders LLC. All rights reserved. + * + * @date Jul 2024 + * @author Barrett Edwards + */ + +/* INCLUDES ==================================================================*/ + +/* printf() + */ +#include + +/* calloc() + * free() + */ +#include + +/* memset() + * memcpy() + */ +#include + +/* open() + */ +#include + +/* close() + * unlink() + */ +#include + +/* errno + */ +#include + +/* opendir() + */ +#include + +/* LOG_* Macros + */ +#include + +/* cxl objects + * cxl_new() + */ +#include + +/* struct daxctl_region + */ +#include + +#include + +/* log_init() + * log_free() + * log_dbg() + * log_info() + * log_err(); + */ +#include "log.h" + +#include "libmem.h" + +/* MACROS ====================================================================*/ + +#define LMLN_SYSFS_ATTR_SIZE 1024 +#define LMLN_FILEPATH 1024 +#define LMFP_MEM_DIR "/sys/devices/system/memory" + +/* ENUMERATIONS ==============================================================*/ + +/* STRUCTS ===================================================================*/ + +/** + * Object representing a kernel memory block + */ +struct mem_blk +{ + int id; + int node; + int online; + int device; + int removable; + int state; + unsigned long int valid_zones; + struct mem_ctx *ctx; +}; + +/** + * Memory library context + */ +struct mem_ctx +{ + struct log_ctx *log; // Must be first for mem_set_log_fn + int refcount; + int num; + int num_regions; + struct mem_blk *blocks; + struct cxl_ctx *cxl; + struct cxl_region **regions; +}; + +/* GLOBAL VARIABLES ==========================================================*/ + +/** + * String representtaion of enum _LMPL + */ +const char *_LMPL[] = +{ + "offline", + "online", + "online_kernel", + "online_movable" +}; + +/** + * String representation of enum _LMST + */ +const char *_LMST[] = +{ + "offline", + "online", + "going-offline", +}; + +/** + * String representation of enum _LMZN + */ +const char *_LMZN[] = +{ + "DMA", + "DMA32", + "Normal", + "Movable", + "none", +}; + +/* PROTOTYPES ================================================================*/ + +// Static methods for sysfs read / write +static int mem_sysfs_read(struct mem_ctx *ctx, const char *path, char *buf); +static int mem_sysfs_write(struct mem_ctx *ctx, const char *path, const char *buf); + +// Compare functions for qsort +int mem_compare_cxl_memdevs(const void* a, const void* b); +int mem_compare_cxl_regions(const void* a, const void* b); +int mem_compare_ints(const void* a, const void* b); +int mem_compare_mem_blks(const void* a, const void* b); + +static int mem_blk_init(struct mem_ctx *ctx); + +/* FUNCTIONS =================================================================*/ + +int mem_blk_get_device(struct mem_blk *blk) +{ + return blk->device; +} + +/** + * Get the first memory block in the system + */ +struct mem_blk *mem_blk_get_first(struct mem_ctx *ctx) +{ + int rv; + struct mem_blk *blk; + + blk = NULL; + + if (ctx->blocks == NULL) + { + rv = mem_blk_init(ctx); + if (rv != 0) + { + err(ctx, "mem_blk_init() failed: %d", rv); + goto end; + } + } + + if (ctx->num > 0) + blk = &ctx->blocks[0]; + +end: + + return blk; +} + +int mem_blk_get_id(struct mem_blk *blk) +{ + return blk->id; +} + +/** + * Get the next memory block in the system + */ +struct mem_blk *mem_blk_get_next(struct mem_blk *blk) +{ + int i; + struct mem_ctx *ctx; + struct mem_blk *next; + + ctx = blk->ctx; + next = NULL; + + for ( i = 0 ; i < ctx->num ; i++ ) + if (blk->id == ctx->blocks[i].id) + break; + i++; + + if (i < ctx->num) + next = &ctx->blocks[i]; + + return next; +} + +int mem_blk_get_node(struct mem_blk *blk) +{ + return blk->node; +} + +struct cxl_region *mem_blk_get_region(struct mem_blk *blk) +{ + int num; + struct cxl_region *region, **regions; + struct mem_ctx *ctx; + unsigned long long block_size, base, size, end, addr; + + ctx = blk->ctx; + region = NULL; + num = mem_num_regions(ctx); + + // If there are no regions then return null + if (num == 0) + goto end; + + // Get memory block size in bytes + block_size = mem_system_get_blocksize(ctx); + if (block_size == 0) + { + err(ctx, "Unable to read system memory block size"); + goto end; + } + addr = block_size * blk->id; + + regions = mem_get_regions(ctx); + if (regions == NULL) + { + err(ctx, "Could not obtain regions"); + goto end; + } + + for ( int i = 0 ; i < num ; i++) + { + region = regions[i]; + + // Get region base address + base = cxl_region_get_resource(region); + if (base == 0 || base == 0xFFFFFFFFFFFFFFFF) + { + num = 0; + err(ctx, "Unable to get cxl region %s resource address", cxl_region_get_devname(region)); + goto end; + } + + // Get region size in bytes + size = cxl_region_get_size(region); + if (size == 0) + { + num = 0; + warn(ctx, "Region size was zero for region %s", cxl_region_get_devname(region)); + goto end; + } + end = base + size; + + if (base <= addr && addr < end ) + goto end; + } + + region = NULL; + +end: + + return region; +} + +int mem_blk_get_state(struct mem_blk *blk) +{ + if (blk->state == LMST_OFFLINE) + return LMPL_OFFLINE; + + else if (blk->valid_zones & LMZM_DMA) + return LMPL_KERNEL; + + else if (blk->valid_zones & LMZM_DMA32) + return LMPL_KERNEL; + + else if (blk->valid_zones & LMZM_NORMAL) + return LMPL_ONLINE; + + else if (blk->valid_zones & LMZM_MOVABLE) + return LMPL_MOVABLE; + + else + return LMPL_ONLINE; +} + +unsigned long mem_blk_get_zones(struct mem_blk *blk) +{ + return blk->valid_zones; +} + +static int mem_blk_init(struct mem_ctx *ctx) +{ + int rv, i, num, index; + DIR *d; + struct dirent *e; + struct mem_blk *mb; + char path[LMLN_FILEPATH]; + char buf[LMLN_FILEPATH]; + + // Initialize variables + rv = 1; + num = 0; + i = 0; + + // Validate inputs + // Skip if the blocks array has already been initialized + if (ctx->num > 0) + { + rv = 0; + goto end; + } + + // 1: Count the number of memory directories + + // Open the directory + d = opendir(LMFP_MEM_DIR); + if (d == NULL) + { + err(ctx, "Could not open memory directory for enumeration: %s", LMFP_MEM_DIR); + goto end; + } + + // Walk the directory tree and get the block index numbers + for (e = readdir(d) ; e != NULL ; e = readdir(d)) + if (e->d_type == DT_DIR && sscanf(e->d_name, "memory%d", &index) == 1) + num++; + + // Allocate array for mem_blks + ctx->blocks = calloc(num, sizeof(struct mem_blk)); + ctx->num = num; + + info(ctx, "Found %d Memory Blocks", num); + + // Populate the mem_blk array + rewinddir(d); + + // Walk the directory tree and get the block index numbers + for (e = readdir(d) ; e != NULL ; e = readdir(d)) + if (e->d_type == DT_DIR && sscanf(e->d_name, "memory%d", &index) == 1) + { + mb = &ctx->blocks[i++]; + mb->ctx = ctx; + mb->id = index; + + { + // Open memory directory to search for node link + DIR *d2; + struct dirent *e2; + mb->node = -1; + sprintf(path, "%s/%s", LMFP_MEM_DIR, e->d_name); + d2 = opendir(path); + if (d2 != NULL) + for (e2 = readdir(d2) ; e2 != NULL ; e2 = readdir(d2)) + if (e2->d_type == DT_LNK && !strncmp("node", e2->d_name, 4)) + sscanf(e2->d_name, "node%d", &mb->node); + } + + sprintf(path, "%s/%s/online", LMFP_MEM_DIR, e->d_name); + if (mem_sysfs_read(ctx, path, buf) > 0) + mb->online = strtoul(buf, NULL, 0); + + sprintf(path, "%s/%s/phys_device", LMFP_MEM_DIR, e->d_name); + if (mem_sysfs_read(ctx, path, buf) > 0) + mb->device = strtoul(buf, NULL, 0); + + sprintf(path, "%s/%s/removable", LMFP_MEM_DIR, e->d_name); + if (mem_sysfs_read(ctx, path, buf) > 0) + mb->removable = strtoul(buf, NULL, 0); + + sprintf(path, "%s/%s/state", LMFP_MEM_DIR, e->d_name); + if (mem_sysfs_read(ctx, path, buf) > 0) + for ( int j = 0 ; j < LMZN_MAX ; j++) + if (!strcmp(buf, mem_lmst(j))) + { + mb->state = j; + break; + } + + sprintf(path, "%s/%s/valid_zones", LMFP_MEM_DIR, e->d_name); + if (mem_sysfs_read(ctx, path, buf) > 0) + { + char *state = NULL; + mb->valid_zones = 0; + for (char *t = strtok_r(buf, " ", &state); t ; t = strtok_r(NULL, " ", &state)) + for ( int j = 0 ; j < LMZN_MAX ; j++ ) + if (!strcmp(t, mem_lmzn(j))) + mb->valid_zones |= (0x01 << j); + } + } + + closedir(d); + + // Sort the array + qsort(ctx->blocks, ctx->num, sizeof(struct mem_blk), mem_compare_mem_blks); + + rv = 0; + +end: + + return rv; +} + +int mem_blk_is_online(struct mem_blk *blk) +{ + return blk->online; +} + +int mem_blk_is_removable(struct mem_blk *blk) +{ + return blk->removable; +} + +/** + * Offline a memory block + */ +int mem_blk_offline(struct mem_blk *blk) +{ + int rv, ret, index, state; + DIR *d; + char path[LMLN_FILEPATH]; + struct dirent *e; + struct mem_ctx *ctx; + + // Initialize variables + rv = 0; + ctx = blk->ctx; + + // Open the directory + d = opendir(LMFP_MEM_DIR); + if (d == NULL) + { + err(ctx, "Could not open memory directory for enumeration: %s", LMFP_MEM_DIR); + rv = 1; + goto end; + } + + // Loop through all the directory entries and online the memory block that matches + for (e = readdir(d) ; e != NULL ; e = readdir(d)) + if (e->d_type == DT_DIR && sscanf(e->d_name, "memory%d", &index) == 1 && index == blk->id) + { + state = mem_blk_get_state(blk); + info(ctx, "Found memory block %d. Current State: %d Desired State %d", index, state, LMPL_OFFLINE); + + if ( state == LMPL_OFFLINE ) + { + info(ctx, "Memory block %d already offline. Skipping", index, mem_lmpl(state)); + rv = 0; + goto end; + } + + sprintf(path, "%s/%s/%s", LMFP_MEM_DIR, e->d_name, "online"); + ret = mem_sysfs_write(ctx, path, "0"); + if (ret != 2) + { + err(ctx, "Failed to offline memory block %d", index); + rv += 1; + } + else + info(ctx, "Offlined memory block %d", index); + } +end: + + return rv; +} + +int mem_blk_online(struct mem_blk *blk) +{ + int rv, ret, index, state; + DIR *d; + char path[LMLN_FILEPATH]; + struct dirent *e; + struct mem_ctx *ctx; + + // Initialize variables + rv = 0; + ctx = blk->ctx; + + // Open the directory + d = opendir(LMFP_MEM_DIR); + if (d == NULL) + { + err(ctx, "Could not open memory directory for enumeration: %s", LMFP_MEM_DIR); + rv = 1; + goto end; + } + + // Loop through all the directory entries and online the memory block that matches + for (e = readdir(d) ; e != NULL ; e = readdir(d)) + if (e->d_type == DT_DIR && sscanf(e->d_name, "memory%d", &index) == 1 && index == blk->id) + { + state = mem_blk_get_state(blk); + info(ctx, "Found memory block %d. Current State: %d Desired State %d", index, state, LMPL_MOVABLE); + + if (state == LMPL_MOVABLE) + { + info(ctx, "Memory block %d already in state %s. Skipping", index, mem_lmpl(state)); + rv = 0; + goto end; + } + + if (state != LMPL_OFFLINE) + { + err(ctx, "Failed to online Memory block %d becuase it is not offline: %s", index, mem_lmpl(mem_blk_get_state(blk))); + rv = 1; + goto end; + } + + sprintf(path, "%s/%s/%s", LMFP_MEM_DIR, e->d_name, "state"); + ret = mem_sysfs_write(ctx, path, "online_movable"); + if (ret != 15) + { + err(ctx, "Failed to online memory block %d", index); + rv += 1; + } + else + info(ctx, "Onlined memory block %d", index); + } +end: + + return rv; +} + +/** + * Print out the values of a struct mem_blk + */ +void mem_blk_print(struct mem_blk *blk) +{ + printf("id %d\n", blk->id); + printf("node %d\n", blk->node); + printf("online %d\n", blk->online); + printf("device %d\n", blk->device); + printf("removable %d\n", blk->removable); + printf("state %d - %s\n", blk->state, mem_lmst(blk->state)); + for ( int i = 0 ; i < LMZN_MAX ; i++ ) + { + if ((0x01 << i) & blk->valid_zones) + printf("%s ", mem_lmzn(i)); + } + printf("\n"); +} + +int mem_blk_set_state(struct mem_blk *blk, int state) +{ + int rv, ret, index; + DIR *d; + char path[LMLN_FILEPATH]; + struct dirent *e; + struct mem_ctx *ctx; + + // Initialize variables + rv = 0; + ctx = blk->ctx; + + // Validate inputs + if (state < 0 || state >= LMPL_MAX) + { + err(ctx, "Attempted to set invalid state: %d", state); + rv = 1; + goto end; + } + + // Open the directory + d = opendir(LMFP_MEM_DIR); + if (d == NULL) + { + err(ctx, "Could not open memory directory for enumeration: %s", LMFP_MEM_DIR); + rv = 1; + goto end; + } + + // Loop through all the directory entries and online the memory block that matches + for (e = readdir(d) ; e != NULL ; e = readdir(d)) + if (e->d_type == DT_DIR && sscanf(e->d_name, "memory%d", &index) == 1 && index == blk->id) + { + info(ctx, "Found memory block %d. Current State: %d Desired State %d", index, mem_blk_get_state(blk), state); + if (mem_blk_get_state(blk) == state) + { + info(ctx, "Memory block %d already in state %s. Skipping", index, mem_lmpl(state)); + rv = 0; + goto end; + } + + if (state != LMPL_OFFLINE && mem_blk_get_state(blk) != LMPL_OFFLINE) + { + err(ctx, "Failed to set state of Memory block %d to %s becuase it is not offline: %s", index, mem_lmpl(state), mem_lmpl(mem_blk_get_state(blk))); + rv = 1; + goto end; + } + + sprintf(path, "%s/%s/%s", LMFP_MEM_DIR, e->d_name, "state"); + ret = mem_sysfs_write(ctx, path, mem_lmpl(state)); + if (ret < 0 || ret != (int) (strlen(mem_lmpl(state)) + 1)) + { + err(ctx, "Failed to set state to %s on memory block %d. %d", mem_lmpl(state), index, ret); + rv += 1; + } + else + info(ctx, "Set state to %s on memory block %d", mem_lmpl(state), index); + } +end: + + return rv; +} + +/** + * Get a struct mem_blk* from a memory block ID + */ +struct mem_blk *mem_blkid_get_blk(struct mem_ctx *ctx, int id) +{ + struct mem_blk *blk; + + // Validate Inputs + if (ctx == NULL || id < 0) + return NULL; + + mem_blk_foreach(ctx, blk) + if (blk->id == id) + return blk; + + return NULL; +} + +/** + * Get the physical device for the memory block + */ +int mem_blkid_get_device(struct mem_ctx *ctx, int id) +{ + struct mem_blk *blk; + + mem_blk_foreach(ctx, blk) + if (id == blk->id) + return blk->device; + + return -1; +} + +/** + * Get the numa node for the memory blocks in the system + * @return The number of blocks. 0 if error + */ +int mem_blkid_get_node(struct mem_ctx *ctx, int index) +{ + struct mem_blk *blk; + + mem_blk_foreach(ctx, blk) + if (blk->id == index) + return blk->node; + + return -1; +} + +/** + * Get the onnline state of a memory block + */ +int mem_blkid_get_state(struct mem_ctx *ctx, int id) +{ + struct mem_blk *blk; + + mem_blk_foreach(ctx, blk) + if (id == blk->id) + return blk->state; + + return -1; +} + +/** + * Get a bitfield representing the valid zones* + */ +unsigned long mem_blkid_get_zones(struct mem_ctx *ctx, int id) +{ + struct mem_blk *blk; + + mem_blk_foreach(ctx, blk) + if (id == blk->id) + return blk->valid_zones; + + return 0; +} + +/** + * Determine if a memory block is online by phys_index + */ +int mem_blkid_is_online(struct mem_ctx *ctx, int id) +{ + struct mem_blk *blk; + + mem_blk_foreach(ctx, blk) + if (id == blk->id) + return blk->online; + + return -1; +} + +/** + * Determine if a memory block is removable by phys_index + */ +int mem_blkid_is_removable(struct mem_ctx *ctx, int id) +{ + struct mem_blk *blk; + + mem_blk_foreach(ctx, blk) + if (id == blk->id) + return blk->removable; + + return -1; +} + +/** + * Offline a memory block by phys_index + */ +int mem_blkid_offline(struct mem_ctx *ctx, int id) +{ + struct mem_blk *blk; + + mem_blk_foreach(ctx, blk) + if (id == blk->id) + return mem_blk_offline(blk); + + return 1; +} + +/** + * Online a memory block by phys_index + * @return 0 upon success, non-zero othersise + */ +int mem_blkid_online(struct mem_ctx *ctx, int id) +{ + struct mem_blk *blk; + + mem_blk_foreach(ctx, blk) + if (id == blk->id) + return mem_blk_online(blk); + + return -1; +} + +/** + * Set the online state of the memory block + */ +int mem_blkid_set_state(struct mem_ctx *ctx, int id, int state) +{ + struct mem_blk *blk; + + mem_blk_foreach(ctx, blk) + if (id == blk->id) + return mem_blk_set_state(blk, state); + + return -1; +} + +/** + * Compare cxl_memdev function for qsort + */ +int mem_compare_cxl_memdevs(const void* a, const void* b) +{ + struct cxl_memdev *arg1 = *(struct cxl_memdev **)a; + struct cxl_memdev *arg2 = *(struct cxl_memdev **)b; + + int i1 = cxl_memdev_get_id(arg1); + int i2 = cxl_memdev_get_id(arg2); + + return mem_compare_ints(&i1, &i2); +} + +/** + * Compare cxl_region function for qsort + */ +int mem_compare_cxl_regions(const void* a, const void* b) +{ + struct cxl_region *arg1 = *(struct cxl_region **)a; + struct cxl_region *arg2 = *(struct cxl_region **)b; + + int i1 = cxl_region_get_id(arg1); + int i2 = cxl_region_get_id(arg2); + + return mem_compare_ints(&i1, &i2); +} + +/** + * Compare int function for qsort + */ +int mem_compare_ints(const void* a, const void* b) +{ + int arg1 = *(const int*)a; + int arg2 = *(const int*)b; + + if (arg1 < arg2) return -1; + if (arg1 > arg2) return 1; + return 0; +} + +/** + * Compare mem_blk function for qsort + */ +int mem_compare_mem_blks(const void* a, const void* b) +{ + struct mem_blk *arg1 = (struct mem_blk *)a; + struct mem_blk *arg2 = (struct mem_blk *)b; + + int i1 = arg1->id; + int i2 = arg2->id; + + return mem_compare_ints(&i1, &i2); +} + +/** + * Search for and return a cxl_memdev object matching name + * @return struct cxl_memdev *. NULL if error. + */ +struct cxl_memdev *mem_get_memdev(struct mem_ctx *ctx, char *name) +{ + struct cxl_memdev *memdev; + + cxl_memdev_foreach(ctx->cxl, memdev) + if (!strcmp(cxl_memdev_get_devname(memdev), name)) + return memdev; + + return NULL; +} + +/** + * Return an array of pointers to cxl_memdevs + */ +struct cxl_memdev **mem_get_memdevs(struct mem_ctx *ctx) +{ + int i, num; + struct cxl_memdev *memdev; + struct cxl_memdev **array; + + // Initialize variables + i = 0; + num = mem_num_memdevs(ctx); + if (num == 0) + return NULL; + + // Allocate memory + array = calloc(num, sizeof(*array)); + + // Populate the array with the memdev pointers + cxl_memdev_foreach(ctx->cxl, memdev) + array[i++] = memdev; + + // Sort the array + qsort(array, num, sizeof(*array), mem_compare_cxl_memdevs); + + return array; +} + +/** + * Search for and return a cxl_region object matching name + * @return struct cxl_region* or NULL if not found + */ +struct cxl_region *mem_get_region(struct mem_ctx *ctx, char * name) +{ + struct cxl_bus *bus; + struct cxl_decoder *decoder; + struct cxl_region *region; + + cxl_bus_foreach(ctx->cxl, bus) + cxl_decoder_foreach(cxl_bus_get_port(bus), decoder) + cxl_region_foreach(decoder, region) + if (!strcmp(name, cxl_region_get_devname(region))) + goto end; + + region = NULL; + +end: + + return region; +} + +/** + * Return an array of pointers to cxl_regions + */ +struct cxl_region **mem_get_regions(struct mem_ctx *ctx) +{ + struct cxl_bus *bus; + struct cxl_decoder *decoder; + struct cxl_region *region; + struct cxl_region **array; + int i, num; + + if (ctx->regions != NULL) + return ctx->regions; + + // Initialize variables + i = 0; + num = mem_num_regions(ctx); + if (num == 0) + return NULL; + + // Allocate memory + array = calloc(num, sizeof(*array)); + + // Loop through CXL tree and get regions + cxl_bus_foreach(ctx->cxl, bus) + cxl_decoder_foreach(cxl_bus_get_port(bus), decoder) + cxl_region_foreach(decoder, region) + array[i++] = region; + + // Sort the array + qsort(array, num, sizeof(*array), mem_compare_cxl_regions); + + ctx->num_regions = num; + ctx->regions = array; + + return array; +} + +/** + * Search for and return root cxl_decoder object + * @return struct cxl_decoder* or NULL if error + */ +struct cxl_decoder *mem_get_root_decoder(struct mem_ctx *ctx) +{ + struct cxl_bus *bus; + struct cxl_port *port; + struct cxl_decoder *decoder; + + decoder = NULL; + + bus = cxl_bus_get_first(ctx->cxl); + if (bus == NULL) + { + err(ctx, "Unable to obtain first cxl bus"); + goto end; + } + + port = cxl_bus_get_port(bus); + if (port == NULL) + { + err(ctx, "Unable to obtain first cxl bus port on bus %s", cxl_bus_get_devname(bus)); + goto end; + } + + decoder = cxl_decoder_get_first(port); + +end: + + return decoder; +} + +/** + * Return a const char * (string) representation of enum LMPL + */ +const char *mem_lmpl(int policy) +{ + if (policy < 0 || policy >= LMPL_MAX) + return NULL; + return _LMPL[policy]; +} + +/** + * Return a const char * (string) representation of enum LMST + */ +const char *mem_lmst(int state) +{ + if (state < 0 || state >= LMST_MAX) + return NULL; + return _LMST[state]; +} + +/** + * Return a const char * (string) representation of enum LMZN + */ +const char *mem_lmzn(int zone) +{ + if (zone < 0 || zone >= LMZN_MAX) + return NULL; + return _LMZN[zone]; +} + +/** + * Return current library log priority level + */ +int mem_log_get_priority(struct mem_ctx *ctx) +{ + return ctx->log->priority; +} + +/** + * Set the log library destination to use + * @param dst int from enum LMLD + */ +void mem_log_set_destination(struct mem_ctx *ctx, int dst, char *filepath) +{ + if (dst == LMLD_SYSLOG) + ctx->log->log_fn = log_to_syslog; + else if (dst == LDST_NULL) + ctx->log->log_fn = log_to_null; + else if (dst == LDST_FILE & filepath != NULL) + { + ctx->log->file = fopen(filepath, "a"); + ctx->log->log_fn = log_to_file; + } + else + ctx->log->log_fn = log_to_stdio; + + info(ctx, "Set log destination to %d %s", dst, log_dst_to_str(dst)); +} + +/** + * Set logging function + */ +void mem_log_set_fn(struct mem_ctx *ctx, + void (*mem_log_fn)( struct mem_ctx *ctx, + int priority, + const char *fn, + int line, + const char *format, + va_list args)) +{ + ctx->log->log_fn = (log_fn) mem_log_fn; + info(ctx, "custom logging function %p registered\n", mem_log_fn); +} + +/** + * Set library log priority level + */ +void mem_log_set_priority(struct mem_ctx *ctx, int priority) +{ + if (priority > LOG_DEBUG) + priority = LOG_DEBUG; + if (priority < 0 ) + priority = 0; + + ctx->log->priority = priority; + info(ctx, "logging priority set to %d - %s\n", priority, log_priority_to_str(priority)); +} + +/** + * Get the Interleave granulariy presented by first port of the bus + */ +int mem_memdev_get_interleave_granularity(struct mem_ctx *ctx, struct cxl_memdev *memdev) +{ + unsigned int rv; + struct cxl_bus *bus; + struct cxl_port *parent, *port; + struct cxl_decoder *decoder; + + rv = 0; + + bus = cxl_memdev_get_bus(memdev); + if (bus == NULL) + { + rv = 0; + err(ctx, "Unable to obtain cxl_bus for memdev %s: %d", cxl_memdev_get_devname(memdev), rv); + goto end; + } + + parent = cxl_bus_get_port(bus); + if (parent == NULL) + { + rv = 0; + err(ctx, "Unable to obtain cxl_port for bus %s: %d", cxl_bus_get_devname(bus), rv); + goto end; + } + + port = cxl_port_get_first(parent); + if (port == NULL) + { + rv = 0; + err(ctx, "Unable to obtain first cxl_port for parent %s: %d", cxl_port_get_devname(parent), rv); + goto end; + } + + decoder = cxl_decoder_get_first(port); + if (decoder == NULL) + { + rv = 0; + err(ctx, "Unable to obtain decoder for cxl_port %s: %d", cxl_port_get_devname(port), rv); + goto end; + } + + rv = cxl_decoder_get_interleave_granularity(decoder); + +end: + + return rv; +} + +/** + * Is a memdev free to be added to a new region + * @return 1 for true, 0 for false + */ +int mem_memdev_is_available(struct mem_ctx *ctx, struct cxl_memdev *memdev) +{ + int rv; + struct cxl_endpoint *endpoint; + struct cxl_port *port; + struct cxl_decoder *decoder; + struct cxl_region *region; + + rv = 0; + + // Check if memdev is even enabled + rv = cxl_memdev_is_enabled(memdev); + if (rv == 0) + goto end; + + // Get endpoint + endpoint = cxl_memdev_get_endpoint(memdev); + if (endpoint == NULL) + { + rv = 0; + err(ctx, "Unable to get cxl_endpoint for memdev %s", cxl_memdev_get_devname(memdev)); + goto end; + } + + // Check if endpoint is enabled + rv = cxl_endpoint_is_enabled(endpoint); + if (rv == 0) + goto end; + + // Get the port + port = cxl_endpoint_get_port(endpoint); + if (port == NULL) + { + rv = 0; + err(ctx, "Unable to get cxl_port for cxl_endpoint %s", cxl_endpoint_get_devname(endpoint)); + goto end; + } + + // Check if port is enabled + rv = cxl_port_is_enabled(port); + if (rv == 0) + goto end; + + // Get Decoder + decoder = cxl_decoder_get_first(port); + if (decoder == NULL) + { + rv = 0; + err(ctx, "Unable to get cxl_decoder for cxl_port %s", cxl_port_get_devname(port)); + goto end; + } + + region = cxl_decoder_get_region(decoder); + if (region == NULL) + rv = 1; + else + rv = 0; + +end: + + return rv; +} + +/** + * Create a new lib mem context + */ +int mem_new(struct mem_ctx **ctx) +{ + int rv; + struct mem_ctx *c; + + rv = 1; + + // Allocate memory for the object + c = calloc(1, sizeof(struct mem_ctx)); + if (c == NULL) + return -ENOMEM; + + c->refcount = 1; + + // Get a cxl context + rv = cxl_new(&c->cxl); + if (rv != 0) + goto end; + + // Set up logger + c->log = log_init("libmem", LDST_SYSLOG, LOG_ERR, 1, NULL); + + log_info(c->log, "mem_ctx created at: %p\n", c); + log_dbg(c->log, "log_priority=%d\n", c->log->priority); + + // Store logger object into mem_ctx + *ctx = c; + + rv = 0; + +end: + + return rv; +} + +/** + * Count and return the number of CXL rmemdevs + */ +int mem_num_memdevs(struct mem_ctx* ctx) +{ + int num; + struct cxl_memdev *memdev; + + num = 0; + + cxl_memdev_foreach(ctx->cxl, memdev) + num++; + + return num; +} + +/** + * Count and return the number of CXL regions + */ +int mem_num_regions(struct mem_ctx *ctx) +{ + int num; + struct cxl_bus *bus; + struct cxl_decoder *decoder; + struct cxl_region *region; + + if (ctx->regions != NULL) + return ctx->num_regions; + + num = 0; + + // Loop through the cxl bus to count the number of cxl_regions + cxl_bus_foreach(ctx->cxl, bus) + cxl_decoder_foreach(cxl_bus_get_port(bus), decoder) + cxl_region_foreach(decoder, region) + num++; + + return num; +} + +/** + * mem_ref - Create an additional reference on the mem context + * @param ctx struct mem_ctx context created by cxl_new() + */ +struct mem_ctx *mem_ref(struct mem_ctx *ctx) +{ + if (ctx == NULL) + return NULL; + ctx->refcount++; + return ctx; +} + +/** + * Create a region from a list of memory devices + */ +int mem_region_create(struct mem_ctx *ctx, int granularity, int num, struct cxl_memdev **memdevs) +{ + int rv; + unsigned long long size, total_size; + char name[256]; + struct cxl_decoder *root, *decoder; + struct cxl_region *region; + struct cxl_memdev *memdev; + + // Initialize variables + rv = 1; + total_size = 0; + + root = mem_get_root_decoder(ctx); + if (root == NULL) + { + err(ctx, "Could not obtain root decoder"); + goto end; + } + + region = cxl_decoder_create_ram_region(root); + if (region == NULL) + { + err(ctx, "Could not create ram region"); + goto end;; + } + else + info(ctx, "Created ram region %s", cxl_region_get_devname(region)); + + cxl_region_set_interleave_ways(region, num); + cxl_region_set_interleave_granularity(region, granularity); + + info(ctx, "Set interleave ways to %d on region %s", num, cxl_region_get_devname(region)); + info(ctx, "Set interleave granularity to %d on region %s", granularity, cxl_region_get_devname(region)); + + // Loop through requested memory devices + for ( int i = 0 ; i < num ; i++ ) + { + memdev = memdevs[i]; + if (memdev == NULL) + { + err(ctx, "Memdev poiner was NULL"); + goto delete_region; + } + + size = cxl_memdev_get_ram_size(memdev); + decoder = cxl_decoder_get_first(cxl_endpoint_get_port(cxl_memdev_get_endpoint(memdev))); + + rv = cxl_decoder_set_mode(decoder, CXL_DECODER_MODE_RAM); + if (rv != 0) + { + err(ctx, "Attempt to set decoder mode failed: %d", rv); + goto delete_region; + } + else + info(ctx, "Set decoder mode to %s on decoder %s", cxl_decoder_mode_name(CXL_DECODER_MODE_RAM), cxl_decoder_get_devname(decoder)); + + rv = cxl_decoder_set_dpa_size(decoder, size); + if (rv != 0) + { + err(ctx, "Attempt to set decoder dpa size failed: %d", rv); + goto delete_region; + } + else + info(ctx, "Set decoder DPA size to %llu on decoder %s", size, cxl_decoder_get_devname(decoder)); + + total_size += size; + } + + // Set region total size + rv = cxl_region_set_size(region, total_size); + if (rv != 0) + { + err(ctx, "Attempt to set region size failed: %d", rv); + goto delete_region; + } + else + info(ctx, "Set region size to %llu on region %s", total_size, cxl_region_get_devname(region)); + + // Loop through requested memory devices + for ( int i = 0 ; i < num ; i++ ) + { + memdev = memdevs[i]; + if (memdev == NULL) + { + err(ctx, "Memdev poiner was NULL"); + goto delete_region; + } + + decoder = cxl_decoder_get_first(cxl_endpoint_get_port(cxl_memdev_get_endpoint(memdev))); + + // Set region targets to decoders + rv = cxl_region_set_target(region, i, decoder); + if (rv != 0) + { + err(ctx, "Unable to set region target i: %d rv: %d", i, rv); + goto delete_region; + } + else + info(ctx, "Set region target %d to %s on region %s", i, cxl_decoder_get_devname(decoder), cxl_region_get_devname(region)); + } + + // commit the region + rv = cxl_region_decode_commit(region); + if (rv != 0) + { + err(ctx, "Decode commit failed: %d", rv); + goto delete_region; + } + else + info(ctx, "Decode commit on region %s", cxl_region_get_devname(region)); + + // bind the region + rv = cxl_region_enable(region); + if (rv != 0) + { + err(ctx, "Failed to enable region: %d", rv); + goto delete_region; + } + else + info(ctx, "Enabled region %s", cxl_region_get_devname(region)); + + rv = 0; + + goto end; + +delete_region: + + strcpy(name, cxl_region_get_devname(region)); + rv = cxl_region_delete(region); + if (rv != 0) + { + err(ctx, "Failed to delete region %s rv: %d", name, rv); + goto end; + } + else + err(ctx, "Deleted region %s", name); + + rv = 1; + +end: + + return rv; +} + +/** + * Set a cxl_region to devdax mode + */ +int mem_region_daxmode(struct mem_ctx *ctx, struct cxl_region *region) +{ + int rv; + struct daxctl_region *dax_region; + struct daxctl_dev *dax_dev; + struct daxctl_memory *dax_mem; + + rv = 1; + + // Get the dax region for the cxl_region + dax_region = cxl_region_get_daxctl_region(region); + if (dax_region == NULL) + { + err(ctx, "Failed to obtain dax_region for cxl region %s\n", cxl_region_get_devname(region)); + goto end; + } + + // Get the dax device + dax_dev = daxctl_dev_get_first(dax_region); + if (dax_dev == NULL) + { + err(ctx, "Failed to obtain dax_dev for dax_region %s\n", daxctl_region_get_devname(dax_region)); + goto end; + } + + // Check if region is already in devdax mode + dax_mem = daxctl_dev_get_memory(dax_dev); + if (dax_mem == NULL) + { + rv = 0; + info(ctx, "dax_dev %s was already in devdax mode\n", daxctl_dev_get_devname(dax_dev)); + goto end; + } + + if (cxl_region_is_enabled(region)) + { + // Offline all the blocks for the cxl_region + rv = mem_region_offline_blocks(ctx, region); + if (rv != 0) + { + err(ctx, "Failed to offline all region blocks: %d", rv); + goto end; + } + else + info(ctx, "Offlined all memory blocks of region %s", cxl_region_get_devname(region)); + + } + + // Disable the dax device if enabled + if (daxctl_dev_is_enabled(dax_dev)) + { + rv = daxctl_dev_disable(dax_dev); + if (rv != 0) + { + err(ctx, "Failed to disable dax_dev %s %d\n", daxctl_dev_get_devname(dax_dev), rv); + goto end; + } + else + info(ctx, "Disabled dax device %s", daxctl_dev_get_devname(dax_dev)); + } + + // Set region to devdax mode + rv = daxctl_dev_enable_devdax(dax_dev); + if (rv != 0) + { + err(ctx, "Failed to enable dax mode on %s %d\n", daxctl_dev_get_devname(dax_dev), rv); + goto end; + } + else + info(ctx, "Enabled devdax mode on dax device %s", daxctl_dev_get_devname(dax_dev)); + + rv = 0; + +end: + + return rv; +} + +/** + * Delete a cxl_region + * + * This function will attempt to offline all the memory blocks + * before deleting the retion. If a memory block fails to offline + * this command will fail + */ +int mem_region_delete(struct mem_ctx *ctx, struct cxl_region *region) +{ + int rv; + char buf[LMLN_FILEPATH]; + + // Initialize variables + rv = 1; + + if (mem_region_num_blocks_online(ctx, region) > 0) + { + // Offline all the region blocks + rv = mem_region_offline_blocks(ctx, region); + if (rv != 0) + { + err(ctx, "Failed to offline all region blocks: %d", rv); + goto end; + } + else + info(ctx, "Offlined all memory blocks of region %s", cxl_region_get_devname(region)); + } + + // Diable the region + rv = cxl_region_disable(region); + if (rv != 0) + { + err(ctx, "Failed to disable region: %d", rv); + goto end; + } + else + info(ctx, "Disabled region %s", cxl_region_get_devname(region)); + + // Copy name for final debug print + strncpy(buf, cxl_region_get_devname(region), LMLN_FILEPATH); + + // Delete the region + rv = cxl_region_delete(region); + if (rv != 0) + { + err(ctx, "Failed to delete region %s rv: %d", cxl_region_get_devname(region), rv); + goto end; + } + else + info(ctx, "Deleted region %s", buf); + + rv = 0; + +end: + + return rv; +} + +/** + * Get the state of block offset within + */ +int mem_region_get_blk_state(struct mem_ctx *ctx, struct cxl_region *region, int offset) +{ + int rv; + unsigned long long block_size, base, size, end, addr; + struct mem_blk *blk; + + rv = -1; + + if (offset < 0) + { + err(ctx, "Requested offset is below zero: %d", offset); + goto end; + } + + // Get memory block size in bytes + block_size = mem_system_get_blocksize(ctx); + if (block_size == 0) + { + rv = -1; + err(ctx, "Unable to obtain system memory block size"); + goto end; + } + + // Get region base address + base = cxl_region_get_resource(region); + if (base == 0 || base == 0xFFFFFFFFFFFFFFFF) + { + rv = -1; + err(ctx, "Unable to get cxl region %s resource address\n", cxl_region_get_devname(region)); + goto end; + } + + // Get region size in bytes + size = cxl_region_get_size(region); + if (size == 0) + { + rv = -1; + warn(ctx, "Region size was zero for region %s", cxl_region_get_devname(region)); + goto end; + } + end = base + size; + addr = base + block_size * offset; + + // Verify offset block is within region + if (addr >= end) + { + err(ctx, "Could not get offset within region as it exceeds region range"); + rv = -1; + goto end; + } + + // Loop through the memory directories and check address + mem_blk_foreach(ctx, blk) + { + if (addr == (block_size * blk->id)) + { + rv = mem_blk_get_state(blk); + goto end; + } + } + +end: + + return rv; +} + +/** + * Return an array of integers representing the block index number + * @return int* array of phys_index numebrs, NULL on error + * + * The length of the array is mem_get_num_blocks() + */ +int *mem_region_get_blocks(struct mem_ctx *ctx, struct cxl_region *region) +{ + unsigned long long block_size, base, size, end, addr; + struct mem_blk *blk; + int i, num; + int *array; + + i = 0; + array = NULL; + + // Get memory block size in bytes + block_size = mem_system_get_blocksize(ctx); + if (block_size == 0) + { + err(ctx, "Unable to read system memory block size"); + goto end; + } + + // Get region base address + base = cxl_region_get_resource(region); + if (base == 0) + { + err(ctx, "Unable to get cxl region %s resource address", cxl_region_get_devname(region)); + return NULL; + } + + // Get region size in bytes + size = cxl_region_get_size(region); + if (size == 0) + { + warn(ctx, "Region size was zero for region %s", cxl_region_get_devname(region)); + return NULL; + } + end = base + size; + + // Get the number of blocks + num = mem_region_num_blocks(ctx, region); + + // Allocate memory for the array + array = malloc(num * sizeof(int)); + if (array == NULL) + return NULL; + + mem_blk_foreach(ctx, blk) + { + addr = block_size * blk->id; + if (addr >= base && addr < end) + array[i++] = blk->id; + } + + // Sort the array + qsort(array, num, sizeof(int), mem_compare_ints); + +end: + + return array; +} + +/** + * Get total memory capacity of a cxl_region in bytes + */ +unsigned long long mem_region_get_capacity(struct mem_ctx *ctx, struct cxl_region *region) +{ + unsigned long long capacity; + + capacity = mem_system_get_blocksize(ctx) * mem_region_num_blocks(ctx, region); + + return capacity; +} + +/** + * Get offline memory capacity of a cxl_region in bytes + */ +unsigned long long mem_region_get_capacity_offline(struct mem_ctx *ctx, struct cxl_region *region) +{ + unsigned long long capacity; + + capacity = mem_system_get_blocksize(ctx) * mem_region_num_blocks_offline(ctx, region); + + return capacity; +} + +/** + * Get online memory capacity of a cxl_region in bytes + */ +unsigned long long mem_region_get_capacity_online(struct mem_ctx *ctx, struct cxl_region *region) +{ + unsigned long long capacity; + + capacity = mem_system_get_blocksize(ctx) * mem_region_num_blocks_online(ctx, region); + + return capacity; +} + +/** + * Determine and return true if cxl_region is in system-ram mode + */ +int mem_region_is_rammode(struct mem_ctx *ctx, struct cxl_region* region) +{ + int rv; + struct daxctl_region *dax_region; + struct daxctl_dev *dax_dev; + struct daxctl_memory *dax_mem; + + rv = -1; + + // Get the dax region for the cxl_region + dax_region = cxl_region_get_daxctl_region(region); + if (dax_region == NULL) + { + rv = -1; + err(ctx, "Failed to obtain dax_region for cxl region %s\n", cxl_region_get_devname(region)); + goto end; + } + + // Get the dax device + dax_dev = daxctl_dev_get_first(dax_region); + if (dax_dev == NULL) + { + err(ctx, "Failed to obtain dax_dev for dax_region %s\n", daxctl_region_get_devname(dax_region)); + goto end; + } + + // Check if region is already in ram mode + dax_mem = daxctl_dev_get_memory(dax_dev); + if (dax_mem == NULL) + { + rv = 0; + } + else + rv = 1; + +end: + + return rv; +} + +/** + * Determine and return true if cxl_region is in daxmode + */ +int mem_region_is_daxmode(struct mem_ctx *ctx, struct cxl_region* region) +{ + int rv; + struct daxctl_region *dax_region; + struct daxctl_dev *dax_dev; + struct daxctl_memory *dax_mem; + + rv = -1; + + // Get the dax region for the cxl_region + dax_region = cxl_region_get_daxctl_region(region); + if (dax_region == NULL) + { + err(ctx, "Failed to obtain dax_region for cxl region %s\n", cxl_region_get_devname(region)); + goto end; + } + + // Get the dax device + dax_dev = daxctl_dev_get_first(dax_region); + if (dax_dev == NULL) + { + err(ctx, "Failed to obtain dax_dev for dax_region %s\n", daxctl_region_get_devname(dax_region)); + goto end; + } + + // Check if region is already in ram mode + dax_mem = daxctl_dev_get_memory(dax_dev); + if (dax_mem == NULL) + { + rv = 1; + } + else + rv = 0; + +end: + + return rv; +} + +/** + * Get the number of memory blocks within a cxl_region + * @param num blocks. <0 if error + */ +int mem_region_num_blocks(struct mem_ctx *ctx, struct cxl_region *region) +{ + int num; + unsigned long long block_size, base, size, end, addr; + struct mem_blk *blk; + + num = 0; + + // Get memory block size in bytes + block_size = mem_system_get_blocksize(ctx); + if (block_size == 0) + { + num = -1; + err(ctx, "Unable to obtain system memory block size"); + goto end; + } + + // Get region base address + base = cxl_region_get_resource(region); + if (base == 0 || base == 0xFFFFFFFFFFFFFFFF) + { + num = 0; + err(ctx, "Unable to get cxl region %s resource address\n", cxl_region_get_devname(region)); + goto end; + } + + // Get region size in bytes + size = cxl_region_get_size(region); + if (size == 0) + { + num = 0; + warn(ctx, "Region size was zero for region %s", cxl_region_get_devname(region)); + goto end; + } + end = base + size; + + // Loop through the memory directories and check address + mem_blk_foreach(ctx, blk) + { + addr = block_size * blk->id; + if (addr >= base && addr < end) + num++; + } + +end: + + return num; +} + +/** + * Get the number of memory blocks offline within a cxl_region + * @param num blocks. <0 if error + */ +int mem_region_num_blocks_offline(struct mem_ctx *ctx, struct cxl_region *region) +{ + int num; + unsigned long long block_size, base, size, end, addr; + struct mem_blk *blk; + + num = 0; + + // Get memory block size in bytes + block_size = mem_system_get_blocksize(ctx); + if (block_size == 0) + { + err(ctx, "Unable to read system memory block size"); + goto end; + } + + // Get region base address + base = cxl_region_get_resource(region); + if (base == 0 || base == 0xFFFFFFFFFFFFFFFF) + { + num = 0; + err(ctx, "Unable to get cxl region %s resource address", cxl_region_get_devname(region)); + goto end; + } + + // Get region size in bytes + size = cxl_region_get_size(region); + if (size == 0) + { + num = 0; + warn(ctx, "Region size was zero for region %s", cxl_region_get_devname(region)); + goto end; + } + end = base + size; + + // Loop through the memory directories and check address + mem_blk_foreach(ctx, blk) + { + addr = block_size * blk->id; + if (addr >= base && addr < end && !mem_blk_is_online(blk)) + num++; + } + +end: + + return num; +} + +/** + * Get the number of memory blocks online within a cxl_region + * @param num blocks. <0 if error + */ +int mem_region_num_blocks_online(struct mem_ctx *ctx, struct cxl_region *region) +{ + int num; + unsigned long long block_size, base, size, end, addr; + struct mem_blk *blk; + + num = 0; + + // Get memory block size in bytes + block_size = mem_system_get_blocksize(ctx); + if (block_size == 0) + { + err(ctx, "Unable to read system memory block size"); + goto end; + } + + // Get region base address + base = cxl_region_get_resource(region); + if (base == 0 || base == 0xFFFFFFFFFFFFFFFF) + { + num = 0; + err(ctx, "Unable to get cxl region %s resource address", cxl_region_get_devname(region)); + goto end; + } + + // Get region size in bytes + size = cxl_region_get_size(region); + if (size == 0) + { + num = 0; + warn(ctx, "Region size was zero for region %s", cxl_region_get_devname(region)); + goto end; + } + end = base + size; + + // Loop through the memory directories and check address + mem_blk_foreach(ctx, blk) + { + addr = block_size * blk->id; + if (addr >= base && addr < end && mem_blk_is_online(blk)) + num++; + } + +end: + + return num; +} + +/** + * Offline all blocks in a region + */ +int mem_region_offline_blocks(struct mem_ctx *ctx, struct cxl_region *region) +{ + int rv, ret; + struct mem_blk *blk; + unsigned long long block_size, base, size, end, addr; + + // Initialize variables + rv = 1; + + // Get memory block size in bytes + block_size = mem_system_get_blocksize(ctx); + if (block_size == 0) + { + rv = 1; + err(ctx, "Unable to obtain system memory block size"); + goto end; + } + + // Get region base address + base = cxl_region_get_resource(region); + if (base == 0 || base == 0xFFFFFFFFFFFFFFFF) + { + rv = 1; + err(ctx, "Unable to get cxl region %s resource address\n", cxl_region_get_devname(region)); + goto end; + } + + // Get region size in bytes + size = cxl_region_get_size(region); + if (size == 0) + { + rv = 0; + warn(ctx, "Region size was zero for region %s", cxl_region_get_devname(region)); + goto end; + } + end = base + size; + + // Loop through the memory directories and check address + rv = 0; + mem_blk_foreach(ctx, blk) + { + addr = block_size * blk->id; + if (addr >= base && addr < end) + { + ret = mem_blk_offline(blk); + if (ret != 0) + { + err(ctx, "Could not offline memory block %d. %d", blk->id, ret); + rv += 1; + } + } + } + + if (rv == 0) + info(ctx, "Offlined all blocks of region %s", cxl_region_get_devname(region)); + else + err(ctx, "Failed to offline all blocks of region %s", cxl_region_get_devname(region)); + +end: + + return rv; +} + +/** + * Online all blocks in a region + */ +int mem_region_online_blocks(struct mem_ctx *ctx, struct cxl_region *region) +{ + int rv, ret; + struct mem_blk *blk; + unsigned long long block_size, base, size, end, addr; + + // Initialize variables + rv = 1; + + // Get memory block size in bytes + block_size = mem_system_get_blocksize(ctx); + if (block_size == 0) + { + rv = 1; + err(ctx, "Unable to obtain system memory block size"); + goto end; + } + + // Get region base address + base = cxl_region_get_resource(region); + if (base == 0 || base == 0xFFFFFFFFFFFFFFFF) + { + rv = 1; + err(ctx, "Unable to get cxl region %s resource address\n", cxl_region_get_devname(region)); + goto end; + } + + // Get region size in bytes + size = cxl_region_get_size(region); + if (size == 0) + { + rv = 0; + warn(ctx, "Region size was zero for region %s", cxl_region_get_devname(region)); + goto end; + } + end = base + size; + + // Loop through the memory directories and check address + rv = 0; + mem_blk_foreach(ctx, blk) + { + addr = block_size * blk->id; + if (addr >= base && addr < end) + { + ret = mem_blk_online(blk); + if (ret != 0) + { + err(ctx, "Could not online memory block %d. %d", blk->id, ret); + rv += 1; + } + } + } + + if (rv == 0) + info(ctx, "Onlined all blocks of region %s", cxl_region_get_devname(region)); + else + err(ctx, "Failed to online all blocks of region %s", cxl_region_get_devname(region)); + +end: + + return rv; +} + +/** + * Set a cxl_region to system-ram mode + */ +int mem_region_rammode(struct mem_ctx *ctx, struct cxl_region *region) +{ + int rv; + struct daxctl_region *dax_region; + struct daxctl_dev *dax_dev; + struct daxctl_memory *dax_mem; + + rv = 1; + + // Get the dax region for the cxl_region + dax_region = cxl_region_get_daxctl_region(region); + if (dax_region == NULL) + { + rv = 1; + err(ctx, "Failed to obtain dax_region for cxl region %s\n", cxl_region_get_devname(region)); + goto end; + } + + // Get the dax device + dax_dev = daxctl_dev_get_first(dax_region); + if (dax_dev == NULL) + { + err(ctx, "Failed to obtain dax_dev for dax_region %s\n", daxctl_region_get_devname(dax_region)); + goto end; + } + + // Check if region is already in ram mode + dax_mem = daxctl_dev_get_memory(dax_dev); + if (dax_mem != NULL) + { + rv = 0; + info(ctx, "dax_dev %s was already in system-ram mode\n", daxctl_dev_get_devname(dax_dev)); + goto end; + } + + // Disable the dax device if enabled + if (daxctl_dev_is_enabled(dax_dev)) + { + rv = daxctl_dev_disable(dax_dev); + if (rv != 0) + { + err(ctx, "Failed to disable dax_dev %s %d\n", daxctl_dev_get_devname(dax_dev), rv); + goto end; + } + else + info(ctx, "Disabled dax device %s", daxctl_dev_get_devname(dax_dev)); + } + + // Set region to system-ram mode + rv = daxctl_dev_enable_ram(dax_dev); + if (rv != 0) + { + err(ctx, "Failed to enable system ram mode on %s %d\n", daxctl_dev_get_devname(dax_dev), rv); + goto end; + } + else + info(ctx, "Enabled system-ram mode on dax device %s", daxctl_dev_get_devname(dax_dev)); + + rv = 0; + +end: + + return rv; +} + +/** + * Set the online state of the memory block within a specified region + */ +int mem_region_set_blk_state(struct mem_ctx *ctx, struct cxl_region *region, int offset, int mode) +{ + int rv; + unsigned long long block_size, base, size, end, addr; + struct mem_blk *blk; + + rv = -1; + + if (offset < 0) + { + err(ctx, "Requested offset is below zero: %d", offset); + goto end; + } + + // Get memory block size in bytes + block_size = mem_system_get_blocksize(ctx); + if (block_size == 0) + { + rv = -1; + err(ctx, "Unable to obtain system memory block size"); + goto end; + } + + // Get region base address + base = cxl_region_get_resource(region); + if (base == 0 || base == 0xFFFFFFFFFFFFFFFF) + { + rv = -1; + err(ctx, "Unable to get cxl region %s resource address\n", cxl_region_get_devname(region)); + goto end; + } + + // Get region size in bytes + size = cxl_region_get_size(region); + if (size == 0) + { + rv = -1; + warn(ctx, "Region size was zero for region %s", cxl_region_get_devname(region)); + goto end; + } + end = base + size; + addr = base + block_size * offset; + + // Verify offset block is within region + if (addr >= end) + { + err(ctx, "Could not get offset within region as it exceeds region range"); + rv = -1; + goto end; + } + + // Loop through the memory directories and check address + mem_blk_foreach(ctx, blk) + { + if (addr == (block_size * blk->id)) + { + rv = mem_blk_set_state(blk, mode); + goto end; + } + } + +end: + + return rv; +} + +/** + * Read in a sysfs attribute + * @return the number of bytes read. negative errno if an error + */ +static int mem_sysfs_read(struct mem_ctx *ctx, const char *path, char *buf) +{ + int n, fd; + + fd = open(path, O_RDONLY|O_CLOEXEC); + if (fd < 0) + { + n = -errno; + err(ctx, "Failed to open sysfs file: %s %d - %s", path, errno, strerror(errno) ); + goto end; + } + + n = read(fd, buf, LMLN_SYSFS_ATTR_SIZE); + close(fd); + + if (n < 0 || n >= LMLN_SYSFS_ATTR_SIZE) + { + buf[0] = 0; + n = -errno; + err(ctx, "Failed to read sysfs file: %s %d - %s", path, errno, strerror(errno) ); + goto end; + } + + buf[n] = 0; + if (n > 0 && buf[n-1] == '\n') + buf[n-1] = 0; + +end: + + return n; +} + +/** + * Write a value to a sysfs atribute + * @return the number of bytes written, 0 or negative number of bytes written if an error + */ +static int mem_sysfs_write(struct mem_ctx *ctx, const char *path, const char *buf) +{ + int n, fd, len; + + fd = open(path, O_WRONLY|O_CLOEXEC); + if (fd < 0) + { + n = -errno; + err(ctx, "Failed to open sysfs file: %s %d - %s", path, errno, strerror(errno) ); + goto end; + } + + len = strlen(buf) + 1; + + n = write(fd, buf, len); + close(fd); + + if (n < len) + { + err(ctx, "Failed to write all bytes to sysfs file: %s %d/%d", path, n, len); + n = -n; + goto end; + } + +end: + + return n; +} + +/** + * Return an array of integers representing the block index number + * @return int* array of phys_index numebrs, NULL on error + * + * The length of the array is mem_get_num_blocks() + */ +int *mem_system_get_blocks(struct mem_ctx *ctx) +{ + int i, num; + int *array; + struct mem_blk *blk; + + i = 0; + + // Get the number of blocks + num = mem_system_num_blocks(ctx); + + // Allocate memory for the array + array = malloc(num * sizeof(int)); + if (array == NULL) + return NULL; + + // Walk the list of blocks and add the IDs + mem_blk_foreach(ctx, blk) + array[i++] = mem_blk_get_id(blk); + + // Sort the array + qsort(array, num, sizeof(int), mem_compare_ints); + + return array; +} + +/** + * Get system memory block size + * @return unsigned long long size i Get system memory block size + * @return unsigned long long size in bytes. 0 if error + */ +unsigned long long mem_system_get_blocksize(struct mem_ctx *ctx) +{ + int rv; + char path[LMLN_FILEPATH]; + char buf[LMLN_SYSFS_ATTR_SIZE]; + + sprintf(path, "%s/%s", LMFP_MEM_DIR, "block_size_bytes"); + rv = mem_sysfs_read(ctx, path, buf); + if (rv <= 0) + { + err(ctx, "Unable to read system memory block size: %d\n", rv); + return 0; + } + + return strtoul(buf, NULL, 16); +} + +/** + * Get total system memory capacity in bytes + */ +unsigned long long mem_system_get_capacity(struct mem_ctx *ctx) +{ + unsigned long long capacity; + + capacity = mem_system_get_blocksize(ctx) * mem_system_num_blocks(ctx); + + return capacity; +} + +/** + * Get offline system memory capacity in bytes + */ +unsigned long long mem_system_get_capacity_offline(struct mem_ctx *ctx) +{ + unsigned long long capacity; + + capacity = mem_system_get_blocksize(ctx) * mem_system_num_blocks_offline(ctx); + + return capacity; +} + +/** + * Get online system memory capacity in bytes + */ +unsigned long long mem_system_get_capacity_online(struct mem_ctx *ctx) +{ + unsigned long long capacity; + + capacity = mem_system_get_blocksize(ctx) * mem_system_num_blocks_online(ctx); + + return capacity; +} + +/** + * Get the current auto_online_policy + * + * Returns an int LMPL represeting the online policy + */ +int mem_system_get_policy(struct mem_ctx *ctx) +{ + int rv; + char path[LMLN_FILEPATH]; + char buf[LMLN_SYSFS_ATTR_SIZE]; + + sprintf(path, "%s/%s", LMFP_MEM_DIR, "auto_online_blocks"); + rv = mem_sysfs_read(ctx, path, buf); + if (rv <= 0) + { + err(ctx, "Unable to read system auto memory online policy from sysfs: %d", rv); + return -1; + } + + return mem_to_lmpl(buf); +} + +/** + * Get the number of memory blocks in the system + * @return The number of blocks. 0 if error + */ +int mem_system_num_blocks(struct mem_ctx *ctx) +{ + struct mem_blk *blk; + int num; + + num = 0; + + mem_blk_foreach(ctx, blk) + num++; + + return num; +} + +/** + * Return the number of memory blocks that are offline + */ +int mem_system_num_blocks_offline(struct mem_ctx *ctx) +{ + struct mem_blk *blk; + int num; + + num = 0; + + mem_blk_foreach(ctx, blk) + if (!mem_blk_is_online(blk)) + num++; + + return num; +} + +/** + * Return the number of memory blocks that are online + */ +int mem_system_num_blocks_online(struct mem_ctx *ctx) +{ + struct mem_blk *blk; + int num; + + num = 0; + + mem_blk_foreach(ctx, blk) + if (mem_blk_is_online(blk)) + num++; + + return num; +} + +/** + * Set the auto online policy for a memory block + * @param mode int representing policy [LMPL] + */ +int mem_system_set_policy(struct mem_ctx *ctx, int mode) +{ + int rv; + char path[LMLN_FILEPATH]; + + rv = 0; + + if (mode < 0 || mode >= LMPL_MAX) + { + rv = -2; + err(ctx, "User attempted to set an invalid memory auto online policy: %d", mode); + goto end; + } + + if (mode == mem_system_get_policy(ctx)) + { + info(ctx, "Memory policy already in state %s. Skipping", mem_lmpl(mode)); + rv = 0; + goto end; + } + + sprintf(path, "%s/%s", LMFP_MEM_DIR, "auto_online_blocks"); + rv = mem_sysfs_write(ctx, path, mem_lmpl(mode)); + if (rv <= 0 || rv != (int) (strlen(mem_lmpl(mode))+1)) + { + err(ctx, "Failed to write memory auto online policy to sysfs: %d", rv); + rv = -1; + goto end;; + } + else + info(ctx, "Set online policy to %s", mem_lmpl(mode)); + + rv = 0; + +end: + + return rv; +} + +/* Return the enum LMPL representing a string */ +int mem_to_lmpl(char *policy) +{ + for ( int i = 0 ; i < LMPL_MAX ; i++ ) + if (!strcmp(policy, mem_lmpl(i))) + return i; + return -1; +} + +/* Return the enum LMST representing a string */ +int mem_to_lmst(char *state) +{ + for ( int i = 0 ; i < LMST_MAX ; i++ ) + if (!strcmp(state, mem_lmst(i))) + return i; + return -1; +} + +/* Return the enum LMZN representing a string */ +int mem_to_lmzn(char *zone) +{ + for ( int i = 0 ; i < LMZN_MAX ; i++ ) + if (!strcmp(zone, mem_lmzn(i))) + return i; + return -1; +} + +/** + * Free the memory context object + */ +int mem_unref(struct mem_ctx *ctx) +{ + if (ctx == NULL) + return 1; + + // Decrement the ref counter and check if there are still references + ctx->refcount--; + if (ctx->refcount > 0) + return 0; + + if (ctx->regions != NULL) + free(ctx->regions); + + if (ctx->cxl) + cxl_unref(ctx->cxl); + + if (ctx->log) + log_free(ctx->log); + + free(ctx); + + return 0; +} + +// Append point //////////////////////////////////////////////////////////////////// + diff --git a/libmem.h b/libmem.h new file mode 100644 index 0000000..e835156 --- /dev/null +++ b/libmem.h @@ -0,0 +1,210 @@ +/** + * @file libmem.h + * + * @brief Header file for memory management library + * + * @copyright Copyright (C) 2024 Jackrabbit Founders LLC. All rights reserved. + * + * @date Jul 2024 + * @author Barrett Edwards + * + * Macro / Enumeration Prefixes (LM) + * PL - Memory Online Policies + * ST - State options + * ZN - Valid Zones bitfield enum + * ZM - Valid Zones bitfield masks + */ +#ifndef _LIBMEM_H +#define _LIBMEM_H + +/* INCLUDES ==================================================================*/ + +/* MACROS ====================================================================*/ + +#define mem_blk_foreach(ctx, blk) for (blk = mem_blk_get_first(ctx); blk != NULL; blk = mem_blk_get_next(blk)) + +/* ENUMERATIONS ==============================================================*/ + +/** + * Enumereation represeting log destinations + */ +enum LMLD +{ + LMLD_STDIO = 0, + LMLD_SYSLOG = 1, + LMLD_NULL = 2, + LMLD_FILE = 3, + LMLD_MAX +}; + +/* Auto Online Policy Options */ +enum LMPL +{ + LMPL_OFFLINE = 0, + LMPL_ONLINE = 1, + LMPL_KERNEL = 2, + LMPL_MOVABLE = 3, + LMPL_MAX +}; + +/* State options */ +enum LMST +{ + LMST_OFFLINE = 0, + LMST_ONLINE = 1, + LMST_GOING_OFFLINE = 2, + LMST_MAX +}; + +/* Valid Zone options */ +enum LMZN +{ + LMZN_DMA = 0, + LMZN_DMA32 = 1, + LMZN_NORMAL = 2, + LMZN_MOVABLE = 3, + LMZN_NONE = 4, + LMZN_MAX +}; + +/* Bitfield masks for valid_zones */ +#define LMZM_DMA (0x01) +#define LMZM_DMA32 (0x02) +#define LMZM_NORMAL (0x04) +#define LMZM_MOVABLE (0x08) +#define LMZM_NONE (0x10) + +/* STRUCTS ===================================================================*/ + +struct mem_ctx; +struct mem_blk; + +/* + * Typedef for mem_set_log_fn() + */ +typedef void (*mem_log_fn)(struct mem_ctx *ctx, int priority, const char *fn, int ln, const char *format, va_list args); + +/* GLOBAL VARIABLES ==========================================================*/ + +/* PROTOTYPES ================================================================*/ + +/* Library Instantiation */ +int mem_new(struct mem_ctx **ctx); +struct mem_ctx * mem_ref(struct mem_ctx *ctx); +int mem_unref(struct mem_ctx *ctx); + +/* Library Log Configuration */ +int mem_log_get_priority(struct mem_ctx *ctx); +void mem_log_set_destination(struct mem_ctx *ctx, int dst, char *file); +void mem_log_set_fn(struct mem_ctx *ctx, + void (*mem_log_fn)(struct mem_ctx *ctx, + int priority, + const char *fn, + int line, + const char *format, + va_list args)); +void mem_log_set_priority(struct mem_ctx *ctx, int priority); + +/* Library Collections API - Get */ +struct cxl_region * mem_get_region(struct mem_ctx *ctx, char *name); +struct cxl_memdev * mem_get_memdev(struct mem_ctx *ctx, char *name); +struct cxl_memdev ** mem_get_memdevs(struct mem_ctx *ctx); +struct cxl_region ** mem_get_regions(struct mem_ctx *ctx); +struct cxl_decoder * mem_get_root_decoder(struct mem_ctx *ctx); +int mem_num_memdevs(struct mem_ctx* ctx); +int mem_num_regions(struct mem_ctx* ctx); + +/* Memory System API - Get */ +int * mem_system_get_blocks(struct mem_ctx *ctx); +unsigned long long mem_system_get_blocksize(struct mem_ctx *ctx); +unsigned long long mem_system_get_capacity(struct mem_ctx *ctx); +unsigned long long mem_system_get_capacity_offline(struct mem_ctx *ctx); +unsigned long long mem_system_get_capacity_online(struct mem_ctx *ctx); +int mem_system_get_policy(struct mem_ctx *ctx); +int mem_system_num_blocks(struct mem_ctx *ctx); +int mem_system_num_blocks_online(struct mem_ctx *ctx); +int mem_system_num_blocks_offline(struct mem_ctx *ctx); + +/* Memory System API - Actions */ +int mem_system_set_policy(struct mem_ctx *ctx, int mode); + +/* Memory Block API - Enumeration */ +struct mem_blk * mem_blk_get_first(struct mem_ctx *ctx); +struct mem_blk * mem_blk_get_next(struct mem_blk *blk); + +/* Memory Block API - Get */ +int mem_blk_get_device(struct mem_blk *blk); +int mem_blk_get_id(struct mem_blk *blk); +int mem_blk_get_node(struct mem_blk *blk); +struct cxl_region * mem_blk_get_region(struct mem_blk *blk); +int mem_blk_get_state(struct mem_blk *blk); +unsigned long mem_blk_get_zones(struct mem_blk *blk); +int mem_blk_is_online(struct mem_blk *blk); +int mem_blk_is_removable(struct mem_blk *blk); + +/* Memory Block API - Actions */ +int mem_blk_offline(struct mem_blk *blk); +int mem_blk_online(struct mem_blk *blk); +int mem_blk_set_state(struct mem_blk *blk, int state); + +/* Memory BlockID API - Get */ +struct mem_blk * mem_blkid_get_blk(struct mem_ctx *ctx, int id); +int mem_blkid_get_device(struct mem_ctx *ctx, int id); +int mem_blkid_get_node(struct mem_ctx *ctx, int id); +int mem_blkid_get_state(struct mem_ctx *ctx, int id); +unsigned long mem_blkid_get_zones(struct mem_ctx *ctx, int id); +int mem_blkid_is_online(struct mem_ctx *ctx, int id); +int mem_blkid_is_removable(struct mem_ctx *ctx, int id); + +/* Memory BlockID API - Actions */ +int mem_blkid_offline(struct mem_ctx *ctx, int index); +int mem_blkid_online(struct mem_ctx *ctx, int index); +int mem_blkid_set_state(struct mem_ctx *ctx, int index, int state); + +/* Memory Memdev API - Get */ +int mem_memdev_get_interleave_granularity(struct mem_ctx *ctx, struct cxl_memdev *memdev); +int mem_memdev_is_available(struct mem_ctx *ctx, struct cxl_memdev *memdev); + +/* Memory Region API - Get */ +int mem_region_get_blk_state(struct mem_ctx *ctx, struct cxl_region *region, int offset); +int * mem_region_get_blocks(struct mem_ctx *ctx, struct cxl_region *region); +unsigned long long mem_region_get_capacity(struct mem_ctx *ctx, struct cxl_region *region); +unsigned long long mem_region_get_capacity_offline(struct mem_ctx *ctx, struct cxl_region *region); +unsigned long long mem_region_get_capacity_online(struct mem_ctx *ctx, struct cxl_region *region); +int mem_region_is_daxmode(struct mem_ctx *ctx, struct cxl_region* region); +int mem_region_is_rammode(struct mem_ctx *ctx, struct cxl_region* region); +int mem_region_num_blocks(struct mem_ctx *ctx, struct cxl_region *region); +int mem_region_num_blocks_offline(struct mem_ctx *ctx, struct cxl_region *region); +int mem_region_num_blocks_online(struct mem_ctx *ctx, struct cxl_region *region); + +/* Memory Region API - Actions */ +int mem_region_create(struct mem_ctx *ctx, int granularity, int num, struct cxl_memdev **memdevs); +int mem_region_delete(struct mem_ctx *ctx, struct cxl_region *region); + +int mem_region_offline_blocks(struct mem_ctx *ctx, struct cxl_region *region); +int mem_region_online_blocks(struct mem_ctx *ctx, struct cxl_region *region); +int mem_region_set_blk_state(struct mem_ctx *ctx, struct cxl_region *region, int offset, int mode); + +int mem_region_daxmode(struct mem_ctx *ctx, struct cxl_region *region); +int mem_region_rammode(struct mem_ctx *ctx, struct cxl_region *region); + +/* Compare functions for qsort */ +int mem_compare_ints(const void* a, const void* b); +int mem_compare_cxl_memdevs(const void* a, const void* b); +int mem_compare_cxl_regions(const void* a, const void* b); + +/* Print Functions */ +void mem_blk_print(struct mem_blk *blk); + +/* String representations of enumerations */ +const char * mem_lmpl(int policy); +const char * mem_lmst(int state); +const char * mem_lmzn(int zone); + +/* Convert strings into enum values */ +int mem_to_lmpl(char *policy); +int mem_to_lmst(char *state); +int mem_to_lmzn(char *zone); + +#endif //ifndef _LIBMEM_H + diff --git a/log.c b/log.c new file mode 100644 index 0000000..9ba0013 --- /dev/null +++ b/log.c @@ -0,0 +1,196 @@ +/** + * @file log.c + * + * @brief Code file for logging library + * + * @copyright Copyright (C) 2024 Jackrabbit Founders LLC. All rights reserved. + * + * @date Jul 2024 + * @author Barrett Edwards + */ + +/* INCLUDES ==================================================================*/ + +/* printf() + * fprintf() + * vfprintf() + * FILE + * stdout + * stderr + */ +#include + +/* calloc() + * free() + */ +#include + +/* getpid() + */ +#include + +/* clock_gettime() + */ +#include + +/* va_start + * va_end + */ +#include + +/* LOG_* Macros + */ +#include + +#include "log.h" + +/* MACROS ====================================================================*/ + +/* ENUMERATIONS ==============================================================*/ + +/* STRUCTS ===================================================================*/ + +/* GLOBAL VARIABLES ==========================================================*/ + +/** + * String representation of logging levels + */ +static const char *levels[] = +{ + "EMERG", + "ALERT", + "CRIT", + "ERR", + "WARNING", + "NOTICE", + "INFO", + "DEBUG", +}; + +static const char *log_destinations[] = +{ + "STDIO", + "SYSLOG", + "NULL", + "FILE", +}; + +/* PROTOTYPES ================================================================*/ + +/* FUNCTIONS =================================================================*/ + +void log_to_syslog(struct log_ctx *ctx, int priority, const char *fn, int ln, const char *format, va_list args) +{ + vsyslog(priority, format, args); +} + +void log_to_stdio(struct log_ctx *ctx, int priority, const char *fn, int ln, const char *format, va_list args) +{ + struct timespec ts; + FILE *fp; + + if (priority >= LOG_INFO) + fp = stdout; + else + fp = stderr; + + if (ctx->timestamp) + { + // Get time for entry + clock_gettime(CLOCK_REALTIME, &ts); + + // print the time first + fprintf(fp, "[%10ld.%09ld] [%d] %s - ", ts.tv_sec, ts.tv_nsec, getpid(), levels[priority]); + fprintf(fp, "%s: %s:%d ", ctx->owner, fn, ln); + } + + vfprintf(fp, format, args); +} + +void log_to_file(struct log_ctx *ctx, int priority, const char *fn, int ln, const char *format, va_list args) +{ + struct timespec ts; + FILE *f = ctx->file; + + if (ctx->timestamp) + { + // Get time for entry + clock_gettime(CLOCK_REALTIME, &ts); + + // print the time first + fprintf(f, "[%10ld.%09ld] [%d] %s - ", ts.tv_sec, ts.tv_nsec, getpid(), levels[priority]); + fprintf(f, "%s: %s:%d ", ctx->owner, fn, ln); + } + + // print the message + vfprintf(f, format, args); + + // flush + fflush(f); +} + +void log_submit(struct log_ctx *ctx, int priority, const char *fn, int ln, const char *format, ...) +{ + va_list args; + va_start(args, format); + ctx->log_fn(ctx, priority, fn, ln, format, args); + va_end(args); +} + +struct log_ctx *log_init(const char *owner, int dst, int priority, int timestamp, char *filepath) +{ + struct log_ctx *l; + + // Allocate memory for the log struct + l = calloc(1, sizeof(*l)); + if (l == NULL) + goto end; + + // Set values + l->owner = owner; + l->priority = priority; + l->timestamp = timestamp; + + if (dst == LDST_SYSLOG) + l->log_fn = log_to_syslog; + else if (dst == LDST_NULL) + l->log_fn = log_to_null; + else if (dst == LDST_FILE & filepath != NULL) + { + l->file = fopen(filepath, "a"); + l->log_fn = log_to_file; + } + else + l->log_fn = log_to_stdio; + +end: + return l; +} + +/** + * Free the memory used by struct log_ctx + */ +int log_free(struct log_ctx *ctx) +{ + if (ctx->file != NULL) + fclose(ctx->file); + + if (ctx != NULL) + free(ctx); + + return 0; +} + +const char *log_priority_to_str(int priority) +{ + if (priority < 0 || priority > LOG_DEBUG) + return NULL; + return levels[priority]; +} + +const char *log_dst_to_str(int dst) +{ + if (dst < 0 || dst > LDST_MAX) + return NULL; + return log_destinations[dst]; +} diff --git a/log.h b/log.h new file mode 100644 index 0000000..f33e788 --- /dev/null +++ b/log.h @@ -0,0 +1,95 @@ +/** + * @file log.h + * + * @brief Header file for logging library + * + * @copyright Copyright (C) 2024 Jackrabbit Founders LLC. All rights reserved. + * + * @date Jul 2024 + * @author Barrett Edwards + * + * Log levels for reference: + * LOG_EMERG 0 system is unusable + * LOG_ALERT 1 action must be taken immediately + * LOG_CRIT 2 critical conditions + * LOG_ERR 3 error conditions + * LOG_WARNING 4 warning conditions + * LOG_NOTICE 5 normal but significant condition + * LOG_INFO 6 informational + * LOG_DEBUG 7 debug-level messages + */ + +#ifndef _LOG_H +#define _LOG_H + +/* INCLUDES ==================================================================*/ + +/* MACROS ====================================================================*/ + +#define dbg(x, arg...) log_dbg(x->log, ## arg) +#define info(x, arg...) log_info(x->log, ## arg) +#define notice(x, arg...) log_notice(x->log, ## arg) +#define warn(x, arg...) log_warn(x->log, ## arg) +#define err(x, arg...) log_err(x->log, ## arg) + +#ifdef ENABLE_LOGGING +# define log_dbg(ctx, arg...) do { if ((ctx)->priority >= LOG_DEBUG) log_submit(ctx, LOG_DEBUG, __FUNCTION__, __LINE__, ## arg); } while(0) +# define log_info(ctx, arg...) do { if ((ctx)->priority >= LOG_INFO) log_submit(ctx, LOG_INFO, __FUNCTION__, __LINE__, ## arg); } while(0) +# define log_notice(ctx, arg...) do { if ((ctx)->priority >= LOG_NOTICE) log_submit(ctx, LOG_NOTICE, __FUNCTION__, __LINE__, ## arg); } while(0) +# define log_warn(ctx, arg...) do { if ((ctx)->priority >= LOG_WARNING) log_submit(ctx, LOG_WARNING, __FUNCTION__, __LINE__, ## arg); } while(0) +# define log_err(ctx, arg...) do { if ((ctx)->priority >= LOG_ERR) log_submit(ctx, LOG_ERR, __FUNCTION__, __LINE__, ## arg); } while(0) +#else +# define log_dbg(ctx, arg...) {} +# define log_info(ctx, arg...) {} +# define log_notice(ctx, arg...) {} +# define log_warn(ctx, arg...) {} +# define log_err(ctx, arg...) {} +#endif + +/* ENUMERATIONS ==============================================================*/ + +/** + * Enumereation represeting log destinations to use + */ +enum LOG_DST +{ + LDST_STDIO = 0, + LDST_SYSLOG = 1, + LDST_NULL = 2, + LDST_FILE = 3, + LDST_MAX +}; + +/* STRUCTS ===================================================================*/ + +struct log_ctx; +typedef void (*log_fn)(struct log_ctx *ctx, int priority, const char *fn, int ln, const char *format, va_list args); + +struct log_ctx +{ + log_fn log_fn; + int timestamp; + int priority; + const char *owner; + FILE *file; +}; + +/* GLOBAL VARIABLES ==========================================================*/ + +/* PROTOTYPES ================================================================*/ + +struct log_ctx *log_init(const char *owner, int dst, int level, int timestamp, char *filepath); +void log_submit(struct log_ctx *ctx, int priority, const char *fn, int ln, const char *format, ...); +int log_free(struct log_ctx *ctx); + +/* Log functions */ +void log_to_file(struct log_ctx *ctx, int priority, const char *fn, int ln, const char *format, va_list args); +void log_to_stdio(struct log_ctx *ctx, int priority, const char *fn, int ln, const char *format, va_list args); +void log_to_syslog(struct log_ctx *ctx, int priority, const char *fn, int ln, const char *format, va_list args); +static inline void log_to_null(struct log_ctx *ctx, int priority, const char *fn, int ln, const char *format, va_list args) {} + +const char *log_priority_to_str(int priority); +const char *log_dst_to_str(int dst); + +#endif //ifndef _LOG_H + diff --git a/options.c b/options.c new file mode 100644 index 0000000..b03346b --- /dev/null +++ b/options.c @@ -0,0 +1,2410 @@ +/** + * @file options.c + * + * @brief Code file for CLI options + * + * @copyright Copyright (C) 2024 Jackrabbit Founders LLC. All rights reserved. + * + * @date Jul 2024 + * @author Barrett Edwards + * + */ + +/* INCLUDES ==================================================================*/ + +/* memset() + * memcpy() + * strdup() + * strlen() + * strnlen() + * strstr() + * strcmp() + * strncmp() + */ +#include + +/* free() + * calloc() + * malloc() + * realloc() + * strtoul() + * exit() + * getenv() + */ +#include + +/* struct argp + * struct argp_state + * argp_parse() + */ +#include + +#include "options.h" + +/* MACROS ====================================================================*/ + +#define DEFAULT_WIDTH 16 //!< Default width for printing __u8 buffers +#define MIN_WIDTH 4 //!< Minium width for printing buffers + +/* ENUMERATIONS ==============================================================*/ + +/* STRUCTS ===================================================================*/ + +/** + * Local struct used to define shell environment variables and the key to parse them + */ +struct envopt +{ + char key; + char *name; +}; + +/* PROTOTYPES ================================================================*/ + +static void print_help(int option); +static void print_options(struct argp_option *o); +static void print_usage(int option, struct argp_option *o); + +static int pr_main (int key, char *arg, struct argp_state *state); + +static int pr_block (int key, char *arg, struct argp_state *state); +static int pr_list (int key, char *arg, struct argp_state *state); +static int pr_region (int key, char *arg, struct argp_state *state); +static int pr_set (int key, char *arg, struct argp_state *state); +static int pr_show (int key, char *arg, struct argp_state *state); + +static int pr_set_block (int key, char *arg, struct argp_state *state); +static int pr_set_region (int key, char *arg, struct argp_state *state); +static int pr_set_system (int key, char *arg, struct argp_state *state); + +static int pr_show_block (int key, char *arg, struct argp_state *state); +static int pr_show_capacity (int key, char *arg, struct argp_state *state); +static int pr_show_device (int key, char *arg, struct argp_state *state); +static int pr_show_num (int key, char *arg, struct argp_state *state); +static int pr_show_region (int key, char *arg, struct argp_state *state); +static int pr_show_system (int key, char *arg, struct argp_state *state); + +/* GLOBAL VARIABLES ==========================================================*/ + +/** + * Global varible to store parsed CLI options + */ +struct opt *opts; + +const char *argp_program_version = "version 0.1"; + +/** + * String representation of CLOP Enumeration + */ +char *CLOP[] = { + "VERBOSITY", + "PRNT_OPTS", + "INFILE", + "OUTFILE", + "NUM", + "ALL", + "LEN", + "LIMIT", + "CMD", + "DATA", + "HUMAN", + "DEVICE", + "REGION", + "BLOCK", + "GRANULARITY", + "ZONE", + "ONLINE", + "OFFLINE", + "KERNEL", + "MOVABLE", + "BLOCKS", + "DEVICES", + "REGIONS" +}; + + +/* Help Output strings */ +const char *ho_main = "\n\ +Usage: mem [[subcommand] . . .] \n\n\ +Subcommands: \n\ + block Perform actions on a memory block(s) \n\ + info Display information about memory system \n\ + list List memory blocks \n\ + region Perform actions on a memory region \n\ + set Configure a component or sytem setting \n\ + show Display information \n\ +"; + +const char *ho_block = "\n\ +Usage: mem block [ ] \n\n\ +Subcommands: \n\ + online Online a memory block \n\ + offline Offline a memory block \n\ + kernel Online a memory block to zone normal \n\ + movable Online a memory block to zone movable \n\ +"; + +const char *ho_list = "\n\ +Usage: mem list [ ] \n\n\ +Filters. These filter the data to include only the desired qualifier: \n\ + offline Show offline blocks \n\ + online Show online blocks \n\ + Show blocks of a region \n\ +"; + +const char *ho_region = "\n\ +Usage: mem region [ ] \n\n\ +Subcommands: \n\ + create Create a region from memory devices (mem0 mem1 ... ) \n\ + delete Delete a region \n\ + disable Disable a region \n\ + enable Enable a region \n\ + daxmode Enable DAX mode of a region \n\ + rammode Enable RAM mode of a region (default)\n\ +"; + +const char *ho_set = "\n\ +Usage: mem set [subcommand ] \n\n\ +Subcommands: \n\ + policy Set system memory online policy \n\ +"; + +const char *ho_show = "\n\ +Usage: mem show [subcommand ] \n\n\ +Subcommands: \n\ + block State of memory blocks \n\ + capacity Show memory capacity \n\ + device List of memory devices \n\ + num Count of items \n\ + region List of memory regions \n\ + system Memory System values \n\ +"; + +const char *ho_show_block = "\n\ +Usage: mem show block [subcommand ] \n\n\ +Single memory block Subcommands. Requires to be specified: \n\ + device Show physical device of a specific block \n\ + isonline Show if a specific block is online \n\ + isremovable Show if a specific block is removable \n\ + node Show the CPU node of a specific block \n\ + state Show the state of a specific memory block \n\ + zones Show the valid zones of a specific memory block \n\ + \n\ +Filters. These filter the data to include only the desired qualifier:\n\ + offline Show offline memory blocks \n\ + online Show online memory blocks \n\ + Show memory blocks from region \n\ +"; + +const char *ho_show_capacity = "\n\ +Usage: mem show capacity [subcommand ] \n\n\ +Filters. These filter the data to include only the desired qualifier: \n\ + offline Show capacity of offline blocks \n\ + online Show capacity of online blocks \n\ + Show capacity of a region \n\ +"; + +const char *ho_show_device = "\n\ +Usage: mem show device [subcommand ] \n\n\ +Subcommands: \n\ + ig Show device interleave granularity \n\ + isavailable Show if device is not currently part of a region \n\n\ +Filters. These filter the data to include only the desired qualifier: \n\ + Show devices that are part of a region \n\ + Show device that matches this name \n\ +"; + +const char *ho_show_num = "\n\ +Usage: mem show num \n\n\ +Objects to count: \n\ + blocks Show number of blocks \n\ + devices Show number of devices \n\ + regions Show number of regions \n\n\ +Filters. These filter the data to include only the desired qualifier: \n\ + offline Show number of offline blocks \n\ + online Show number of online blocks \n\ + Show number of blocks that are part of a region \n\ +"; + +const char *ho_show_region = "\n\ +Usage: mem show region [subcommand ] \n\n\ +Filters. These filter the data to include only the desired qualifier: \n\ + Show this region \n\ +"; + +const char *ho_show_system = "\n\ +Usage: mem show system [subcommand ] \n\n\ +Subcommands: \n\ + blocksize Show system block size \n\ + policy Show system auto online policy \n\ +"; + + +/** + * Global array of CLI options to pull from the shell environment if present + */ +struct envopt envopts[] = +{ + {0,0} +}; + +/** + * Global char pointer to dynamically store the name of the application + * + * This is allocated and stored when parse_options() is called + */ +static char *app_name; + +/** + * CLAP_MAIN - mem + */ +struct argp_option ao_main[] = +{ + {0, 0, 0, OPTION_HIDDEN, "Command options", 1}, + + {0, 0, 0, OPTION_HIDDEN, "Object options", 3}, + {"block", 'b', "INT", OPTION_HIDDEN, "Memory Block ID (e.g. 306)", 0}, + {"device", 'd', "STR", OPTION_HIDDEN, "Device name (e.g. mem0)", 0}, + {"region", 'r', "STR", OPTION_HIDDEN, "Region name (e.g. region0)", 0}, + {"interleave", 'g', "INT", OPTION_HIDDEN, "Interleave Granularity (e.g. 4096)", 0}, + + {0, 0, 0, OPTION_HIDDEN, "State options", 5}, + {"offline", '0', NULL, OPTION_HIDDEN, "Offline object", 0}, + {"online", '1', NULL, OPTION_HIDDEN, "Online object", 0}, + {"kernel", 'k', NULL, OPTION_HIDDEN, "Zone normal/kernel", 0}, + {"movable", 'm', NULL, OPTION_HIDDEN, "Zone movable", 0}, + + {0, 0, 0, OPTION_HIDDEN, "Output options", 7}, + {"human", 'H', NULL, OPTION_HIDDEN, "Human readable output (K, M, G, T)", 0}, + {"num", 'n', NULL, OPTION_HIDDEN, "Dsipaly the number of items", 0}, + {"all", 'a', NULL, OPTION_HIDDEN, "Perform action on all items", 0}, + + {0, 0, 0, 0, "Help options", 9}, + {"help", 'h', NULL, 0, "Display Help", 0}, + {"usage", 701, NULL, 0, "Display Usage", 0}, + {"version", 702, NULL, 0, "Display Version", 0}, + {"print-options", 706, NULL, OPTION_HIDDEN, "Print options array", 0}, + + {0,0,0,0,0,0} // Final option should be all null +}; + +/** + * CLAP_BLOCK - mem block + */ +struct argp_option ao_block[] = +{ + {0, 0, 0, OPTION_HIDDEN, "Command options", 1}, + + {0, 0, 0, 0, "Object options", 3}, + {"block", 'b', "INT", 0, "Memory Block ID (e.g. 306)", 0}, + {"device", 'd', "STR", OPTION_HIDDEN, "Device name (e.g. mem0)", 0}, + {"region", 'r', "STR", 0, "Region name (e.g. region0)", 0}, + {"interleave", 'g', "INT", OPTION_HIDDEN, "Interleave Granularity (e.g. 4096)", 0}, + {"all", 'a', NULL, 0, "Perform action on all items", 0}, + + {0, 0, 0, 0, "State options", 5}, + {"offline", '0', NULL, 0, "Offline object", 0}, + {"online", '1', NULL, 0, "Online object", 0}, + {"kernel", 'k', NULL, 0, "Zone normal/kernel", 0}, + {"movable", 'm', NULL, 0, "Zone movable", 0}, + + {0, 0, 0, OPTION_HIDDEN, "Output options", 7}, + {"human", 'H', NULL, OPTION_HIDDEN, "Human readable output (K, M, G, T)", 0}, + {"num", 'n', NULL, OPTION_HIDDEN, "Dsipaly the number of items", 0}, + + {0, 0, 0, 0, "Help options", 9}, + {"help", 'h', NULL, 0, "Display Help", 0}, + {"usage", 701, NULL, 0, "Display Usage", 0}, + {"version", 702, NULL, 0, "Display Version", 0}, + {"print-options", 706, NULL, OPTION_HIDDEN, "Print options array", 0}, + + {0,0,0,0,0,0} // Final option should be all null +}; + +/** + * CLAP_LIST - mem list + */ +struct argp_option ao_list[] = +{ + {0, 0, 0, OPTION_HIDDEN, "Command options", 1}, + + {0, 0, 0, OPTION_HIDDEN, "Filter options", 3}, + {"block", 'b', "INT", OPTION_HIDDEN, "Memory Block ID (e.g. 306)", 0}, + {"device", 'd', "STR", OPTION_HIDDEN, "Device name (e.g. mem0)", 0}, + {"region", 'r', "STR", 0, "Region name (e.g. region0)", 0}, + {"interleave", 'g', "INT", OPTION_HIDDEN, "Interleave Granularity (e.g. 4096)", 0}, + {"offline", '0', NULL, 0, "List Offline blocks", 0}, + {"online", '1', NULL, 0, "List Online blocks", 0}, + + {0, 0, 0, OPTION_HIDDEN, "State options", 5}, + {"kernel", 'k', NULL, OPTION_HIDDEN, "Zone normal/kernel", 0}, + {"movable", 'm', NULL, OPTION_HIDDEN, "Zone movable", 0}, + + {0, 0, 0, 0, "Output options", 7}, + {"human", 'H', NULL, 0, "Human readable output (K, M, G, T)", 0}, + {"num", 'n', NULL, OPTION_HIDDEN, "Dsipaly the number of items", 0}, + {"all", 'a', NULL, OPTION_HIDDEN, "Perform action on all items", 0}, + + {0, 0, 0, 0, "Help options", 9}, + {"help", 'h', NULL, 0, "Display Help", 0}, + {"usage", 701, NULL, 0, "Display Usage", 0}, + {"version", 702, NULL, 0, "Display Version", 0}, + {"print-options", 706, NULL, OPTION_HIDDEN, "Print options array", 0}, + + {0,0,0,0,0,0} // Final option should be all null +}; + + +/** + * CLAP_REGION - mem region + */ +struct argp_option ao_region[] = +{ + {0, 0, 0, OPTION_HIDDEN, "Command options", 1}, + + {0, 0, 0, 0, "Create options", 3}, + {"block", 'b', "INT", OPTION_HIDDEN, "Memory Block ID (e.g. 306)", 0}, + {"device", 'd', "STR", OPTION_HIDDEN, "Device name (e.g. mem0)", 0}, + {"region", 'r', "STR", OPTION_HIDDEN, "Region name (e.g. region0)", 0}, + {"interleave", 'g', "INT", 0, "Interleave Granularity (Default 4096)",0}, + {"all", 'a', NULL, 0, "Use all memory devices", 0}, + + {0, 0, 0, OPTION_HIDDEN, "State options", 5}, + {"offline", '0', NULL, OPTION_HIDDEN, "Offline object", 0}, + {"online", '1', NULL, OPTION_HIDDEN, "Online object", 0}, + {"kernel", 'k', NULL, OPTION_HIDDEN, "Zone normal/kernel", 0}, + {"movable", 'm', NULL, OPTION_HIDDEN, "Zone movable", 0}, + + {0, 0, 0, OPTION_HIDDEN, "Output options", 7}, + {"human", 'H', NULL, OPTION_HIDDEN, "Human readable output (K, M, G, T)", 0}, + {"num", 'n', NULL, OPTION_HIDDEN, "Dsipaly the number of items", 0}, + + {0, 0, 0, 0, "Help options", 9}, + {"help", 'h', NULL, 0, "Display Help", 0}, + {"usage", 701, NULL, 0, "Display Usage", 0}, + {"version", 702, NULL, 0, "Display Version", 0}, + {"print-options", 706, NULL, OPTION_HIDDEN, "Print options array", 0}, + + {0,0,0,0,0,0} // Final option should be all null +}; + +/** + * CLAP_SET - mem set + */ +struct argp_option ao_set[] = +{ + {0, 0, 0, OPTION_HIDDEN, "Command options", 1}, + + {0, 0, 0, OPTION_HIDDEN, "Object options", 3}, + {"block", 'b', "INT", OPTION_HIDDEN, "Memory Block ID (e.g. 306)", 0}, + {"device", 'd', "STR", OPTION_HIDDEN, "Device name (e.g. mem0)", 0}, + {"region", 'r', "STR", OPTION_HIDDEN, "Region name (e.g. region0)", 0}, + {"interleave", 'g', "INT", OPTION_HIDDEN, "Interleave Granularity (e.g. 4096)", 0}, + + {0, 0, 0, 0, "Policy options", 5}, + {"offline", '0', NULL, 0, "Default new memory blocks to offline", 0}, + {"online", '1', NULL, 0, "Default new memory blocks to online", 0}, + {"kernel", 'k', NULL, 0, "Default new memory blocks to normal", 0}, + {"movable", 'm', NULL, 0, "Default new memory blocks to movable", 0}, + + {0, 0, 0, OPTION_HIDDEN, "Output options", 7}, + {"human", 'H', NULL, OPTION_HIDDEN, "Human readable output (K, M, G, T)", 0}, + {"num", 'n', NULL, OPTION_HIDDEN, "Dsipaly the number of items", 0}, + {"all", 'a', NULL, OPTION_HIDDEN, "Perform action on all items", 0}, + + {0, 0, 0, 0, "Help options", 9}, + {"help", 'h', NULL, 0, "Display Help", 0}, + {"usage", 701, NULL, 0, "Display Usage", 0}, + {"version", 702, NULL, 0, "Display Version", 0}, + {"print-options", 706, NULL, OPTION_HIDDEN, "Print options array", 0}, + + {0,0,0,0,0,0} // Final option should be all null +}; + +/** + * CLAP_SHOW - mem show + */ +struct argp_option ao_show[] = +{ + {0, 0, 0, OPTION_HIDDEN, "Command options", 1}, + + {0, 0, 0, OPTION_HIDDEN, "Object options", 3}, + {"block", 'b', "INT", OPTION_HIDDEN, "Memory Block ID (e.g. 306)", 0}, + {"device", 'd', "STR", OPTION_HIDDEN, "Device name (e.g. mem0)", 0}, + {"region", 'r', "STR", OPTION_HIDDEN, "Region name (e.g. region0)", 0}, + {"interleave", 'g', "INT", OPTION_HIDDEN, "Interleave Granularity (e.g. 4096)", 0}, + + {0, 0, 0, OPTION_HIDDEN, "State options", 5}, + {"offline", '0', NULL, OPTION_HIDDEN, "Offline object", 0}, + {"online", '1', NULL, OPTION_HIDDEN, "Online object", 0}, + {"kernel", 'k', NULL, OPTION_HIDDEN, "Zone normal/kernel", 0}, + {"movable", 'm', NULL, OPTION_HIDDEN, "Zone movable", 0}, + + {0, 0, 0, OPTION_HIDDEN, "Output options", 7}, + {"human", 'H', NULL, OPTION_HIDDEN, "Human readable output (K, M, G, T)", 0}, + {"num", 'n', NULL, OPTION_HIDDEN, "Dsipaly the number of items", 0}, + {"all", 'a', NULL, OPTION_HIDDEN, "Perform action on all items", 0}, + + {0, 0, 0, 0, "Help options", 9}, + {"help", 'h', NULL, 0, "Display Help", 0}, + {"usage", 701, NULL, 0, "Display Usage", 0}, + {"version", 702, NULL, 0, "Display Version", 0}, + {"print-options", 706, NULL, OPTION_HIDDEN, "Print options array", 0}, + + {0,0,0,0,0,0} // Final option should be all null +}; + +/** + * CLAP_SHOW_BLOCK - mem show block + */ +struct argp_option ao_show_block[] = +{ + {0, 0, 0, OPTION_HIDDEN, "Command options", 1}, + + {0, 0, 0, OPTION_HIDDEN, "Object options", 3}, + {"device", 'd', "STR", OPTION_HIDDEN, "Device name (e.g. mem0)", 0}, + {"interleave", 'g', "INT", OPTION_HIDDEN, "Interleave Granularity (e.g. 4096)", 0}, + + {0, 0, 0, OPTION_HIDDEN, "Result Filters", 5}, + {"block", 'b', "INT", 0, "Memory Block ID (e.g. 306)", 0}, + {"offline", '0', NULL, 0, "Offline object", 0}, + {"online", '1', NULL, 0, "Online object", 0}, + {"region", 'r', "STR", 0, "Region name (e.g. region0)", 0}, + {"kernel", 'k', NULL, OPTION_HIDDEN, "Zone normal/kernel", 0}, + {"movable", 'm', NULL, OPTION_HIDDEN, "Zone movable", 0}, + + {0, 0, 0, OPTION_HIDDEN, "Output options", 7}, + {"human", 'H', NULL, OPTION_HIDDEN, "Human readable output (K, M, G, T)", 0}, + {"num", 'n', NULL, OPTION_HIDDEN, "Dsipaly the number of items", 0}, + {"all", 'a', NULL, OPTION_HIDDEN, "Perform action on all items", 0}, + + {0, 0, 0, 0, "Help options", 9}, + {"help", 'h', NULL, 0, "Display Help", 0}, + {"usage", 701, NULL, 0, "Display Usage", 0}, + {"version", 702, NULL, 0, "Display Version", 0}, + {"print-options", 706, NULL, OPTION_HIDDEN, "Print options array", 0}, + + {0,0,0,0,0,0} // Final option should be all null +}; + +/** + * CLAP_SHOW_CAPACITY - mem show capacity + */ +struct argp_option ao_show_capacity[] = +{ + {0, 0, 0, OPTION_HIDDEN, "Command options", 1}, + + {0, 0, 0, OPTION_HIDDEN, "Filter options", 3}, + {"block", 'b', "INT", OPTION_HIDDEN, "Memory Block ID (e.g. 306)", 0}, + {"device", 'd', "STR", OPTION_HIDDEN, "Device name (e.g. mem0)", 0}, + {"region", 'r', "STR", 0, "Region name (e.g. region0)", 0}, + {"interleave", 'g', "INT", OPTION_HIDDEN, "Interleave Granularity (e.g. 4096)", 0}, + {"offline", '0', NULL, 0, "Offline capacity", 0}, + {"online", '1', NULL, 0, "Online capacity", 0}, + + {0, 0, 0, OPTION_HIDDEN, "State options", 5}, + {"kernel", 'k', NULL, OPTION_HIDDEN, "Zone normal/kernel", 0}, + {"movable", 'm', NULL, OPTION_HIDDEN, "Zone movable", 0}, + + {0, 0, 0, 0, "Output options", 7}, + {"human", 'H', NULL, 0, "Human readable output (K, M, G, T)", 0}, + {"num", 'n', NULL, OPTION_HIDDEN, "Dsipaly the number of items", 0}, + {"all", 'a', NULL, OPTION_HIDDEN, "Perform action on all items", 0}, + + {0, 0, 0, 0, "Help options", 9}, + {"help", 'h', NULL, 0, "Display Help", 0}, + {"usage", 701, NULL, 0, "Display Usage", 0}, + {"version", 702, NULL, 0, "Display Version", 0}, + {"print-options", 706, NULL, OPTION_HIDDEN, "Print options array", 0}, + + {0,0,0,0,0,0} // Final option should be all null +}; + +/** + * CLAP_SHOW_DEVICE - mem show device + */ +struct argp_option ao_show_device[] = +{ + {0, 0, 0, OPTION_HIDDEN, "Command options", 1}, + + {0, 0, 0, OPTION_HIDDEN, "Object options", 3}, + {"block", 'b', "INT", OPTION_HIDDEN, "Memory Block ID (e.g. 306)", 0}, + {"device", 'd', "STR", 0, "Device name (e.g. mem0)", 0}, + {"region", 'r', "STR", 0, "Region name (e.g. region0)", 0}, + {"interleave", 'g', "INT", OPTION_HIDDEN, "Interleave Granularity (e.g. 4096)", 0}, + + {0, 0, 0, OPTION_HIDDEN, "State options", 5}, + {"offline", '0', NULL, OPTION_HIDDEN, "Offline object", 0}, + {"online", '1', NULL, OPTION_HIDDEN, "Online object", 0}, + {"kernel", 'k', NULL, OPTION_HIDDEN, "Zone normal/kernel", 0}, + {"movable", 'm', NULL, OPTION_HIDDEN, "Zone movable", 0}, + + {0, 0, 0, 0, "Output options", 7}, + {"human", 'H', NULL, 0, "Human readable output (K, M, G, T)", 0}, + {"num", 'n', NULL, OPTION_HIDDEN, "Dsipaly the number of items", 0}, + {"all", 'a', NULL, OPTION_HIDDEN, "Perform action on all items", 0}, + + {0, 0, 0, 0, "Help options", 9}, + {"help", 'h', NULL, 0, "Display Help", 0}, + {"usage", 701, NULL, 0, "Display Usage", 0}, + {"version", 702, NULL, 0, "Display Version", 0}, + {"print-options", 706, NULL, OPTION_HIDDEN, "Print options array", 0}, + + {0,0,0,0,0,0} // Final option should be all null +}; + +/** + * CLAP_SHOW_NUM - mem show num + */ +struct argp_option ao_show_num[] = +{ + {0, 0, 0, OPTION_HIDDEN, "Command options", 1}, + + {0, 0, 0, OPTION_HIDDEN, "Filters", 3}, + {"block", 'b', "INT", OPTION_HIDDEN, "Memory Block ID (e.g. 306)", 0}, + {"device", 'd', "STR", OPTION_HIDDEN, "Device name (e.g. mem0)", 0}, + {"offline", '0', NULL, 0, "Num offline blocks", 0}, + {"online", '1', NULL, 0, "Num online blocks", 0}, + {"region", 'r', "STR", 0, "Num blocks in region (e.g. region0)", 0}, + {"interleave", 'g', "INT", OPTION_HIDDEN, "Interleave Granularity (e.g. 4096)", 0}, + + {0, 0, 0, OPTION_HIDDEN, "State options", 5}, + {"kernel", 'k', NULL, OPTION_HIDDEN, "Zone normal/kernel", 0}, + {"movable", 'm', NULL, OPTION_HIDDEN, "Zone movable", 0}, + + {0, 0, 0, OPTION_HIDDEN, "Output options", 7}, + {"human", 'H', NULL, OPTION_HIDDEN, "Human readable output (K, M, G, T)", 0}, + {"num", 'n', NULL, OPTION_HIDDEN, "Dsipaly the number of items", 0}, + {"all", 'a', NULL, OPTION_HIDDEN, "Perform action on all items", 0}, + + {0, 0, 0, 0, "Help options", 9}, + {"help", 'h', NULL, 0, "Display Help", 0}, + {"usage", 701, NULL, 0, "Display Usage", 0}, + {"version", 702, NULL, 0, "Display Version", 0}, + {"print-options", 706, NULL, OPTION_HIDDEN, "Print options array", 0}, + + {0,0,0,0,0,0} // Final option should be all null +}; + +/** + * CLAP_SHOW_REGION - Options for mem show region + */ +struct argp_option ao_show_region[] = +{ + {0, 0, 0, OPTION_HIDDEN, "Command options", 1}, + + {0, 0, 0, OPTION_HIDDEN, "Object options", 3}, + {"block", 'b', "INT", OPTION_HIDDEN, "Memory Block ID (e.g. 306)", 0}, + {"device", 'd', "STR", OPTION_HIDDEN, "Device name (e.g. mem0)", 0}, + {"region", 'r', "STR", 0, "Region name (e.g. region0)", 0}, + {"interleave", 'g', "INT", OPTION_HIDDEN, "Interleave Granularity (e.g. 4096)", 0}, + + {0, 0, 0, OPTION_HIDDEN, "State options", 5}, + {"offline", '0', NULL, OPTION_HIDDEN, "Offline object", 0}, + {"online", '1', NULL, OPTION_HIDDEN, "Online object", 0}, + {"kernel", 'k', NULL, OPTION_HIDDEN, "Zone normal/kernel", 0}, + {"movable", 'm', NULL, OPTION_HIDDEN, "Zone movable", 0}, + + {0, 0, 0, 0, "Output options", 7}, + {"human", 'H', NULL, 0, "Human readable output (K, M, G, T)", 0}, + {"num", 'n', NULL, OPTION_HIDDEN, "Dsipaly the number of items", 0}, + {"all", 'a', NULL, OPTION_HIDDEN, "Perform action on all items", 0}, + + {0, 0, 0, 0, "Help options", 9}, + {"help", 'h', NULL, 0, "Display Help", 0}, + {"usage", 701, NULL, 0, "Display Usage", 0}, + {"version", 702, NULL, 0, "Display Version", 0}, + {"print-options", 706, NULL, OPTION_HIDDEN, "Print options array", 0}, + + {0,0,0,0,0,0} // Final option should be all null +}; + +/** + * CLAP_SHOW_SYSTEM - mem show system + */ +struct argp_option ao_show_system[] = +{ + {0, 0, 0, OPTION_HIDDEN, "Command options", 1}, + + {0, 0, 0, OPTION_HIDDEN, "Object options", 3}, + {"block", 'b', "INT", OPTION_HIDDEN, "Memory Block ID (e.g. 306)", 0}, + {"device", 'd', "STR", OPTION_HIDDEN, "Device name (e.g. mem0)", 0}, + {"region", 'r', "STR", OPTION_HIDDEN, "Region name (e.g. region0)", 0}, + {"interleave", 'g', "INT", OPTION_HIDDEN, "Interleave Granularity (e.g. 4096)", 0}, + + {0, 0, 0, OPTION_HIDDEN, "State options", 5}, + {"offline", '0', NULL, OPTION_HIDDEN, "Offline object", 0}, + {"online", '1', NULL, OPTION_HIDDEN, "Online object", 0}, + {"kernel", 'k', NULL, OPTION_HIDDEN, "Zone normal/kernel", 0}, + {"movable", 'm', NULL, OPTION_HIDDEN, "Zone movable", 0}, + + {0, 0, 0, 0, "Output options", 7}, + {"human", 'H', NULL, 0, "Human readable output (K, M, G, T)", 0}, + {"num", 'n', NULL, OPTION_HIDDEN, "Dsipaly the number of items", 0}, + {"all", 'a', NULL, OPTION_HIDDEN, "Perform action on all items", 0}, + + {0, 0, 0, 0, "Help options", 9}, + {"help", 'h', NULL, 0, "Display Help", 0}, + {"usage", 701, NULL, 0, "Display Usage", 0}, + {"version", 702, NULL, 0, "Display Version", 0}, + {"print-options", 706, NULL, OPTION_HIDDEN, "Print options array", 0}, + + {0,0,0,0,0,0} // Final option should be all null +}; + +/** + * struct argp objects + * + * Use [CLAP] enum to index into this array + */ +struct argp ap_main = {ao_main , pr_main , 0, 0, 0, 0, 0}; + +struct argp ap_block = {ao_block , pr_block , 0, 0, 0, 0, 0}; +struct argp ap_list = {ao_list , pr_list , 0, 0, 0, 0, 0}; +struct argp ap_region = {ao_region , pr_region , 0, 0, 0, 0, 0}; +struct argp ap_set = {ao_set , pr_set , 0, 0, 0, 0, 0}; +struct argp ap_show = {ao_show , pr_show , 0, 0, 0, 0, 0}; + +struct argp ap_show_block = {ao_show_block , pr_show_block , 0, 0, 0, 0, 0}; +struct argp ap_show_capacity = {ao_show_capacity , pr_show_capacity , 0, 0, 0, 0, 0}; +struct argp ap_show_device = {ao_show_device , pr_show_device , 0, 0, 0, 0, 0}; +struct argp ap_show_num = {ao_show_num , pr_show_num , 0, 0, 0, 0, 0}; +struct argp ap_show_region = {ao_show_region , pr_show_region , 0, 0, 0, 0, 0}; +struct argp ap_show_system = {ao_show_system , pr_show_system , 0, 0, 0, 0, 0}; + +/* FUNCTIONS =================================================================*/ + +/* + * Print a unsigned char buffer + */ +static void prnt_buf(void *buf, unsigned long len, unsigned long width, int print_header) +{ + unsigned long i, j, k, rows; + __u8 *ptr; + + /* STEP 1: Verify Inputs */ + if ( buf == NULL) + return; + + if ( len == 0 ) + return; + + if ( width == 0 ) + width = DEFAULT_WIDTH; + + if ( width < MIN_WIDTH ) + width = MIN_WIDTH; + + ptr = (__u8*) buf; + + /* Compute the number of rows to print */ + rows = len / width; + if ( (len % width) > 0) + rows++; + + /* Print index '0x0000: ' */ + if (print_header) { + printf(" "); + for ( i = 0 ; i < width ; i++ ) + printf("%02lu ", i); + printf("\n"); + } + + k = 0; + for ( i = 0 ; i < rows ; i++ ) { + printf("0x%08lx: ", i * width); + for ( j = 0 ; j < width ; j++ ) { + if (k >= len) + break; + + printf("%02x ", ptr[i*width + j]); + + k++; + } + printf("\n"); + } + + return; +} + +/** + * Return a string representation of CLI Option Names [CLOP] + */ +char *clop(int u) +{ + if (u >= CLOP_MAX) + return NULL; + else + return CLOP[u]; +} + +/** + * Free allocated memory by option parsing proceedure + * + * @return 0 upon success. Non zero otherwise + */ +int options_free() +{ + int rv; + struct opt *o; + int i; + + rv = 1; + + if (opts == NULL) + goto end; + + for ( i = 0 ; i < CLOP_MAX ; i++ ) + { + o = &opts[i]; + + // Free buf field + if (o->buf) + free(o->buf); + o->buf = NULL; + + // Free str field + if (o->str) + free(o->str); + o->str = NULL; + } + + // Free options array + free(opts); + + // Free app Name + if (app_name) + free(app_name); + + rv = 0; + +end: + + return rv; +} + +/** + * Print the command line flag options to the screen as part of help output + * + * @param o the menu level [CLAP] enum + */ +static void print_options(struct argp_option *o) +{ + int len; + + while (o->doc != NULL) + { + // Break if this is the ending NULL entry + if ( !o->name && !o->key && !o->arg && !o->flags && !o->doc && !o->group ) + break; + + // Skip Hidden Options + if (o->flags & OPTION_HIDDEN) { + o++; + continue; + } + + // Determine if this is a section heading + else if ( !o->name && !o->key && !o->arg && !o->flags && o->doc) + printf("\n%s:\n", o->doc); + + // Print normal option entry + else { + + // IF this option has a single character key, print the key, else print spaces + if (isalnum(o->key)) + printf(" -%c, ", o->key); + else + printf(" "); + len = 6; + + // If this option has a long name, print the long name + if (o->name) { + printf("--%s", o->name); + len += strlen(o->name) + 2; + } + + // If this option has an arg type, print the type + if (o->arg) { + printf("=%s", o->arg); + len += strlen(o->arg) + 1; + } + + // Print remaining spaces up to description column + for ( int i = 0 ; i < CLMR_HELP_COLUMN - len ; i++ ) + printf(" "); + + // Print description of this option + printf("%s\n", o->doc); + } + o++; + } +} + +/** + * Debug function to print out the options array at the end of parsing + */ +static void print_options_array(struct opt *o) +{ + int i, len, maxlen; + + maxlen = 0; + + // Find max length of CLOP String + for (i = 0 ; i < CLOP_MAX ; i++) { + len = strlen(clop(i)); + if (len > maxlen) + maxlen = len; + } + + // Print Header + printf("##"); // index + printf(" Name"); // OP Name + for (int k = 5 ; k <= maxlen ; k++) // Spaces + printf(" "); + printf(" S"); // Set + printf(" u8"); // u8 + printf(" u16"); // u16 + printf(" u32"); // u32 + printf(" u64"); // u64 + printf(" val"); // val + printf(" num"); // num + printf(" len"); // len + printf(" str"); // str + printf("\n"); + + // Print each entry + for (i = 0 ; i < CLOP_MAX ; i++) { + // index + printf("%02d", i); + + // OP Name + printf(" %s", clop(i)); + + // Spaces + for (int k = strlen(clop(i)) ; k < maxlen ; k++) + printf(" "); + + printf(" %d", o[i].set); // Set + printf(" 0x%02x", o[i].u8); // u8 + printf(" 0x%04x", o[i].u16); // u16 + printf(" 0x%08x", o[i].u32); // u32 + printf(" 0x%016llx", o[i].u64); // u64 + printf(" 0x%04x", o[i].val); // val + printf(" 0x%016llx", o[i].num); // num + printf(" 0x%016llx", o[i].len); // len + + if (o[i].str) + printf(" %s", o[i].str); + + printf("\n"); + + if (o[i].len > 0) + prnt_buf(o[i].buf, o[i].len, 4, 0); + } +} + +/** + * Print the usage information for a option level + * + * @param option Menu item from enum [CLAP] + * @param o struct argp_option* to the string data to pull from + * STEPS: + * 1: Initialize variables + * 2: Generate header text + * 3: Count the number of short options with no argument + * 4: If there is at least one short option with no arg, append short options with no argument here + * 5: Append short options with arguments + * 6: Append long options + * 7: Find index of last space before character 80 + * 8: Loop through usage buffer and break it up into smaller chunks +*/ +static void print_usage(int option, struct argp_option *o) +{ + int hdr_len, buf_len, num, i, index; + char buf[4096]; + char str[4096]; + char *ptr; + struct argp_option *original; + + // STEP 1: Initialize variables + num = 0; + index = 0; + hdr_len = 0; + buf_len = 1; + memset(buf, 0, 4096); + memset(str, 0, 4096); + original = o; + ptr = buf; + + // STEP 2: Generate header text + switch(option) + { + case CLAP_MAIN: sprintf(str, "Usage: %s ", app_name); break; + case CLAP_SET: sprintf(str, "Usage: %s set ", app_name); break; + case CLAP_SHOW: sprintf(str, "Usage: %s show ", app_name); break; + case CLAP_BLOCK: sprintf(str, "Usage: %s block ", app_name); break; + default: break; + } + hdr_len = strlen(str); + + // STEP 3: Count the number of short options with no argument + while ( !( !o->name && !o->key && !o->arg && !o->flags && !o->doc && !o->group ) ) + { + if (isalnum(o->key) && !o->arg) + num++; + o++; + } + + // Reset pointer + o = original; + + // STEP 4: If there is at least one short option with no arg, append short options with no argument here + if ( num > 0 ) + { + // Add Leader [- + sprintf(&buf[buf_len], "[-"); + buf_len += 2; + + // Add each key character + while ( !( !o->name && !o->key && !o->arg && !o->flags && !o->doc && !o->group ) ) + { + // If this option has a single character key, print the key, else print spaces + if (isalnum(o->key) && !o->arg) { + sprintf(&buf[buf_len], "%c", o->key); + buf_len += 1; + } + o++; + } + + // Add trailing ] + sprintf(&buf[buf_len], "] "); + buf_len += 2; + } + + // Reset pointer + o = original; + + // STEP 5: Append short options with arguments + while ( !( !o->name && !o->key && !o->arg && !o->flags && !o->doc && !o->group ) ) + { + // If this option has a single character key and an arg + if (isalnum(o->key) && o->arg) { + sprintf(&buf[buf_len], "[-%c=%s] ", o->key, o->arg); + buf_len += 6; + buf_len += strlen(o->arg); + } + o++; + } + + // Reset pointer + o = original; + + // STEP 6: Append long options + while ( !( !o->name && !o->key && !o->arg && !o->flags && !o->doc && !o->group ) ) + { + // If this option has a long name, print the long name + if (o->name) + { + sprintf(&buf[buf_len], "[--%s", o->name); + buf_len += strlen(o->name) + 3; + + // If this option has an arg type, print the type + if (o->arg) + { + sprintf(&buf[buf_len], "=%s", o->arg); + buf_len += strlen(o->arg) + 1; + } + + // Add trailing ] + sprintf(&buf[buf_len], "] "); + buf_len += 2; + } + o++; + } + + // STEP 7: Find index of last space before character 80 + index = 0; + for ( i = 1 ; i < (CLMR_MAX_HELP_WIDTH-hdr_len) ; i++ ) { + if (ptr[i] == ' ') + index = i; + if (ptr[i]==0) + break; + } + + // STEP 8: Loop through usage buffer and break it up into smaller chunks + while (index != 0) + { + // Copy the line of the buffer into str + memcpy(&str[hdr_len], &ptr[1], index-1); + + // Set the next char after the string to 0 + str[hdr_len+index-1] = 0; + + // Print the merged string + printf("%s\n", str); + + // Clear the header portion of the print str + memset(str, ' ', hdr_len); + + // Advance buffer + ptr = &ptr[index]; + + // Find index of last space before character 80 + index = 0; + for ( i = 1 ; i < (CLMR_MAX_HELP_WIDTH-hdr_len) ; i++ ) { + if (ptr[i] == ' ') + index = i; + if (ptr[i]==0) + break; + } + } +} + +/** + * Print the Help output + * + * @param option the level to print [CLAP] + * + */ +static void print_help(int option) +{ + printf("Memory Expression Management CLI Tool\n"); + + switch (option) + { + case CLAP_MAIN: + printf("%s", ho_main); + print_options(ao_main); + printf("\n"); + break; + + case CLAP_BLOCK: + printf("%s", ho_block); + print_options(ao_block); + printf("\n"); + break; + + case CLAP_LIST: + printf("%s", ho_list); + print_options(ao_list); + printf("\n"); + break; + + case CLAP_REGION: + printf("%s", ho_region); + print_options(ao_region); + printf("\n"); + break; + + case CLAP_SET: + printf("%s", ho_set); + print_options(ao_set); + printf("\n"); + break; + + case CLAP_SHOW: + printf("%s", ho_show); + print_options(ao_show); + printf("\n"); + break; + + case CLAP_SHOW_BLOCK: + printf("%s", ho_show_block); + print_options(ao_show_block); + printf("\n"); + break; + + case CLAP_SHOW_CAPACITY: + printf("%s", ho_show_capacity); + print_options(ao_show_capacity); + printf("\n"); + break; + + case CLAP_SHOW_DEVICE: + printf("%s", ho_show_device); + print_options(ao_show_device); + printf("\n"); + break; + + case CLAP_SHOW_NUM: + printf("%s", ho_show_num); + print_options(ao_show_num); + printf("\n"); + break; + + case CLAP_SHOW_REGION: + printf("%s", ho_show_region); + print_options(ao_show_region); + printf("\n"); + break; + + case CLAP_SHOW_SYSTEM: + printf("%s", ho_show_system); + print_options(ao_show_system); + printf("\n"); + break; + + default: + break; + } +} + +/** + * Common parse function + * + * This function implements the common flags to most parsers + * + * @return 0 success, non-zero to indicate a problem + * + * Global keys + * -T --tcp-address Server TCP Address + * -P --tcp-port Server TCP Port + * -V --verbosity Set Verbosity Flag + * -X --verbosity-hex Set all Verbosity Flags with hex value + * -A --all All of collection + * -h --help Display Help + * + * Standard key mapping + * -n --length Length + * -o --offset Memory Offset + * -w --write Perform a Write transaction + * --data Write Data (up to 4 bytes) + * --infile Filename for input data + * --outfile Filename for output data + * + * Non char key mapping + * 701 - usage + * 702 - version + * 703 - data + * 704 - infile + * 705 - outfile + * 706 - print-options + * + * Special Keys: + * ARGP_KEY_INIT Called first. Initialize any data structures here + * ARGP_KEY_ARG Called for non option parameter (no flag) + * ARGP_KEY_NO_ARGS Only called if there are no args at all + * ARGP_KEY_SUCCESS Called after all args completed successfuly + * ARGP_KEY_END Last call. Verify parameters. Fill in missing parameters + * ARGP_KEY_ERROR Called after an error + * ARGP_KEY_FINI Absolutely last call to this parse functon + */ +static int pr_common(int key, char *arg, struct argp_state *state, int type, struct argp_option *ao) +{ + struct opt *o, *opts = (struct opt*) state->input; + int rv = 0; + + switch (key) + { + // all + case 'a': + o = &opts[CLOP_ALL]; + o->set = 1; + break; + + // block + case 'b': + o = &opts[CLOP_BLOCK]; + o->set = 1; + o->val = strtoul(arg, NULL, 0); + break; + + // device + case 'd': + o = &opts[CLOP_DEVICE]; + o->set = 1; + o->str = strdup(arg); + rv = sscanf(arg, "mem%d", &o->u32); + if (rv != 1) + { + fprintf(stderr, "Error: Could not parse mem device: %s\n", arg); + exit(1); + } + rv = 0; + break; + + // granularity + case 'g': + o = &opts[CLOP_GRANULARITY]; + o->set = 1; + o->u32 = strtoul(arg, NULL, 0); + break; + + // help + case 'h': + print_help(type); + exit(0); + break; + + // human + case 'H': + o = &opts[CLOP_HUMAN]; + o->set = 1; + break; + + // kernel + case 'k': + o = &opts[CLOP_KERNEL]; + o->set = 1; + break; + + // movable + case 'm': + o = &opts[CLOP_MOVABLE]; + o->set = 1; + break; + + // num + case 'n': + o = &opts[CLOP_NUM]; + o->set = 1; + break; + + // offline + case 'o': + o = &opts[CLOP_OFFLINE]; + o->set = 1; + break; + + // online + case 'O': + o = &opts[CLOP_ONLINE]; + o->set = 1; + break; + + // region + case 'r': + o = &opts[CLOP_REGION]; + o->set = 1; + o->str = strdup(arg); + rv = sscanf(arg, "region%d", &o->u32); + if (rv != 1) + { + fprintf(stderr, "Error: Could not parse region: %s\n", arg); + rv = 1; + exit(1); + } + rv = 0; + break; + + // verbosity + case 'v': + o = &opts[CLOP_VERBOSITY]; + o->set = 1; + o->u32 += 1; + break; + + // usage + case 701: + print_usage(type, ao); + exit(0); + break; + + // version + case 702: + printf("%s\n", argp_program_version); + exit(0); + break; + + // print-options + case 706: + o = &opts[CLOP_PRNT_OPTS]; + o->set = 1; + break; + + // Last call. Verify parameters. Fill in missing values + case ARGP_KEY_END: + break; + } + return rv; +} + +/** + * Parse function for: mem + * + * @return 0 success, non-zero to indicate a problem + */ +static int pr_main(int key, char *arg, struct argp_state *state) +{ + struct opt *o, *opts = (struct opt*) state->input; + int rv = pr_common(key, arg, state, CLAP_MAIN, ao_main); + + switch (key) + { + case ARGP_KEY_ARG: + if (!strcmp(arg, "block") || !strcmp(arg, "blk") ) + rv = argp_parse(&ap_block, state->argc-state->next+1, &state->argv[state->next-1], ARGP_IN_ORDER | ARGP_NO_HELP, 0, opts); + + else if (!strcmp(arg, "info")) + { + o = &opts[CLOP_CMD]; + o->set = 1; + o->val = CLCM_INFO; + } + + else if (!strcmp(arg, "list")) + rv = argp_parse(&ap_list, state->argc-state->next+1, &state->argv[state->next-1], ARGP_IN_ORDER | ARGP_NO_HELP, 0, opts); + + else if (!strcmp(arg, "region") || !strcmp(arg, "reg") ) + rv = argp_parse(&ap_region, state->argc-state->next+1, &state->argv[state->next-1], ARGP_IN_ORDER | ARGP_NO_HELP, 0, opts); + + else if (!strcmp(arg, "set")) + rv = argp_parse(&ap_set, state->argc-state->next+1, &state->argv[state->next-1], ARGP_IN_ORDER | ARGP_NO_HELP, 0, opts); + + else if (!strcmp(arg, "show")) + rv = argp_parse(&ap_show, state->argc-state->next+1, &state->argv[state->next-1], ARGP_IN_ORDER | ARGP_NO_HELP, 0, opts); + + else + argp_error (state, "Invalid subcommand"); + + // Stop current parser + state->next = state->argc; + break; + + case ARGP_KEY_END: + + // Print options array if requested + if (opts[CLOP_PRNT_OPTS].set) + print_options_array(opts); + + // Print help if a command has not been set + if (!opts[CLOP_CMD].set) + { + print_help(CLAP_MAIN); + exit(0); + } + break; + } + return rv; +} + +/** + * Parse function for: mem block + * + * @return 0 success, non-zero to indicate a problem + */ +static int pr_block(int key, char *arg, struct argp_state *state) +{ + struct opt *o, *opts = (struct opt*) state->input; + int index, index1, index2, rv = pr_common(key, arg, state, CLAP_BLOCK, ao_block); + + switch (key) + { + case ARGP_KEY_ARG: + if (!strcmp(arg, "online") || !strcmp(arg, "on") ) + opts[CLOP_ONLINE].set = 1; + + else if (!strcmp(arg, "offline") || !strcmp(arg, "off" ) ) + opts[CLOP_OFFLINE].set = 1; + + else if (!strcmp(arg, "kernel") || !strcmp(arg, "normal") ) + opts[CLOP_KERNEL].set = 1; + + else if (!strcmp(arg, "movable") || !strcmp(arg, "move") ) + opts[CLOP_KERNEL].set = 1; + + else if (!strcmp(arg, "all") ) + opts[CLOP_ALL].set = 1; + + else if (sscanf(arg, "region%d", &index) == 1) + { + opts[CLOP_REGION].set = 1; + opts[CLOP_REGION].str = strdup(arg); + } + else + { + char *dash = strstr(arg, "-"); + + if (dash == NULL && sscanf(arg, "%d ", &index1) == 1) + { + o = &opts[CLOP_BLOCK]; + o->set = 1; + o->val = index1; + } + else if (dash != NULL && sscanf(arg, "%d-%d", &index1, &index2) == 2) + { + o = &opts[CLOP_BLOCK]; + o->set = 1; + o->num = (index2-index1)+1; + o->buf = calloc(o->num, sizeof(int)); + for (int i = 0 ; i < (int) o->num ; i++) + ((int*)o->buf)[i] = index1+i; + } + else + argp_error (state, "Invalid subcommand"); + } + + break; + + case ARGP_KEY_END: + + if (opts[CLOP_REGION].set) + { + opts[CLOP_CMD].set = 1; + opts[CLOP_CMD].val = CLCM_SET_REGION_BLOCK_STATE; + } + else if (opts[CLOP_OFFLINE].set) + { + opts[CLOP_CMD].set = 1; + opts[CLOP_CMD].val = CLCM_BLOCK_OFFLINE; + } + else if (opts[CLOP_MOVABLE].set) + { + opts[CLOP_CMD].set = 1; + opts[CLOP_CMD].val = CLCM_SET_BLOCK_STATE; + opts[CLOP_ZONE].set = 1; + opts[CLOP_ZONE].val = CLZN_MOVABLE; + } + else if (opts[CLOP_KERNEL].set) + { + opts[CLOP_CMD].set = 1; + opts[CLOP_CMD].val = CLCM_SET_BLOCK_STATE; + opts[CLOP_ZONE].set = 1; + opts[CLOP_ZONE].val = CLZN_NORMAL; + } + else if (opts[CLOP_ONLINE].set) + { + opts[CLOP_CMD].set = 1; + opts[CLOP_CMD].val = CLCM_BLOCK_ONLINE; + } + + if ( (opts[CLOP_ONLINE].set + + opts[CLOP_OFFLINE].set + + opts[CLOP_KERNEL].set + + opts[CLOP_MOVABLE].set) > 1) + { + fprintf(stderr, "Error: Multiple states specified. Specify only one desired block state\n"); + exit(0); + } + + // Print options array if requested + if (opts[CLOP_PRNT_OPTS].set) + { + print_options_array(opts); + opts[CLOP_PRNT_OPTS].set = 0; + } + + // If the block identifier is not set, print help and exit + if (opts[CLOP_BLOCK].set == 0 && opts[CLOP_ALL].set == 0) + { + fprintf(stderr, "Error: Missing block id(s)\n"); + print_help(CLAP_BLOCK); + exit(0); + } + + // Print help if a command has not been set + if (!opts[CLOP_CMD].set) + { + fprintf(stderr, "Error: Missing command\n"); + print_help(CLAP_BLOCK); + exit(0); + } + + break; + } + return rv; +} + +/** + * Parse function for: mem list + * + * @return 0 success, non-zero to indicate a problem + */ +static int pr_list(int key, char *arg, struct argp_state *state) +{ + struct opt *o, *opts = (struct opt*) state->input; + int index, index1, index2, rv = pr_common(key, arg, state, CLAP_LIST, ao_list); + + opts[CLOP_CMD].set = 1; + opts[CLOP_CMD].val = CLCM_LIST; + + switch (key) + { + case ARGP_KEY_ARG: + if (!strcmp(arg, "online") || !strcmp(arg, "on") ) + opts[CLOP_ONLINE].set = 1; + + else if (!strcmp(arg, "offline") || !strcmp(arg, "off" ) ) + opts[CLOP_OFFLINE].set = 1; + + else if (!strcmp(arg, "kernel") || !strcmp(arg, "normal") ) + opts[CLOP_KERNEL].set = 1; + + else if (!strcmp(arg, "movable") || !strcmp(arg, "move") ) + opts[CLOP_KERNEL].set = 1; + + else if (sscanf(arg, "region%d", &index) == 1) + { + opts[CLOP_REGION].set = 1; + opts[CLOP_REGION].str = strdup(arg); + } + else + { + char *dash = strstr(arg, "-"); + + if (dash == NULL && sscanf(arg, "%d ", &index1) == 1) + { + o = &opts[CLOP_BLOCK]; + o->set = 1; + o->val = index1; + } + else if (dash != NULL && sscanf(arg, "%d-%d", &index1, &index2) == 2) + { + o = &opts[CLOP_BLOCK]; + o->set = 1; + o->num = (index2-index1)+1; + o->buf = calloc(o->num, sizeof(int)); + for (int i = 0 ; i < (int) o->num ; i++) + ((int*)o->buf)[i] = index1+i; + } + else + argp_error (state, "Invalid subcommand"); + } + + break; + + case ARGP_KEY_END: + + if ( (opts[CLOP_ONLINE].set + + opts[CLOP_OFFLINE].set + + opts[CLOP_KERNEL].set + + opts[CLOP_MOVABLE].set) > 1) + { + fprintf(stderr, "Error: Multiple online states specified. Specify only one desired block state\n"); + exit(0); + } + + // Print options array if requested + if (opts[CLOP_PRNT_OPTS].set) + { + print_options_array(opts); + opts[CLOP_PRNT_OPTS].set = 0; + } + + break; + } + return rv; +} + +/** + * Parse function for: mem region + * + * @return 0 success, non-zero to indicate a problem + */ +static int pr_region(int key, char *arg, struct argp_state *state) +{ + struct opt *opts = (struct opt*) state->input; + int index, rv = pr_common(key, arg, state, CLAP_REGION, ao_region); + + switch (key) + { + case ARGP_KEY_ARG: + if (!strcmp(arg, "create")) + { + opts[CLOP_CMD].set = 1; + opts[CLOP_CMD].val = CLCM_REGION_CREATE; + } + else if (!strcmp(arg, "daxmode") || !strcmp(arg, "dax") ) + { + opts[CLOP_CMD].set = 1; + opts[CLOP_CMD].val = CLCM_REGION_DAXMODE; + } + else if (!strcmp(arg, "delete") || !strcmp(arg, "del") ) + { + opts[CLOP_CMD].set = 1; + opts[CLOP_CMD].val = CLCM_REGION_DELETE; + } + else if (!strcmp(arg, "disable") ) + { + opts[CLOP_CMD].set = 1; + opts[CLOP_CMD].val = CLCM_REGION_DISABLE; + } + else if (!strcmp(arg, "enable") ) + { + opts[CLOP_CMD].set = 1; + opts[CLOP_CMD].val = CLCM_REGION_ENABLE; + } + else if (!strcmp(arg, "rammode") || !strcmp(arg, "ram") ) + { + opts[CLOP_CMD].set = 1; + opts[CLOP_CMD].val = CLCM_REGION_RAMMODE; + } + else if (!strcmp(arg, "all") ) + opts[CLOP_ALL].set = 1; + + else if (sscanf(arg, "region%d", &index) == 1) + { + opts[CLOP_REGION].set = 1; + opts[CLOP_REGION].str = strdup(arg); + } + else if (sscanf(arg, "mem%d", &index) == 1) + { + if (opts[CLOP_CMD].val != CLCM_REGION_CREATE) + argp_error (state, "Invalid subcommand"); + + opts[CLOP_DEVICE].set = 1; + opts[CLOP_DEVICE].num++; + opts[CLOP_DEVICE].buf = realloc(opts[CLOP_DEVICE].buf, sizeof(char *) * opts[CLOP_DEVICE].num); + ((char**) opts[CLOP_DEVICE].buf)[opts[CLOP_DEVICE].num-1] = strdup(arg); + } + else + argp_error (state, "Invalid subcommand"); + + break; + + case ARGP_KEY_END: + + if (opts[CLOP_CMD].val == CLCM_REGION_CREATE + && !opts[CLOP_ALL].set + && !opts[CLOP_DEVICE].set) + { + opts[CLOP_ALL].set = 1; + } + + if (opts[CLOP_CMD].val == CLCM_REGION_DELETE + && !opts[CLOP_ALL].set + && !opts[CLOP_REGION].set) + { + fprintf(stderr, "Error: Missing region name or all\n"); + print_help(CLAP_REGION); + exit(1); + } + + if (opts[CLOP_CMD].val == CLCM_REGION_DISABLE + && !opts[CLOP_ALL].set + && !opts[CLOP_REGION].set) + { + fprintf(stderr, "Error: Missing region name or all\n"); + print_help(CLAP_REGION); + exit(1); + } + + if (opts[CLOP_CMD].val == CLCM_REGION_ENABLE + && !opts[CLOP_ALL].set + && !opts[CLOP_REGION].set) + { + opts[CLOP_ALL].set = 1; + } + + if (opts[CLOP_CMD].val == CLCM_REGION_DAXMODE + && !opts[CLOP_ALL].set + && !opts[CLOP_REGION].set) + { + fprintf(stderr, "Error: Missing region name or all\n"); + print_help(CLAP_REGION); + exit(1); + } + + if (opts[CLOP_CMD].val == CLCM_REGION_RAMMODE + && !opts[CLOP_ALL].set + && !opts[CLOP_REGION].set) + { + fprintf(stderr, "Error: Missing region name or all\n"); + print_help(CLAP_REGION); + exit(1); + } + + // Print options array if requested + if (opts[CLOP_PRNT_OPTS].set) + { + print_options_array(opts); + opts[CLOP_PRNT_OPTS].set = 0; + } + + // Print help if a command has not been set + if (!opts[CLOP_CMD].set) + { + print_help(CLAP_REGION); + exit(1); + } + + break; + } + return rv; +} + +/** + * Parse function for: mem set + * + * @return 0 success, non-zero to indicate a problem + */ +static int pr_set(int key, char *arg, struct argp_state *state) +{ + struct opt *opts = (struct opt*) state->input; + int rv = pr_common(key, arg, state, CLAP_SET, ao_set); + + switch (key) + { + case ARGP_KEY_ARG: + if (!strcmp(arg, "policy") ) + { + opts[CLOP_CMD].set = 1; + opts[CLOP_CMD].val = CLCM_SET_SYSTEM_POLICY; + } + else if (!strcmp(arg, "online") || !strcmp(arg, "on") ) + { + opts[CLOP_ONLINE].set = 1; + } + else if (!strcmp(arg, "offline") || !strcmp(arg, "off") ) + { + opts[CLOP_OFFLINE].set = 1; + } + else if (!strcmp(arg, "kernel") ) + { + opts[CLOP_KERNEL].set = 1; + } + else if (!strcmp(arg, "movable") || !strcmp(arg, "move") ) + { + opts[CLOP_MOVABLE].set = 1; + } + else + argp_error (state, "Invalid subcommand"); + + break; + + case ARGP_KEY_END: + + if (opts[CLOP_CMD].val == CLCM_SET_SYSTEM_POLICY + && !opts[CLOP_ONLINE].set + && !opts[CLOP_OFFLINE].set + && !opts[CLOP_KERNEL].set + && !opts[CLOP_MOVABLE].set) + { + fprintf(stderr, "Error: Missing policy\n"); + exit(0); + } + + if ( (opts[CLOP_ONLINE].set + + opts[CLOP_OFFLINE].set + + opts[CLOP_KERNEL].set + + opts[CLOP_MOVABLE].set) > 1) + { + fprintf(stderr, "Error: Multiple policies specified. Specify only one policy\n"); + exit(0); + } + + if (opts[CLOP_PRNT_OPTS].set) + { + print_options_array(opts); + opts[CLOP_PRNT_OPTS].set = 0; + } + + if (!opts[CLOP_CMD].set) + { + fprintf(stderr, "Error: Missing subcommand\n"); + print_help(CLAP_SET); + exit(0); + } + break; + } + return rv; +} + +/** + * Parse function for: mem show + * + * @return 0 success, non-zero to indicate a problem + */ +static int pr_show(int key, char *arg, struct argp_state *state) +{ + struct opt *opts = (struct opt*) state->input; + int rv = pr_common(key, arg, state, CLAP_SHOW, ao_show); + + switch (key) + { + case ARGP_KEY_ARG: + + if (!strcmp(arg, "block") || !strcmp(arg, "blk") || !strcmp(arg, "blocks") ) + rv = argp_parse(&ap_show_block, state->argc-state->next+1, &state->argv[state->next-1], ARGP_IN_ORDER | ARGP_NO_HELP, 0, opts); + + else if (!strcmp(arg, "capacity") || !strcmp(arg, "cap") || !strcmp(arg, "size") ) + rv = argp_parse(&ap_show_capacity, state->argc-state->next+1, &state->argv[state->next-1], ARGP_IN_ORDER | ARGP_NO_HELP, 0, opts); + + else if (!strcmp(arg, "device") || !strcmp(arg, "dev") || !strcmp(arg, "devices") ) + rv = argp_parse(&ap_show_device, state->argc-state->next+1, &state->argv[state->next-1], ARGP_IN_ORDER | ARGP_NO_HELP, 0, opts); + + else if (!strcmp(arg, "num") ) + rv = argp_parse(&ap_show_num, state->argc-state->next+1, &state->argv[state->next-1], ARGP_IN_ORDER | ARGP_NO_HELP, 0, opts); + + else if (!strcmp(arg, "region") || !strcmp(arg, "rgn") || !strcmp(arg, "regions") ) + rv = argp_parse(&ap_show_region, state->argc-state->next+1, &state->argv[state->next-1], ARGP_IN_ORDER | ARGP_NO_HELP, 0, opts); + + else if (!strcmp(arg, "system") || !strcmp(arg, "sys") ) + rv = argp_parse(&ap_show_system, state->argc-state->next+1, &state->argv[state->next-1], ARGP_IN_ORDER | ARGP_NO_HELP, 0, opts); + + else + argp_error (state, "Invalid subcommand"); + + // Stop current parser + state->next = state->argc; + break; + + case ARGP_KEY_END: + + if (opts[CLOP_PRNT_OPTS].set) + { + print_options_array(opts); + opts[CLOP_PRNT_OPTS].set = 0; + } + + // Fail if no command is set + if (!opts[CLOP_CMD].set) + { + fprintf(stderr, "Error: Missing command\n"); + print_help(CLAP_SHOW); + exit(0); + } + + break; + } + return rv; +} + +/** + * Parse function for: mem show block + * + * @return 0 success, non-zero to indicate a problem + */ +static int pr_show_block(int key, char *arg, struct argp_state *state) +{ + struct opt *opts = (struct opt*) state->input; + int index, val, rv = pr_common(key, arg, state, CLAP_SHOW_BLOCK, ao_show_block); + + switch (key) + { + case ARGP_KEY_ARG: + + if (!strcmp(arg, "isonline") ) + { + opts[CLOP_CMD].set = 1; + opts[CLOP_CMD].val = CLCM_SHOW_BLK_ISONLINE; + } + else if (!strcmp(arg, "isremovable") || !strcmp(arg, "removable") ) + { + opts[CLOP_CMD].set = 1; + opts[CLOP_CMD].val = CLCM_SHOW_BLK_ISREMOVABLE; + } + else if (!strcmp(arg, "node") ) + { + opts[CLOP_CMD].set = 1; + opts[CLOP_CMD].val = CLCM_SHOW_BLK_NODE; + } + else if (!strcmp(arg, "device") || !strcmp(arg, "physdevice") ) + { + opts[CLOP_CMD].set = 1; + opts[CLOP_CMD].val = CLCM_SHOW_BLK_PHYSDEVICE; + } + else if (!strcmp(arg, "state") ) + { + opts[CLOP_CMD].set = 1; + opts[CLOP_CMD].val = CLCM_SHOW_BLK_STATE; + } + else if (!strcmp(arg, "zones") || !strcmp(arg,"validzones") ) + { + opts[CLOP_CMD].set = 1; + opts[CLOP_CMD].val = CLCM_SHOW_BLK_ZONES; + } + else if (!strcmp(arg, "online") || !strcmp(arg, "on") ) + { + opts[CLOP_ONLINE].set = 1; + } + else if (!strcmp(arg, "offline") || !strcmp(arg, "off") ) + { + opts[CLOP_OFFLINE].set = 1; + } + else if (sscanf(arg, "region%d", &index) == 1) + { + opts[CLOP_REGION].set = 1; + opts[CLOP_REGION].str = strdup(arg); + } + else if (sscanf(arg, "%d", &index) == 1) + { + opts[CLOP_BLOCK].set = 1; + opts[CLOP_BLOCK].val = index; + } + else + argp_error (state, "Invalid subcommand"); + + break; + + case ARGP_KEY_END: + + val = opts[CLOP_CMD].val; + if ( ( val == CLCM_SHOW_BLK_ISONLINE + || val == CLCM_SHOW_BLK_ISREMOVABLE + || val == CLCM_SHOW_BLK_NODE + || val == CLCM_SHOW_BLK_PHYSDEVICE + || val == CLCM_SHOW_BLK_STATE + || val == CLCM_SHOW_BLK_ZONES ) + && !opts[CLOP_BLOCK].set) + { + fprintf(stderr, "Error: You must supply a block id for individual block state commands\n"); + exit(1); + } + + if (!opts[CLOP_CMD].set) + { + opts[CLOP_CMD].set = 1; + opts[CLOP_CMD].val = CLCM_SHOW_BLOCKS; + } + + if (opts[CLOP_PRNT_OPTS].set) + { + print_options_array(opts); + opts[CLOP_PRNT_OPTS].set = 0; + } + + // Fail if no command is set + if (!opts[CLOP_CMD].set) + { + print_help(CLAP_SHOW_BLOCK); + exit(0); + } + + break; + } + return rv; +} + +/** + * Parse function for: mem show capacity + * + * @return 0 success, non-zero to indicate a problem + */ +static int pr_show_capacity(int key, char *arg, struct argp_state *state) +{ + struct opt *opts = (struct opt*) state->input; + int index, rv = pr_common(key, arg, state, CLAP_SHOW_CAPACITY, ao_show_capacity); + + opts[CLOP_CMD].set = 1; + opts[CLOP_CMD].val = CLCM_SHOW_CAPACITY; + + switch (key) + { + case ARGP_KEY_ARG: + + if (sscanf(arg, "region%d", &index) ) + { + opts[CLOP_REGION].set = 1; + opts[CLOP_REGION].str = strdup(arg); + } + else if (!strcmp(arg, "online") || !strcmp(arg, "on") ) + { + opts[CLOP_ONLINE].set = 1; + } + else if (!strcmp(arg, "offline") || !strcmp(arg, "off") ) + { + opts[CLOP_OFFLINE].set = 1; + } + else + argp_error (state, "Invalid subcommand"); + + break; + + case ARGP_KEY_END: + + if ( ( opts[CLOP_ONLINE].set + + opts[CLOP_OFFLINE].set) > 1) + { + fprintf(stderr, "Error: Contradictory filter states specified. Please only specify online or offline\n"); + exit(1); + } + + if (opts[CLOP_PRNT_OPTS].set) + { + print_options_array(opts); + opts[CLOP_PRNT_OPTS].set = 0; + } + + // Fail if no command is set + if (!opts[CLOP_CMD].set) + { + print_help(CLAP_SHOW_CAPACITY); + exit(0); + } + + break; + } + return rv; +} + +/** + * Parse function for: mem show device + * + * @return 0 success, non-zero to indicate a problem + */ +static int pr_show_device(int key, char *arg, struct argp_state *state) +{ + struct opt *opts = (struct opt*) state->input; + int index, rv = pr_common(key, arg, state, CLAP_SHOW_DEVICE, ao_show_device); + + switch (key) + { + case ARGP_KEY_ARG: + + if (sscanf(arg, "mem%d", &index) == 1) + { + opts[CLOP_DEVICE].set = 1; + opts[CLOP_DEVICE].str = strdup(arg); + } + else if (sscanf(arg, "region%d", &index) == 1) + { + opts[CLOP_REGION].set = 1; + opts[CLOP_REGION].str = strdup(arg); + } + else if (!strcmp(arg, "isavailable") || !strcmp(arg, "available") || !strcmp(arg, "avail") ) + { + opts[CLOP_CMD].set = 1; + opts[CLOP_CMD].val = CLCM_SHOW_DEVICE_ISAVAILABLE; + } + else if (!strcmp(arg, "ig") || !strcmp(arg, "granularity") || !strcmp(arg, "interleave") ) + { + opts[CLOP_CMD].set = 1; + opts[CLOP_CMD].val = CLCM_SHOW_DEVICE_INTERLEAVE_GRANULARITY; + } + else + argp_error (state, "Invalid subcommand"); + + break; + + + case ARGP_KEY_END: + + if (opts[CLOP_CMD].val == CLCM_SHOW_DEVICE_ISAVAILABLE + && !opts[CLOP_DEVICE].set) + { + fprintf(stderr, "Error: You must specify a device when using subcommand: isavailable\n");; + exit(1); + } + + if (opts[CLOP_CMD].val == CLCM_SHOW_DEVICE_INTERLEAVE_GRANULARITY + && !opts[CLOP_DEVICE].set) + { + fprintf(stderr, "Error: You must specify a device when using subcommand: ig\n");; + exit(1); + } + + if (opts[CLOP_DEVICE].set + && !opts[CLOP_CMD].set) + { + opts[CLOP_CMD].set = 1; + opts[CLOP_CMD].val = CLCM_SHOW_DEVICES; + } + + if (!opts[CLOP_CMD].set) + { + opts[CLOP_CMD].set = 1; + opts[CLOP_CMD].val = CLCM_SHOW_DEVICES; + } + + if (opts[CLOP_PRNT_OPTS].set) + { + print_options_array(opts); + opts[CLOP_PRNT_OPTS].set = 0; + } + + // Fail if no command is set + if (!opts[CLOP_CMD].set) + { + print_help(CLAP_SHOW_DEVICE); + exit(0); + } + + break; + } + return rv; +} + +/** + * Parse function for: mem show num + * + * @return 0 success, non-zero to indicate a problem + */ +static int pr_show_num(int key, char *arg, struct argp_state *state) +{ + struct opt *opts = (struct opt*) state->input; + int index, rv = pr_common(key, arg, state, CLAP_SHOW_NUM, ao_show_num); + + opts[CLOP_NUM].set = 1; + + switch (key) + { + case ARGP_KEY_ARG: + + if (!strcmp(arg, "blocks") || !strcmp(arg, "blk") || !strcmp(arg, "block") ) + opts[CLOP_BLOCKS].set = 1; + + else if (!strcmp(arg, "devices") || !strcmp(arg, "dev") || !strcmp(arg, "device") ) + opts[CLOP_DEVICES].set = 1; + + else if (!strcmp(arg, "regions") || !strcmp(arg, "reg") ) + opts[CLOP_REGIONS].set = 1; + + else if (!strcmp(arg, "online") || !strcmp(arg, "on") ) + opts[CLOP_ONLINE].set = 1; + + else if (!strcmp(arg, "offline") || !strcmp(arg, "off") ) + opts[CLOP_OFFLINE].set = 1; + + else if (sscanf(arg, "region%d", &index) == 1) + { + opts[CLOP_REGION].set = 1; + opts[CLOP_REGION].str = strdup(arg); + } + else + argp_error (state, "Invalid subcommand"); + + break; + + case ARGP_KEY_END: + + if ( ( opts[CLOP_BLOCKS].set + + opts[CLOP_DEVICES].set + + opts[CLOP_REGIONS].set) > 1) + { + fprintf(stderr, "Error: Specify only one type of object\n"); + exit(1); + } + + // Default to show num blocks if no object is specified + if ( ( opts[CLOP_BLOCKS].set + + opts[CLOP_DEVICES].set + + opts[CLOP_REGIONS].set) == 0) + { + opts[CLOP_CMD].set = 1; + opts[CLOP_CMD].val = CLCM_SHOW_NUM_BLOCKS; + } + else if (opts[CLOP_BLOCKS].set) + { + opts[CLOP_CMD].set = 1; + opts[CLOP_CMD].val = CLCM_SHOW_NUM_BLOCKS; + } + else if (opts[CLOP_DEVICES].set) + { + opts[CLOP_CMD].set = 1; + opts[CLOP_CMD].val = CLCM_SHOW_NUM_DEVICES; + } + else if (opts[CLOP_REGIONS].set) + { + opts[CLOP_CMD].set = 1; + opts[CLOP_CMD].val = CLCM_SHOW_NUM_REGIONS; + } + + if (opts[CLOP_PRNT_OPTS].set) + { + print_options_array(opts); + opts[CLOP_PRNT_OPTS].set = 0; + } + + // Fail if no command is set + if (!opts[CLOP_CMD].set) + { + print_help(CLAP_SHOW_NUM); + exit(0); + } + + break; + } + return rv; +} + +/** + * Parse function for: mem show region + * + * @return 0 success, non-zero to indicate a problem + */ +static int pr_show_region(int key, char *arg, struct argp_state *state) +{ + struct opt *opts = (struct opt*) state->input; + int index, rv = pr_common(key, arg, state, CLAP_SHOW_REGION, ao_show_region); + + opts[CLOP_CMD].set = 1; + opts[CLOP_CMD].val = CLCM_SHOW_REGIONS; + + switch (key) + { + case ARGP_KEY_ARG: + + if (sscanf(arg, "region%d", &index) == 1) + { + opts[CLOP_REGION].set = 1; + opts[CLOP_REGION].str = strdup(arg); + } + else + argp_error (state, "Invalid subcommand"); + + break; + + case ARGP_KEY_END: + + if (opts[CLOP_PRNT_OPTS].set) + { + print_options_array(opts); + opts[CLOP_PRNT_OPTS].set = 0; + } + + break; + } + return rv; +} + +/** + * Parse function for: mem show system + * + * @return 0 success, non-zero to indicate a problem + */ +static int pr_show_system(int key, char *arg, struct argp_state *state) +{ + struct opt *opts = (struct opt*) state->input; + int rv = pr_common(key, arg, state, CLAP_SHOW_SYSTEM, ao_show_system); + + switch (key) + { + case ARGP_KEY_ARG: + + if (!strcmp(arg, "blocksize") || !strcmp(arg, "size") || !strcmp(arg, "bs") ) + { + opts[CLOP_CMD].set = 1; + opts[CLOP_CMD].val = CLCM_SHOW_SYSTEM_BLOCKSIZE; + } + else if (!strcmp(arg, "policy") ) + { + opts[CLOP_CMD].set = 1; + opts[CLOP_CMD].val = CLCM_SHOW_SYSTEM_POLICY; + } + else + argp_error (state, "Invalid subcommand"); + + break; + + case ARGP_KEY_END: + + if (opts[CLOP_PRNT_OPTS].set) + { + print_options_array(opts); + opts[CLOP_PRNT_OPTS].set = 0; + } + + // Fail if no command is set + if (!opts[CLOP_CMD].set) + { + print_help(CLAP_SHOW_SYSTEM); + exit(0); + } + + break; + } + return rv; +} + +/** + * Obtain option defaults from environment if present + * + * @return 0 on Success, non zero otherwise + */ +int options_getenv() +{ + struct argp_state state; + int rv; + char *o; + struct envopt *e; + + // Initialize variables + rv = 1; + state.input = opts; + e = envopts; + + if (e == NULL) + goto end; + + // Loop through global envopts array + while (e->key != 0) + { + // Get value from shell environment + o = getenv(e->name); + + // If the returned value is not NULL parse using pr_common() + if (o != NULL) + pr_common(e->key, o, &state, 0, NULL); + + // Advance to the next entry in the global envopts array + e++; + } + + rv = 0; + +end: + + return rv; +} + +/** + * Parse CLI options + * + * @param argc Number of CLI parameters + * @param argv Array of string pointers to CLI parameters + * + * STEPS + * 1: Store app name in global variable + * 2: Allocate and clear memory for options array + * 3: Obtain Option defaults from shell environment + * 4: Parse options + */ +int options_parse(int argc, char *argv[]) +{ + int rv, len; + char default_name[] = "app"; + + rv = 1; + + // STEP 1: Store app name in global variable + app_name = NULL; + if (argc > 0) + { + len = strnlen(argv[0], CLMR_MAX_NAME_LEN); + if (len > 0) + { + app_name = malloc(len+1); + if (app_name == NULL) + goto end; + memset(app_name, 0, len+1); + + if (!strncmp("./", argv[0], 2)) + memcpy(app_name, &argv[0][2], len); + else + memcpy(app_name, argv[0], len); + } + } + else + app_name = default_name; + + // STEP 2: Allocate and clear memory for options array + opts = calloc(CLOP_MAX, sizeof(struct opt)); + if (opts == NULL) + goto end_name; + + // STEP 3: Obtain Option defaults from shell environment + options_getenv(); + + // STEP 4: Parse options + rv = argp_parse(&ap_main, argc, argv, ARGP_IN_ORDER | ARGP_NO_HELP, 0, opts); + if (rv != 0) + goto end_opt; + + return rv; + +end_opt: + + free(opts); + +end_name: + + if (app_name && app_name != default_name) + free(app_name); + +end: + + return rv; +} + diff --git a/options.h b/options.h new file mode 100644 index 0000000..76f6a05 --- /dev/null +++ b/options.h @@ -0,0 +1,247 @@ +/** + * @file options.h + * + * @brief Header file for CLI options + * + * @copyright Copyright (C) 2024 Jackrabbit Founders LLC. All rights reserved. + * + * @date Jul 2024 + * @author Barrett Edwards + * + * Macro / Enumeration Prefixes (CL) + * CLAP - CLI Options Parsers Enumeration (AP) + * CLCM - CLI Command Opcod (CM) + * CLMR - CLI Macros (MR) + * CLOP - CLI Option (CL) + * CLPC - Physical Port Control Opcodes (PC) + * CLPU - Port Unbind Mode Options (PU) + * + * Key mapping + * -o --offline Zone: offline + * -O --online online + * -a --all Perform operatiion on all objects + * -b --block Block id + * -d --device Memdev name + * -g --granularity Interleave Granularity + * -h --help Display Help + * -H --human Display numbers using K, M, G, T units + * -k --kernel Zone: kernel + * -m --movable Zone: Online movable + * -n --num Display the number of objects + * -r --region Region name + * -v --verbose Increase verbosity + * + * Non char key mapping + * 701 - usage + * 702 - version + * 703 - data + * 704 - infile + * 705 - outfile + * 706 - print-options + * + */ + +#ifndef _OPTIONS_H +#define _OPTIONS_H + +/* INCLUDES ==================================================================*/ + +/* __u8 + */ +#include + +/* MACROS ====================================================================*/ + +/** + * CLI Macros (MR) + * + * These are values that don't belong in an enumeration + */ +#define CLMR_HELP_COLUMN 30 +#define CLMR_MAX_HELP_WIDTH 100 +#define CLMR_MAX_NAME_LEN 64 + +/* ENUMERATIONS ==============================================================*/ + +/** + * Verbosity Options (VO) + */ +enum _CLVO +{ + CLVO_GENERAL = 0, + CLVO_MAX +}; + +/** + * Verbosity Bitfield Index (VB) + */ +enum _CLVB +{ + CLVB_GENERAL = (0x01 << 0), +}; + +/** + * CLI Options Parsers Enumeration (AP) + * + * This enumeration identifies each argp parser. It is used to print options + */ +enum _CLAP +{ + CLAP_MAIN = 0, + + CLAP_BLOCK , + CLAP_LIST , + CLAP_REGION , + CLAP_SET , + CLAP_SHOW , + + CLAP_SHOW_BLOCK , + CLAP_SHOW_CAPACITY , + CLAP_SHOW_DEVICE , + CLAP_SHOW_NUM , + CLAP_SHOW_REGION , + CLAP_SHOW_SYSTEM , + + CLAP_MAX +}; + +/** + * CLI Command Opcode (CM) + */ +enum _CLCM +{ + CLCM_NULL = 0, + + CLCM_INFO , + CLCM_LIST , + + CLCM_BLOCK_ONLINE , + CLCM_BLOCK_OFFLINE , + + CLCM_SET_BLOCK_STATE , + CLCM_SET_REGION_BLOCK_STATE , + CLCM_SET_SYSTEM_POLICY , + + CLCM_SHOW_REGIONS , + CLCM_SHOW_BLOCKS , + CLCM_SHOW_DEVICES , + + CLCM_SHOW_CAPACITY , + + CLCM_SHOW_NUM_BLOCKS , + CLCM_SHOW_NUM_DEVICES , + CLCM_SHOW_NUM_REGIONS , + + CLCM_SHOW_SYSTEM_BLOCKSIZE , + CLCM_SHOW_SYSTEM_POLICY , + + CLCM_SHOW_BLK_ISONLINE , + CLCM_SHOW_BLK_ISREMOVABLE , + CLCM_SHOW_BLK_NODE , + CLCM_SHOW_BLK_PHYSDEVICE , + CLCM_SHOW_BLK_STATE , + CLCM_SHOW_BLK_ZONES , + + CLCM_SHOW_REGION_ISENABLED , + CLCM_SHOW_DEVICE_ISAVAILABLE , + CLCM_SHOW_DEVICE_INTERLEAVE_GRANULARITY , + + CLCM_REGION_CREATE , + CLCM_REGION_DAXMODE , + CLCM_REGION_DELETE , + CLCM_REGION_DISABLE , + CLCM_REGION_ENABLE , + CLCM_REGION_RAMMODE , + + CLCM_MAX +}; + +/** + * CLI Option (OP) + */ +enum _CLOP +{ + /* General CLI Options */ + CLOP_VERBOSITY = 0, //!< Count + CLOP_PRNT_OPTS = 1, //!< Print Options Array when completed parsing + CLOP_INFILE = 2, //!< Filename for input + CLOP_OUTFILE = 3, //!< Filename for output + CLOP_NUM = 4, //!< Number of items + CLOP_ALL = 5, //!< Perform aciton on all of collection + CLOP_LEN = 6, //!< Length of data parameter + CLOP_LIMIT = 7, //!< Message Response Limit + CLOP_CMD = 8, //!< Command to RUN + CLOP_DATA = 9, //!< Immediate Data for Write transaction + CLOP_HUMAN = 10, //!< Human readable values (K, M, G, T) + + CLOP_DEVICE = 11, //!< Device name , + CLOP_REGION = 12, //!< Region name , + CLOP_BLOCK = 13, //!< Block id + CLOP_GRANULARITY = 14, //!< Memory Interleave Granularity + CLOP_ZONE = 15, //!< Memory Zone [CLZN] + CLOP_ONLINE = 16, //!< Online action + CLOP_OFFLINE = 17, //!< Offline action + CLOP_KERNEL = 18, //!< Online to kernel normal zone + CLOP_MOVABLE = 19, //!< Online to zone movable + + CLOP_BLOCKS = 20, //!< Show Blocks + CLOP_DEVICES = 21, //!< Show Devices + CLOP_REGIONS = 22, //!< Show Regions + + CLOP_MAX +}; + +/* Valid Zone options */ +enum _CLZN +{ + CLZN_DMA = 0, + CLZN_DMA32 = 1, + CLZN_NORMAL = 2, + CLZN_MOVABLE = 3, + CLZN_NONE = 4, + CLZN_MAX +}; + +/* STRUCTS ===================================================================*/ + +/** + * CLI Option Struct + * + * Each command line parameter is stored in one of these objects + */ +struct opt +{ + int set; //!< Not set (0), set (1) + __u8 u8; //!< Unsigned char value + __u16 u16; //!< Unsigned long value + __u32 u32; //!< Unsigned long value + __u64 u64; //!< Unsigned long long value + __s32 val; //!< Generic signed value + __u64 num; //!< Number of items + __u64 len; //!< Data Buffer Length + char *str; //!< String value + __u8 *buf; //!< Data buffer +}; + +/* GLOBAL VARIABLES ==========================================================*/ + +/** + * Global varible to store parsed CLI options + */ +extern struct opt *opts; + +/* PROTOTYPES ================================================================*/ + +/** + * Free allocated memory by option parsing proceedure + * + * @return 0 upon success. Non zero otherwise + */ +int options_free(); + +/** + * Parse command line options + */ +int options_parse(int argc, char *argv[]); + +#endif //ifndef _OPTIONS_H