/*
Copyright (c) 2015 DataLab, s.l. <http://www.datalab.es>
This file is part of GlusterFS.
This file is licensed to you under your choice of the GNU Lesser
General Public License, version 3 or any later version (LGPLv3 or
later), or the GNU General Public License, version 2 (GPLv2), in all
cases as published by the Free Software Foundation.
*/
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>
#include <glusterfs/syscall.h>
#include "ec-mem-types.h"
#include "ec-code.h"
#include "ec-messages.h"
#include "ec-code-c.h"
#include "ec-helpers.h"
#ifdef USE_EC_DYNAMIC_X64
#include "ec-code-x64.h"
#endif
#ifdef USE_EC_DYNAMIC_SSE
#include "ec-code-sse.h"
#endif
#ifdef USE_EC_DYNAMIC_AVX
#include "ec-code-avx.h"
#endif
#define EC_CODE_SIZE (1024 * 64)
#define EC_CODE_ALIGN 4096
#define EC_CODE_CHUNK_MIN_SIZE 512
#define EC_PROC_BUFFER_SIZE 4096
#define PROC_CPUINFO "/proc/cpuinfo"
struct _ec_code_proc;
typedef struct _ec_code_proc ec_code_proc_t;
struct _ec_code_proc {
int32_t fd;
gf_boolean_t eof;
gf_boolean_t error;
gf_boolean_t skip;
ssize_t size;
ssize_t pos;
char buffer[EC_PROC_BUFFER_SIZE];
};
static ec_code_gen_t *ec_code_gen_table[] = {
#ifdef USE_EC_DYNAMIC_AVX
&ec_code_gen_avx,
#endif
#ifdef USE_EC_DYNAMIC_SSE
&ec_code_gen_sse,
#endif
#ifdef USE_EC_DYNAMIC_X64
&ec_code_gen_x64,
#endif
NULL};
static void
ec_code_arg_set(ec_code_arg_t *arg, uint32_t value)
{
arg->value = value;
}
static void
ec_code_arg_assign(ec_code_builder_t *builder, ec_code_op_t *op,
ec_code_arg_t *arg, uint32_t reg)
{
arg->value = reg;
if (builder->regs <= reg) {
builder->regs = reg + 1;
}
}
static void
ec_code_arg_use(ec_code_builder_t *builder, ec_code_op_t *op,
ec_code_arg_t *arg, uint32_t reg)
{
arg->value = reg;
}
static void
ec_code_arg_update(ec_code_builder_t *builder, ec_code_op_t *op,
ec_code_arg_t *arg, uint32_t reg)
{
arg->value = reg;
}
static ec_code_op_t *
ec_code_op_next(ec_code_builder_t *builder)
{
ec_code_op_t *op;
op = &builder->ops[builder->count++];
memset(op, 0, sizeof(ec_code_op_t));
return op;
}
static void
ec_code_load(ec_code_builder_t *builder, uint32_t bit, uint32_t offset)
{
ec_code_op_t *op;
op = ec_code_op_next(builder);
op->op = EC_GF_OP_LOAD;
ec_code_arg_assign(builder, op, &op->arg1, builder->map[bit]);
ec_code_arg_set(&op->arg2, offset);
ec_code_arg_set(&op->arg3, bit);
}
static void
ec_code_store(ec_code_builder_t *builder, uint32_t reg, uint32_t bit)
{
ec_code_op_t *op;
op = ec_code_op_next(builder);
op->op = EC_GF_OP_STORE;
ec_code_arg_use(builder, op, &op->arg1, builder->map[reg]);
ec_code_arg_set(&op->arg2, 0);
ec_code_arg_set(&op->arg3, bit);
}
static void
ec_code_copy(ec_code_builder_t *builder, uint32_t dst, uint32_t src)
{
ec_code_op_t *op;
op = ec_code_op_next(builder);
op->op = EC_GF_OP_COPY;
ec_code_arg_assign(builder, op, &op->arg1, builder->map[dst]);
ec_code_arg_use(builder, op, &op->arg2, builder->map[src]);
ec_code_arg_set(&op->arg3, 0);
}
static void
ec_code_xor2(ec_code_builder_t *builder, uint32_t dst, uint32_t src)
{
ec_code_op_t *op;
op = ec_code_op_next(builder);
op->op = EC_GF_OP_XOR2;
ec_code_arg_update(builder, op, &op->arg1, builder->map[dst]);
ec_code_arg_use(builder, op, &op->arg2, builder->map[src]);
ec_code_arg_set(&op->arg3, 0);
}
static void
ec_code_xor3(ec_code_builder_t *builder, uint32_t dst, uint32_t src1,
uint32_t src2)
{
ec_code_op_t *op;
if (builder->code->gen->xor3 == NULL) {
ec_code_copy(builder, dst, src1);
ec_code_xor2(builder, dst, src2);
return;
}
op = ec_code_op_next(builder);
op->op = EC_GF_OP_XOR3;
ec_code_arg_assign(builder, op, &op->arg1, builder->map[dst]);
ec_code_arg_use(builder, op, &op->arg2, builder->map[src1]);
ec_code_arg_use(builder, op, &op->arg3, builder->map[src2]);
}
static void
ec_code_xorm(ec_code_builder_t *builder, uint32_t bit, uint32_t offset)
{
ec_code_op_t *op;
op = ec_code_op_next(builder);
op->op = EC_GF_OP_XORM;
ec_code_arg_update(builder, op, &op->arg1, builder->map[bit]);
ec_code_arg_set(&op->arg2, offset);
ec_code_arg_set(&op->arg3, bit);
}
static void
ec_code_dup(ec_code_builder_t *builder, ec_gf_op_t *op)
{
switch (op->op) {
case EC_GF_OP_COPY:
ec_code_copy(builder, op->arg1, op->arg2);
break;
case EC_GF_OP_XOR2:
ec_code_xor2(builder, op->arg1, op->arg2);
break;
case EC_GF_OP_XOR3:
ec_code_xor3(builder, op->arg1, op->arg2, op->arg3);
break;
default:
break;
}
}
static void
ec_code_gf_load(ec_code_builder_t *builder, uint32_t offset)
{
uint32_t i;
for (i = 0; i < builder->code->gf->bits; i++) {
ec_code_load(builder, i, offset);
}
}
static void
ec_code_gf_load_xor(ec_code_builder_t *builder, uint32_t offset)
{
uint32_t i;
for (i = 0; i < builder->code->gf->bits; i++) {
ec_code_xorm(builder, i, offset);
}
}
static void
ec_code_gf_store(ec_code_builder_t *builder)
{
uint32_t i;
for (i = 0; i < builder->code->gf->bits; i++) {
ec_code_store(builder, i, i);
}
}
static void
ec_code_gf_clear(ec_code_builder_t *builder)
{
uint32_t i;
ec_code_xor2(builder, 0, 0);
for (i = 0; i < builder->code->gf->bits; i++) {
ec_code_store(builder, 0, i);
}
}
static void
ec_code_gf_mul(ec_code_builder_t *builder, uint32_t value)
{
ec_gf_mul_t *mul;
ec_gf_op_t *op;
uint32_t map[EC_GF_MAX_REGS];
int32_t i;
mul = builder->code->gf->table[value];
for (op = mul->ops; op->op != EC_GF_OP_END; op++) {
ec_code_dup(builder, op);
}
for (i = 0; i < mul->regs; i++) {
map[i] = builder->map[mul->map[i]];
}
memcpy(builder->map, map, sizeof(uint32_t) * mul->regs);
}
static ec_code_builder_t *
ec_code_prepare(ec_code_t *code, uint32_t count, uint32_t width,
gf_boolean_t linear)
{
ec_code_builder_t *builder;
uint32_t i;
count *= code->gf->bits + code->gf->max_ops;
count += code->gf->bits;
builder = GF_MALLOC(
sizeof(ec_code_builder_t) + sizeof(ec_code_op_t) * count,
ec_mt_ec_code_builder_t);
if (builder == NULL) {
return EC_ERR(ENOMEM);
}
builder->address = 0;
builder->code = code;
builder->size = 0;
builder->count = 0;
builder->regs = 0;
builder->error = 0;
builder->bits = code->gf->bits;
builder->width = width;
builder->data = NULL;
builder->linear = linear;
builder->base = -1;
for (i = 0; i < EC_GF_MAX_REGS; i++) {
builder->map[i] = i;
}
return builder;
}
static size_t
ec_code_space_size(void)
{
return (sizeof(ec_code_space_t) + 15) & ~15;
}
static size_t
ec_code_chunk_size(void)
{
return (sizeof(ec_code_chunk_t) + 15) & ~15;
}
static ec_code_chunk_t *
ec_code_chunk_from_space(ec_code_space_t *space)
{
return (ec_code_chunk_t *)((uintptr_t)space + ec_code_space_size());
}
static void *
ec_code_to_executable(ec_code_space_t *space, void *addr)
{
return (void *)((uintptr_t)addr - (uintptr_t)space +
(uintptr_t)space->exec);
}
static void *
ec_code_from_executable(ec_code_space_t *space, void *addr)
{
return (void *)((uintptr_t)addr - (uintptr_t)space->exec +
(uintptr_t)space);
}
static void *
ec_code_func_from_chunk(ec_code_chunk_t *chunk, void **exec)
{
void *addr;
addr = (void *)((uintptr_t)chunk + ec_code_chunk_size());
*exec = ec_code_to_executable(chunk->space, addr);
return addr;
}
static ec_code_chunk_t *
ec_code_chunk_from_func(ec_code_func_linear_t func)
{
ec_code_chunk_t *chunk;
chunk = (ec_code_chunk_t *)((uintptr_t)func - ec_code_chunk_size());
return ec_code_from_executable(chunk->space, chunk);
}
static ec_code_chunk_t *
ec_code_chunk_split(ec_code_chunk_t *chunk, size_t size)
{
ec_code_chunk_t *extra;
ssize_t avail;
avail = chunk->size - size - ec_code_chunk_size();
if (avail > 0) {
extra = (ec_code_chunk_t *)((uintptr_t)chunk + chunk->size - avail);
extra->space = chunk->space;
extra->size = avail;
list_add(&extra->list, &chunk->list);
chunk->size = size;
}
list_del_init(&chunk->list);
return chunk;
}
static gf_boolean_t
ec_code_chunk_touch(ec_code_chunk_t *prev, ec_code_chunk_t *next)
{
uintptr_t end;
end = (uintptr_t)prev + ec_code_chunk_size() + prev->size;
return (end == (uintptr_t)next);
}
static ec_code_space_t *
ec_code_space_create(ec_code_t *code, size_t size)
{
char path[] = GLUSTERFS_LIBEXECDIR "/ec-code-dynamic.XXXXXX";
ec_code_space_t *space;
void *exec;
int32_t fd, err;
/* We need to create memory areas to store the generated dynamic code.
* Obviously these areas need to be written to be able to create the
* code and they also need to be executable to execute it.
*
* However it's a bad practice to have a memory region that is both
* writable *and* executable. In fact, selinux forbids this and causes
* attempts to do so to fail (unless specifically configured).
*
* To solve the problem we'll use two distinct memory areas mapped to
* the same physical storage. One of the memory areas will have write
* permission, and the other will have execute permission. Both areas
* will have the same contents. The physical storage will be a regular
* file that will be mmapped to both areas.
*/
/* We need to create a temporary file as the backend storage for the
* memory mapped areas. */
/* coverity[secure_temp] mkstemp uses 0600 as the mode and is safe */
fd = mkstemp(path);
if (fd < 0) {
err = errno;
gf_msg(THIS->name, GF_LOG_ERROR, err, EC_MSG_DYN_CREATE_FAILED,
"Unable to create a temporary file for the ec dynamic "
"code");
space = EC_ERR(err);
goto done;
}
/* Once created we don't need to keep it in the file system. It will
* still exist until we close the last file descriptor or unmap the
* memory areas bound to the file. */
sys_unlink(path);
size = (size + EC_CODE_ALIGN - 1) & ~(EC_CODE_ALIGN - 1);
if (sys_ftruncate(fd, size) < 0) {
err = errno;
gf_msg(THIS->name, GF_LOG_ERROR, err, EC_MSG_DYN_CREATE_FAILED,
"Unable to resize the file for the ec dynamic code");
space = EC_ERR(err);
goto done_close;
}
/* This creates an executable memory area to be able to run the
* generated fragments of code. */
exec = mmap(NULL, size, PROT_READ | PROT_EXEC, MAP_SHARED, fd, 0);
if (exec == MAP_FAILED) {
err = errno;
gf_msg(THIS->name, GF_LOG_ERROR, err, EC_MSG_DYN_CREATE_FAILED,
"Unable to map the executable area for the ec dynamic "
"code");
space = EC_ERR(err);
goto done_close;
}
/* It's not important to check the return value of mlock(). If it fails
* everything will continue to work normally. */
mlock(exec, size);
/* This maps a read/write memory area to be able to create the dynamici
* code. */
space = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (space == MAP_FAILED) {
err = errno;
gf_msg(THIS->name, GF_LOG_ERROR, err, EC_MSG_DYN_CREATE_FAILED,
"Unable to map the writable area for the ec dynamic "
"code");
space = EC_ERR(err);
munmap(exec, size);
goto done_close;
}
space->exec = exec;
space->size = size;
space->code = code;
list_add_tail(&space->list, &code->spaces);
INIT_LIST_HEAD(&space->chunks);
done_close:
/* If everything has succeeded, we already have the memory areas
* mapped. We don't need the file descriptor anymore because the
* backend storage will be there until the mmap()'d regions are
* unmapped. */
sys_close(fd);
done:
return space;
}
static void
ec_code_space_destroy(ec_code_space_t *space)
{
list_del_init(&space->list);
munmap(space->exec, space->size);
munmap(space, space->size);
}
static void
ec_code_chunk_merge(ec_code_chunk_t *chunk)
{
ec_code_chunk_t *item, *tmp;
list_for_each_entry_safe(item, tmp, &chunk->space->chunks, list)
{
if ((uintptr_t)item > (uintptr_t)chunk) {
list_add_tail(&chunk->list, &item->list);
if (ec_code_chunk_touch(chunk, item)) {
chunk->size += item->size + ec_code_chunk_size();
list_del_init(&item->list);
}
goto check;
}
if (ec_code_chunk_touch(item, chunk)) {
item->size += chunk->size + ec_code_chunk_size();
list_del_init(&item->list);
chunk = item;
}
}
list_add_tail(&chunk->list, &chunk->space->chunks);
check:
if (chunk->size ==
chunk->space->size - ec_code_space_size() - ec_code_chunk_size()) {
ec_code_space_destroy(chunk->space);
}
}
static ec_code_chunk_t *
ec_code_space_alloc(ec_code_t *code, size_t size)
{
ec_code_space_t *space;
ec_code_chunk_t *chunk;
size_t map_size;
/* To minimize fragmentation, we only allocate chunks of sizes multiples
* of EC_CODE_CHUNK_MIN_SIZE. */
size = ((size + ec_code_chunk_size() + EC_CODE_CHUNK_MIN_SIZE - 1) &
~(EC_CODE_CHUNK_MIN_SIZE - 1)) -
ec_code_chunk_size();
list_for_each_entry(space, &code->spaces, list)
{
list_for_each_entry(chunk, &space->chunks, list)
{
if (chunk->size >= size) {
goto out;
}
}
}
map_size = EC_CODE_SIZE - ec_code_space_size() - ec_code_chunk_size();
if (map_size < size) {
map_size = size;
}
space = ec_code_space_create(code, map_size);
if (EC_IS_ERR(space)) {
return (ec_code_chunk_t *)space;
}
chunk = ec_code_chunk_from_space(space);
chunk->size = map_size - ec_code_space_size() - ec_code_chunk_size();
list_add(&chunk->list, &space->chunks);
out:
chunk->space = space;
return ec_code_chunk_split(chunk, size);
}
static ec_code_chunk_t *
ec_code_alloc(ec_code_t *code, uint32_t size)
{
ec_code_chunk_t *chunk;
LOCK(&code->lock);
chunk = ec_code_space_alloc(code, size);
UNLOCK(&code->lock);
return chunk;
}
static void
ec_code_free(ec_code_chunk_t *chunk)
{
gf_lock_t *lock;
lock = &chunk->space->code->lock;
LOCK(lock);
ec_code_chunk_merge(chunk);
UNLOCK(lock);
}
static int32_t
ec_code_write(ec_code_builder_t *builder)
{
ec_code_gen_t *gen;
ec_code_op_t *op;
uint32_t i;
builder->error = 0;
builder->size = 0;
builder->address = 0;
builder->base = -1;
gen = builder->code->gen;
gen->prolog(builder);
for (i = 0; i < builder->count; i++) {
op = &builder->ops[i];
switch (op->op) {
case EC_GF_OP_LOAD:
gen->load(builder, op->arg1.value, op->arg2.value,
op->arg3.value);
break;
case EC_GF_OP_STORE:
gen->store(builder, op->arg1.value, op->arg3.value);
break;
case EC_GF_OP_COPY:
gen->copy(builder, op->arg1.value, op->arg2.value);
break;
case EC_GF_OP_XOR2:
gen->xor2(builder, op->arg1.value, op->arg2.value);
break;
case EC_GF_OP_XOR3:
gen->xor3(builder, op->arg1.value, op->arg2.value,
op->arg3.value);
break;
case EC_GF_OP_XORM:
gen->xorm(builder, op->arg1.value, op->arg2.value,
op->arg3.value);
break;
default:
break;
}
}
gen->epilog(builder);
return builder->error;
}
static void *
ec_code_compile(ec_code_builder_t *builder)
{
ec_code_chunk_t *chunk;
void *func;
int32_t err;
err = ec_code_write(builder);
if (err != 0) {
return EC_ERR(err);
}
chunk = ec_code_alloc(builder->code, builder->size);
if (EC_IS_ERR(chunk)) {
return chunk;
}
builder->data = ec_code_func_from_chunk(chunk, &func);
err = ec_code_write(builder);
if (err != 0) {
ec_code_free(chunk);
return EC_ERR(err);
}
GF_FREE(builder);
return func;
}
ec_code_t *
ec_code_create(ec_gf_t *gf, ec_code_gen_t *gen)
{
ec_code_t *code;
code = GF_MALLOC(sizeof(ec_code_t), ec_mt_ec_code_t);
if (code == NULL) {
return EC_ERR(ENOMEM);
}
memset(code, 0, sizeof(ec_code_t));
INIT_LIST_HEAD(&code->spaces);
LOCK_INIT(&code->lock);
code->gf = gf;
code->gen = gen;
return code;
}
void
ec_code_destroy(ec_code_t *code)
{
if (!list_empty(&code->spaces)) {
}
LOCK_DESTROY(&code->lock);
GF_FREE(code);
}
static uint32_t
ec_code_value_next(uint32_t *values, uint32_t count, uint32_t *offset)
{
uint32_t i, next;
next = 0;
for (i = *offset + 1; i < count; i++) {
next = values[i];
if (next != 0) {
break;
}
}
*offset = i;
return next;
}
static void *
ec_code_build_dynamic(ec_code_t *code, uint32_t width, uint32_t *values,
uint32_t count, gf_boolean_t linear)
{
ec_code_builder_t *builder;
uint32_t offset, val, next;
builder = ec_code_prepare(code, count, width, linear);
if (EC_IS_ERR(builder)) {
return builder;
}
offset = -1;
next = ec_code_value_next(values, count, &offset);
if (next != 0) {
ec_code_gf_load(builder, offset);
do {
val = next;
next = ec_code_value_next(values, count, &offset);
if (next != 0) {
ec_code_gf_mul(builder, ec_gf_div(code->gf, val, next));
ec_code_gf_load_xor(builder, offset);
}
} while (next != 0);
ec_code_gf_mul(builder, val);
ec_code_gf_store(builder);
} else {
ec_code_gf_clear(builder);
}
return ec_code_compile(builder);
}
static void *
ec_code_build(ec_code_t *code, uint32_t width, uint32_t *values, uint32_t count,
gf_boolean_t linear)
{
void *func;
if (code->gen != NULL) {
func = ec_code_build_dynamic(code, width, values, count, linear);
if (!EC_IS_ERR(func)) {
return func;
}
gf_msg_debug(THIS->name, GF_LOG_DEBUG,
"Unable to generate dynamic code. Falling back "
"to precompiled code");
/* The dynamic code generation shouldn't fail in normal
* conditions, but if it fails at some point, it's very
* probable that it will fail again, so we completely disable
* dynamic code generation. */
code->gen = NULL;
}
ec_code_c_prepare(code->gf, values, count);
if (linear) {
return ec_code_c_linear;
}
return ec_code_c_interleaved;
}
ec_code_func_linear_t
ec_code_build_linear(ec_code_t *code, uint32_t width, uint32_t *values,
uint32_t count)
{
return (ec_code_func_linear_t)ec_code_build(code, width, values, count,
_gf_true);
}
ec_code_func_interleaved_t
ec_code_build_interleaved(ec_code_t *code, uint32_t width, uint32_t *values,
uint32_t count)
{
return (ec_code_func_interleaved_t)ec_code_build(code, width, values, count,
_gf_false);
}
void
ec_code_release(ec_code_t *code, ec_code_func_t *func)
{
if ((func->linear != ec_code_c_linear) &&
(func->interleaved != ec_code_c_interleaved)) {
ec_code_free(ec_code_chunk_from_func(func->linear));
}
}
void
ec_code_error(ec_code_builder_t *builder, int32_t error)
{
if (builder->error == 0) {
gf_msg(THIS->name, GF_LOG_ERROR, error, EC_MSG_DYN_CODEGEN_FAILED,
"Failed to generate dynamic code");
builder->error = error;
}
}
void
ec_code_emit(ec_code_builder_t *builder, uint8_t *bytes, uint32_t count)
{
if (builder->error != 0) {
return;
}
if (builder->data != NULL) {
memcpy(builder->data + builder->size, bytes, count);
}
builder->size += count;
builder->address += count;
}
static char *
ec_code_proc_trim_left(char *text, ssize_t *length)
{
ssize_t len;
for (len = *length; (len > 0) && isspace(*text); len--) {
text++;
}
*length = len;
return text;
}
static char *
ec_code_proc_trim_right(char *text, ssize_t *length, char sep)
{
char *last;
ssize_t len;
len = *length;
last = text;
for (len = *length; (len > 0) && (*text != sep); len--) {
if (!isspace(*text)) {
last = text + 1;
}
text++;
}
*last = 0;
*length = len;
return text;
}
static char *
ec_code_proc_line_parse(ec_code_proc_t *file, ssize_t *length)
{
char *text, *end;
ssize_t len;
len = file->size - file->pos;
text = ec_code_proc_trim_left(file->buffer + file->pos, &len);
end = ec_code_proc_trim_right(text, &len, '\n');
if (len == 0) {
if (!file->eof) {
if (text == file->buffer) {
file->size = file->pos = 0;
file->skip = _gf_true;
} else {
file->size = file->pos = end - text;
memmove(file->buffer, text, file->pos + 1);
}
len = sys_read(file->fd, file->buffer + file->pos,
sizeof(file->buffer) - file->pos - 1);
if (len > 0) {
file->size += len;
}
file->error = len < 0;
file->eof = len <= 0;
return NULL;
}
file->size = file->pos = 0;
} else {
file->pos = end - file->buffer + 1;
}
*length = end - text;
if (file->skip) {
file->skip = _gf_false;
text = NULL;
}
return text;
}
static char *
ec_code_proc_line(ec_code_proc_t *file, ssize_t *length)
{
char *text;
text = NULL;
while (!file->eof) {
text = ec_code_proc_line_parse(file, length);
if (text != NULL) {
break;
}
}
return text;
}
static char *
ec_code_proc_split(char *text, ssize_t *length, char sep)
{
text = ec_code_proc_trim_right(text, length, sep);
if (*length == 0) {
return NULL;
}
(*length)--;
text++;
return ec_code_proc_trim_left(text, length);
}
static uint32_t
ec_code_cpu_check(uint32_t idx, char *list, uint32_t count)
{
ec_code_gen_t *gen;
char **ptr;
char *table[count + 1];
uint32_t i;
for (i = 0; i < count; i++) {
table[i] = list;
list += strlen(list) + 1;
}
gen = ec_code_gen_table[idx];
while (gen != NULL) {
for (ptr = gen->flags; *ptr != NULL; ptr++) {
for (i = 0; i < count; i++) {
if (strcmp(*ptr, table[i]) == 0) {
break;
}
}
if (i >= count) {
gen = ec_code_gen_table[++idx];
break;
}
}
if (*ptr == NULL) {
break;
}
}
return idx;
}
ec_code_gen_t *
ec_code_detect(xlator_t *xl, const char *def)
{
ec_code_proc_t file;
ec_code_gen_t *gen = NULL;
char *line, *data, *list;
ssize_t length;
uint32_t count, base, select;
if (strcmp(def, "none") == 0) {
gf_msg(xl->name, GF_LOG_INFO, 0, EC_MSG_EXTENSION_NONE,
"Not using any cpu extensions");
return NULL;
}
file.fd = sys_open(PROC_CPUINFO, O_RDONLY, 0);
if (file.fd < 0) {
goto out;
}
file.size = file.pos = 0;
file.eof = file.error = file.skip = _gf_false;
select = 0;
if (strcmp(def, "auto") != 0) {
while (ec_code_gen_table[select] != NULL) {
if (strcmp(ec_code_gen_table[select]->name, def) == 0) {
break;
}
select++;
}
if (ec_code_gen_table[select] == NULL) {
gf_msg(xl->name, GF_LOG_WARNING, EINVAL, EC_MSG_EXTENSION_UNKNOWN,
"CPU extension '%s' is not known. Not using any cpu "
"extensions",
def);
return NULL;
}
} else {
def = NULL;
}
while ((line = ec_code_proc_line(&file, &length)) != NULL) {
data = ec_code_proc_split(line, &length, ':');
if ((data != NULL) && (strcmp(line, "flags") == 0)) {
list = data;
count = 0;
while ((data != NULL) && (*data != 0)) {
count++;
data = ec_code_proc_split(data, &length, ' ');
}
base = select;
select = ec_code_cpu_check(select, list, count);
if ((base != select) && (def != NULL)) {
gf_msg(xl->name, GF_LOG_WARNING, ENOTSUP,
EC_MSG_EXTENSION_UNSUPPORTED,
"CPU extension '%s' is not supported", def);
def = NULL;
}
}
}
if (file.error) {
gf_msg(xl->name, GF_LOG_WARNING, 0, EC_MSG_EXTENSION_FAILED,
"Unable to determine supported CPU extensions. Not using any "
"cpu extensions");
gen = NULL;
} else {
gen = ec_code_gen_table[select];
if (gen == NULL) {
gf_msg(xl->name, GF_LOG_INFO, 0, EC_MSG_EXTENSION_NONE,
"Not using any cpu extensions");
} else {
gf_msg(xl->name, GF_LOG_INFO, 0, EC_MSG_EXTENSION,
"Using '%s' CPU extensions", gen->name);
}
}
sys_close(file.fd);
out:
return gen;
}