libmem-release/libmem.c

2563 lines
52 KiB
C
Raw Permalink Normal View History

2024-08-07 22:24:28 +00:00
/**
* @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 <code@jrlabs.io>
*/
/* INCLUDES ==================================================================*/
/* printf()
*/
#include <stdio.h>
/* calloc()
* free()
*/
#include <stdlib.h>
/* memset()
* memcpy()
*/
#include <string.h>
/* open()
*/
#include <fcntl.h>
/* close()
* unlink()
*/
#include <unistd.h>
/* errno
*/
#include <errno.h>
/* opendir()
*/
#include <dirent.h>
/* LOG_* Macros
*/
#include <syslog.h>
/* cxl objects
* cxl_new()
*/
#include <cxl/libcxl.h>
/* struct daxctl_region
*/
#include <daxctl/libdaxctl.h>
#include <cxltoyaml.h>
/* 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 ////////////////////////////////////////////////////////////////////