blob: fc45cd27d8d3ddc2c350ad590629eef71c74576e [file] [log] [blame]
/*
* Note: This is similar to a very basic setup
* of ZBD devices
*
* Specify fdp=1 (With char devices /dev/ng0n1)
*/
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "fio.h"
#include "file.h"
#include "pshared.h"
#include "dataplacement.h"
static int fdp_ruh_info(struct thread_data *td, struct fio_file *f,
struct fio_ruhs_info *ruhs)
{
int ret = -EINVAL;
if (!td->io_ops) {
log_err("fio: no ops set in fdp init?!\n");
return ret;
}
if (td->io_ops->fdp_fetch_ruhs) {
ret = td->io_ops->fdp_fetch_ruhs(td, f, ruhs);
if (ret < 0) {
td_verror(td, errno, "fdp fetch ruhs failed");
log_err("%s: fdp fetch ruhs failed (%d)\n",
f->file_name, errno);
}
} else {
log_err("%s: engine (%s) lacks fetch ruhs\n",
f->file_name, td->io_ops->name);
}
return ret;
}
static int init_ruh_info(struct thread_data *td, struct fio_file *f)
{
struct fio_ruhs_info *ruhs, *tmp;
uint32_t nr_ruhs;
int i, ret;
/* set up the data structure used for FDP to work with the supplied stream IDs */
if (td->o.dp_type == FIO_DP_STREAMS) {
if (!td->o.dp_nr_ids) {
log_err("fio: stream IDs must be provided for dataplacement=streams\n");
return -EINVAL;
}
ruhs = scalloc(1, sizeof(*ruhs) + td->o.dp_nr_ids * sizeof(*ruhs->plis));
if (!ruhs)
return -ENOMEM;
ruhs->nr_ruhs = td->o.dp_nr_ids;
for (int i = 0; i < ruhs->nr_ruhs; i++)
ruhs->plis[i] = td->o.dp_ids[i];
f->ruhs_info = ruhs;
return 0;
}
/*
* Since we don't know the actual number of ruhs. Only fetch the header.
* We will reallocate this buffer and then fetch all the ruhs again.
*/
ruhs = calloc(1, sizeof(*ruhs));
ret = fdp_ruh_info(td, f, ruhs);
if (ret) {
log_err("fio: ruh info failed for %s (%d)\n",
f->file_name, -ret);
goto out;
}
nr_ruhs = ruhs->nr_ruhs;
ruhs = realloc(ruhs, sizeof(*ruhs) + nr_ruhs * sizeof(*ruhs->plis));
if (!ruhs) {
log_err("fio: ruhs buffer realloc failed for %s\n",
f->file_name);
ret = -ENOMEM;
goto out;
}
ruhs->nr_ruhs = nr_ruhs;
ret = fdp_ruh_info(td, f, ruhs);
if (ret) {
log_err("fio: ruh info failed for %s (%d)\n",
f->file_name, -ret);
goto out;
}
if (td->o.dp_nr_ids == 0) {
if (ruhs->nr_ruhs > FIO_MAX_DP_IDS)
ruhs->nr_ruhs = FIO_MAX_DP_IDS;
} else {
for (i = 0; i < td->o.dp_nr_ids; i++) {
if (td->o.dp_ids[i] >= ruhs->nr_ruhs) {
log_err("fio: for %s PID index %d must be smaller than %d\n",
f->file_name, td->o.dp_ids[i],
ruhs->nr_ruhs);
ret = -EINVAL;
goto out;
}
}
ruhs->nr_ruhs = td->o.dp_nr_ids;
}
tmp = scalloc(1, sizeof(*tmp) + ruhs->nr_ruhs * sizeof(*tmp->plis));
if (!tmp) {
ret = -ENOMEM;
goto out;
}
if (td->o.dp_nr_ids == 0) {
for (i = 0; i < ruhs->nr_ruhs; i++)
tmp->plis[i] = ruhs->plis[i];
tmp->nr_ruhs = ruhs->nr_ruhs;
f->ruhs_info = tmp;
free(ruhs);
return 0;
}
tmp->nr_ruhs = td->o.dp_nr_ids;
for (i = 0; i < td->o.dp_nr_ids; i++)
tmp->plis[i] = ruhs->plis[td->o.dp_ids[i]];
f->ruhs_info = tmp;
out:
free(ruhs);
return ret;
}
static int init_ruh_scheme(struct thread_data *td, struct fio_file *f)
{
struct fio_ruhs_scheme *ruh_scheme;
FILE *scheme_fp;
unsigned long long start, end;
uint16_t pli;
int ret = 0;
if (td->o.dp_id_select != FIO_DP_SCHEME)
return 0;
/* Get the scheme from the file */
scheme_fp = fopen(td->o.dp_scheme_file, "r");
if (!scheme_fp) {
log_err("fio: ruh scheme failed to open scheme file %s\n",
td->o.dp_scheme_file);
ret = -errno;
goto out;
}
ruh_scheme = scalloc(1, sizeof(*ruh_scheme));
if (!ruh_scheme) {
ret = -ENOMEM;
goto out_with_close_fp;
}
for (int i = 0;
i < DP_MAX_SCHEME_ENTRIES && fscanf(scheme_fp, "%llu,%llu,%hu\n", &start, &end, &pli) == 3;
i++) {
ruh_scheme->scheme_entries[i].start_offset = start;
ruh_scheme->scheme_entries[i].end_offset = end;
ruh_scheme->scheme_entries[i].pli = pli;
ruh_scheme->nr_schemes++;
}
if (fscanf(scheme_fp, "%llu,%llu,%hu\n", &start, &end, &pli) == 3)
log_info("fio: too many scheme entries in %s. Only the first %d scheme entries are applied\n",
td->o.dp_scheme_file,
DP_MAX_SCHEME_ENTRIES);
f->ruhs_scheme = ruh_scheme;
out_with_close_fp:
fclose(scheme_fp);
out:
return ret;
}
int dp_init(struct thread_data *td)
{
struct fio_file *f;
int i, ret = 0;
for_each_file(td, f, i) {
ret = init_ruh_info(td, f);
if (ret)
break;
ret = init_ruh_scheme(td, f);
if (ret)
break;
}
return ret;
}
void fdp_free_ruhs_info(struct fio_file *f)
{
if (!f->ruhs_info)
return;
sfree(f->ruhs_info);
f->ruhs_info = NULL;
if (!f->ruhs_scheme)
return;
sfree(f->ruhs_scheme);
f->ruhs_scheme = NULL;
}
void dp_fill_dspec_data(struct thread_data *td, struct io_u *io_u)
{
struct fio_file *f = io_u->file;
struct fio_ruhs_info *ruhs = f->ruhs_info;
int dspec;
if (!ruhs || io_u->ddir != DDIR_WRITE) {
io_u->dtype = 0;
io_u->dspec = 0;
return;
}
if (td->o.dp_id_select == FIO_DP_RR) {
if (ruhs->pli_loc >= ruhs->nr_ruhs)
ruhs->pli_loc = 0;
dspec = ruhs->plis[ruhs->pli_loc++];
} else if (td->o.dp_id_select == FIO_DP_SCHEME) {
struct fio_ruhs_scheme *ruhs_scheme = f->ruhs_scheme;
unsigned long long offset = io_u->offset;
int i;
for (i = 0; i < ruhs_scheme->nr_schemes; i++) {
if (offset >= ruhs_scheme->scheme_entries[i].start_offset &&
offset < ruhs_scheme->scheme_entries[i].end_offset) {
dspec = ruhs_scheme->scheme_entries[i].pli;
break;
}
}
/*
* If the write offset is not affected by any scheme entry,
* 0(default RUH) will be assigned to dspec
*/
if (i == ruhs_scheme->nr_schemes)
dspec = 0;
} else {
ruhs->pli_loc = rand_between(&td->fdp_state, 0, ruhs->nr_ruhs - 1);
dspec = ruhs->plis[ruhs->pli_loc];
}
io_u->dtype = td->o.dp_type == FIO_DP_FDP ? FDP_DIR_DTYPE : STREAMS_DIR_DTYPE;
io_u->dspec = dspec;
dprint(FD_IO, "dtype set to 0x%x, dspec set to 0x%x\n", io_u->dtype, io_u->dspec);
}