blob: ae66e0ae7dfbb9246078f933147ec8e03d66729b [file] [log] [blame]
/*
* Copyright (c) 2016, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
* more details.
*/
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <syslog.h>
#include <util/log.h>
#include <util/size.h>
#include <uuid/uuid.h>
#include <util/json.h>
#include <util/filter.h>
#include <json-c/json.h>
#include <util/util.h>
#include <util/kernel.h>
#include <util/fletcher.h>
#include <ndctl/libndctl.h>
#include <subcmd/parse-options.h>
#include <ccan/short_types/short_types.h>
#include <ccan/endian/endian.h>
#define cpu_to_le64 cpu_to_le64
#include <linux/bitmap.h>
enum {
NSINDEX_SIG_LEN = 16,
NSINDEX_ALIGN = 256,
NSINDEX_SEQ_MASK = 0x3,
NSLABEL_UUID_LEN = 16,
NSLABEL_NAME_LEN = 64,
};
struct namespace_index {
char sig[NSINDEX_SIG_LEN];
u8 flags[3];
u8 labelsize;
le32 seq;
le64 myoff;
le64 mysize;
le64 otheroff;
le64 labeloff;
le32 nslot;
le16 major;
le16 minor;
le64 checksum;
char free[0];
};
struct namespace_label {
char uuid[NSLABEL_UUID_LEN];
char name[NSLABEL_NAME_LEN];
le32 flags;
le16 nlabel;
le16 position;
le64 isetcookie;
le64 lbasize;
le64 dpa;
le64 rawsize;
le32 slot;
/*
* Accessing fields past this point should be gated by a
* namespace_label_has() check.
*/
u8 align;
u8 reserved[3];
char type_guid[NSLABEL_UUID_LEN];
char abstraction_guid[NSLABEL_UUID_LEN];
u8 reserved2[88];
le64 checksum;
};
struct nvdimm_data {
struct ndctl_dimm *dimm;
struct ndctl_cmd *cmd_read;
unsigned long config_size;
size_t nslabel_size;
struct log_ctx ctx;
void *data;
int nsindex_size;
int ns_current, ns_next;
};
static size_t sizeof_namespace_label(struct nvdimm_data *ndd)
{
struct namespace_index nsindex;
int v1 = 0, v2 = 0;
ssize_t offset;
if (ndd->nslabel_size)
return ndd->nslabel_size;
for (offset = 0; offset < NSINDEX_ALIGN * 2; offset += NSINDEX_ALIGN) {
ssize_t len = (ssize_t) sizeof(nsindex);
len = ndctl_cmd_cfg_read_get_data(ndd->cmd_read, &nsindex,
len, offset);
if (len < 0)
break;
/*
* Since we're doing a best effort parsing we don't
* fully validate the index block. Instead just assume
* v1.1 unless there's 2 index blocks that say v1.2.
*/
if (le16_to_cpu(nsindex.major) == 1) {
if (le16_to_cpu(nsindex.minor) == 1)
v1++;
else if (le16_to_cpu(nsindex.minor) == 2)
v2++;
}
}
if (v2 > v1)
ndd->nslabel_size = 256;
else
ndd->nslabel_size = 128;
return ndd->nslabel_size;
}
#define namespace_label_has(ndd, field) \
(offsetof(struct namespace_label, field) \
< sizeof_namespace_label(ndd))
static const char NSINDEX_SIGNATURE[] = "NAMESPACE_INDEX\0";
struct action_context {
struct json_object *jdimms;
int labelversion;
FILE *f_out;
FILE *f_in;
};
static int action_disable(struct ndctl_dimm *dimm, struct action_context *actx)
{
if (ndctl_dimm_is_active(dimm)) {
fprintf(stderr, "%s is active, skipping...\n",
ndctl_dimm_get_devname(dimm));
return -EBUSY;
}
return ndctl_dimm_disable(dimm);
}
static int action_enable(struct ndctl_dimm *dimm, struct action_context *actx)
{
return ndctl_dimm_enable(dimm);
}
static int action_zero(struct ndctl_dimm *dimm, struct action_context *actx)
{
return ndctl_dimm_zero_labels(dimm);
}
static struct json_object *dump_label_json(struct ndctl_cmd *cmd_read, ssize_t size)
{
struct nvdimm_data __ndd = {
.nslabel_size = 0,
.cmd_read = cmd_read
}, *ndd = &__ndd;
struct json_object *jarray = json_object_new_array();
struct json_object *jlabel = NULL;
struct namespace_label nslabel;
unsigned int slot = -1;
ssize_t offset;
if (!jarray)
return NULL;
for (offset = NSINDEX_ALIGN * 2; offset < size;
offset += sizeof_namespace_label(ndd)) {
ssize_t len = min((ssize_t) sizeof_namespace_label(ndd),
size - offset);
struct json_object *jobj;
char uuid[40];
slot++;
jlabel = json_object_new_object();
if (!jlabel)
break;
if (len < (ssize_t) sizeof_namespace_label(ndd))
break;
len = ndctl_cmd_cfg_read_get_data(cmd_read, &nslabel, len, offset);
if (len < 0)
break;
if (le32_to_cpu(nslabel.slot) != slot)
continue;
uuid_unparse((void *) nslabel.uuid, uuid);
jobj = json_object_new_string(uuid);
if (!jobj)
break;
json_object_object_add(jlabel, "uuid", jobj);
nslabel.name[NSLABEL_NAME_LEN - 1] = 0;
jobj = json_object_new_string(nslabel.name);
if (!jobj)
break;
json_object_object_add(jlabel, "name", jobj);
jobj = json_object_new_int(le32_to_cpu(nslabel.slot));
if (!jobj)
break;
json_object_object_add(jlabel, "slot", jobj);
jobj = json_object_new_int(le16_to_cpu(nslabel.position));
if (!jobj)
break;
json_object_object_add(jlabel, "position", jobj);
jobj = json_object_new_int(le16_to_cpu(nslabel.nlabel));
if (!jobj)
break;
json_object_object_add(jlabel, "nlabel", jobj);
jobj = json_object_new_int64(le64_to_cpu(nslabel.isetcookie));
if (!jobj)
break;
json_object_object_add(jlabel, "isetcookie", jobj);
jobj = json_object_new_int64(le64_to_cpu(nslabel.lbasize));
if (!jobj)
break;
json_object_object_add(jlabel, "lbasize", jobj);
jobj = json_object_new_int64(le64_to_cpu(nslabel.dpa));
if (!jobj)
break;
json_object_object_add(jlabel, "dpa", jobj);
jobj = json_object_new_int64(le64_to_cpu(nslabel.rawsize));
if (!jobj)
break;
json_object_object_add(jlabel, "rawsize", jobj);
json_object_array_add(jarray, jlabel);
if (sizeof_namespace_label(ndd) < 256)
continue;
uuid_unparse((void *) nslabel.type_guid, uuid);
jobj = json_object_new_string(uuid);
if (!jobj)
break;
json_object_object_add(jlabel, "type_guid", jobj);
uuid_unparse((void *) nslabel.abstraction_guid, uuid);
jobj = json_object_new_string(uuid);
if (!jobj)
break;
json_object_object_add(jlabel, "abstraction_guid", jobj);
}
if (json_object_array_length(jarray) < 1) {
json_object_put(jarray);
if (jlabel)
json_object_put(jlabel);
jarray = NULL;
}
return jarray;
}
static struct json_object *dump_index_json(struct ndctl_cmd *cmd_read, ssize_t size)
{
struct json_object *jarray = json_object_new_array();
struct json_object *jindex = NULL;
struct namespace_index nsindex;
ssize_t offset;
if (!jarray)
return NULL;
for (offset = 0; offset < NSINDEX_ALIGN * 2; offset += NSINDEX_ALIGN) {
ssize_t len = min((ssize_t) sizeof(nsindex), size - offset);
struct json_object *jobj;
jindex = json_object_new_object();
if (!jindex)
break;
if (len < (ssize_t) sizeof(nsindex))
break;
len = ndctl_cmd_cfg_read_get_data(cmd_read, &nsindex, len, offset);
if (len < 0)
break;
nsindex.sig[NSINDEX_SIG_LEN - 1] = 0;
jobj = json_object_new_string(nsindex.sig);
if (!jobj)
break;
json_object_object_add(jindex, "signature", jobj);
jobj = json_object_new_int(le16_to_cpu(nsindex.major));
if (!jobj)
break;
json_object_object_add(jindex, "major", jobj);
jobj = json_object_new_int(le16_to_cpu(nsindex.minor));
if (!jobj)
break;
json_object_object_add(jindex, "minor", jobj);
jobj = json_object_new_int(1 << (7 + nsindex.labelsize));
if (!jobj)
break;
json_object_object_add(jindex, "labelsize", jobj);
jobj = json_object_new_int(le32_to_cpu(nsindex.seq));
if (!jobj)
break;
json_object_object_add(jindex, "seq", jobj);
jobj = json_object_new_int(le32_to_cpu(nsindex.nslot));
if (!jobj)
break;
json_object_object_add(jindex, "nslot", jobj);
json_object_array_add(jarray, jindex);
}
if (json_object_array_length(jarray) < 1) {
json_object_put(jarray);
if (jindex)
json_object_put(jindex);
jarray = NULL;
}
return jarray;
}
static struct json_object *dump_json(struct ndctl_dimm *dimm,
struct ndctl_cmd *cmd_read, ssize_t size)
{
struct json_object *jdimm = json_object_new_object();
struct json_object *jlabel, *jobj, *jindex;
if (!jdimm)
return NULL;
jindex = dump_index_json(cmd_read, size);
if (!jindex)
goto err_jindex;
jlabel = dump_label_json(cmd_read, size);
if (!jlabel)
goto err_jlabel;
jobj = json_object_new_string(ndctl_dimm_get_devname(dimm));
if (!jobj)
goto err_jobj;
json_object_object_add(jdimm, "dev", jobj);
json_object_object_add(jdimm, "index", jindex);
json_object_object_add(jdimm, "label", jlabel);
return jdimm;
err_jobj:
json_object_put(jlabel);
err_jlabel:
json_object_put(jindex);
err_jindex:
json_object_put(jdimm);
return NULL;
}
static int rw_bin(FILE *f, struct ndctl_cmd *cmd, ssize_t size, int rw)
{
char buf[4096];
ssize_t offset, write = 0;
for (offset = 0; offset < size; offset += sizeof(buf)) {
ssize_t len = min((ssize_t) sizeof(buf), size - offset), rc;
if (rw) {
len = fread(buf, 1, len, f);
if (len == 0)
break;
rc = ndctl_cmd_cfg_write_set_data(cmd, buf, len, offset);
if (rc < 0)
return -ENXIO;
write += len;
} else {
len = ndctl_cmd_cfg_read_get_data(cmd, buf, len, offset);
if (len < 0)
return len;
rc = fwrite(buf, 1, len, f);
if (rc != len)
return -ENXIO;
fflush(f);
}
}
if (write)
return ndctl_cmd_submit(cmd);
return 0;
}
static struct ndctl_cmd *read_labels(struct ndctl_dimm *dimm)
{
struct ndctl_bus *bus = ndctl_dimm_get_bus(dimm);
struct ndctl_cmd *cmd_size, *cmd_read;
int rc;
rc = ndctl_bus_wait_probe(bus);
if (rc < 0)
return NULL;
cmd_size = ndctl_dimm_cmd_new_cfg_size(dimm);
if (!cmd_size)
return NULL;
rc = ndctl_cmd_submit(cmd_size);
if (rc || ndctl_cmd_get_firmware_status(cmd_size))
goto out_size;
cmd_read = ndctl_dimm_cmd_new_cfg_read(cmd_size);
if (!cmd_read)
goto out_size;
rc = ndctl_cmd_submit(cmd_read);
if (rc || ndctl_cmd_get_firmware_status(cmd_read))
goto out_read;
ndctl_cmd_unref(cmd_size);
return cmd_read;
out_read:
ndctl_cmd_unref(cmd_read);
out_size:
ndctl_cmd_unref(cmd_size);
return NULL;
}
static int action_write(struct ndctl_dimm *dimm, struct action_context *actx)
{
struct ndctl_cmd *cmd_read, *cmd_write;
ssize_t size;
int rc = 0;
if (ndctl_dimm_is_active(dimm)) {
fprintf(stderr, "dimm is active, abort label write\n");
return -EBUSY;
}
cmd_read = read_labels(dimm);
if (!cmd_read)
return -ENXIO;
cmd_write = ndctl_dimm_cmd_new_cfg_write(cmd_read);
if (!cmd_write) {
ndctl_cmd_unref(cmd_read);
return -ENXIO;
}
size = ndctl_cmd_cfg_read_get_size(cmd_read);
rc = rw_bin(actx->f_in, cmd_write, size, 1);
/*
* If the dimm is already disabled the kernel is not holding a cached
* copy of the label space.
*/
if (!ndctl_dimm_is_enabled(dimm))
goto out;
rc = ndctl_dimm_disable(dimm);
if (rc)
goto out;
rc = ndctl_dimm_enable(dimm);
out:
ndctl_cmd_unref(cmd_read);
ndctl_cmd_unref(cmd_write);
return rc;
}
static int action_read(struct ndctl_dimm *dimm, struct action_context *actx)
{
struct ndctl_cmd *cmd_read;
ssize_t size;
int rc = 0;
cmd_read = read_labels(dimm);
if (!cmd_read)
return -ENXIO;
size = ndctl_cmd_cfg_read_get_size(cmd_read);
if (actx->jdimms) {
struct json_object *jdimm = dump_json(dimm, cmd_read, size);
if (jdimm)
json_object_array_add(actx->jdimms, jdimm);
else
rc = -ENOMEM;
} else
rc = rw_bin(actx->f_out, cmd_read, size, 0);
ndctl_cmd_unref(cmd_read);
return rc;
}
/*
* Note, best_seq(), inc_seq(), sizeof_namespace_index()
* nvdimm_num_label_slots(), label_validate(), and label_write_index()
* are copied from drivers/nvdimm/label.c in the Linux kernel with the
* following modifications:
* 1/ s,nd_,,gc
* 2/ s,ndd->nsarea.config_size,ndd->config_size,gc
* 3/ s,dev_dbg(dev,dbg(ndd,gc
* 4/ s,__le,le,gc
* 5/ s,__cpu_to,cpu_to,gc
* 6/ remove flags argument to label_write_index
* 7/ dropped clear_bit_le() usage in label_write_index
* 8/ s,nvdimm_drvdata,nvdimm_data,gc
*/
static unsigned inc_seq(unsigned seq)
{
static const unsigned next[] = { 0, 2, 3, 1 };
return next[seq & 3];
}
static u32 best_seq(u32 a, u32 b)
{
a &= NSINDEX_SEQ_MASK;
b &= NSINDEX_SEQ_MASK;
if (a == 0 || a == b)
return b;
else if (b == 0)
return a;
else if (inc_seq(a) == b)
return b;
else
return a;
}
size_t sizeof_namespace_index(struct nvdimm_data *ndd)
{
u32 index_span;
if (ndd->nsindex_size)
return ndd->nsindex_size;
/*
* The minimum index space is 512 bytes, with that amount of
* index we can describe ~1400 labels which is less than a byte
* of overhead per label. Round up to a byte of overhead per
* label and determine the size of the index region. Yes, this
* starts to waste space at larger config_sizes, but it's
* unlikely we'll ever see anything but 128K.
*/
index_span = ndd->config_size / (sizeof_namespace_label(ndd) + 1);
index_span /= NSINDEX_ALIGN * 2;
ndd->nsindex_size = index_span * NSINDEX_ALIGN;
return ndd->nsindex_size;
}
int nvdimm_num_label_slots(struct nvdimm_data *ndd)
{
return ndd->config_size / (sizeof_namespace_label(ndd) + 1);
}
static struct namespace_index *to_namespace_index(struct nvdimm_data *ndd,
int i)
{
char *index;
if (i < 0)
return NULL;
index = (char *) ndd->data + sizeof_namespace_index(ndd) * i;
return (struct namespace_index *) index;
}
static int __label_validate(struct nvdimm_data *ndd)
{
/*
* On media label format consists of two index blocks followed
* by an array of labels. None of these structures are ever
* updated in place. A sequence number tracks the current
* active index and the next one to write, while labels are
* written to free slots.
*
* +------------+
* | |
* | nsindex0 |
* | |
* +------------+
* | |
* | nsindex1 |
* | |
* +------------+
* | label0 |
* +------------+
* | label1 |
* +------------+
* | |
* ....nslot...
* | |
* +------------+
* | labelN |
* +------------+
*/
struct namespace_index *nsindex[] = {
to_namespace_index(ndd, 0),
to_namespace_index(ndd, 1),
};
const int num_index = ARRAY_SIZE(nsindex);
bool valid[2] = { 0 };
int i, num_valid = 0;
u32 seq;
for (i = 0; i < num_index; i++) {
u32 nslot;
u8 sig[NSINDEX_SIG_LEN];
u64 sum_save, sum, size;
unsigned int version, labelsize;
memcpy(sig, nsindex[i]->sig, NSINDEX_SIG_LEN);
if (memcmp(sig, NSINDEX_SIGNATURE, NSINDEX_SIG_LEN) != 0) {
dbg(ndd, "nsindex%d signature invalid\n", i);
continue;
}
/* label sizes larger than 128 arrived with v1.2 */
version = le16_to_cpu(nsindex[i]->major) * 100
+ le16_to_cpu(nsindex[i]->minor);
if (version >= 102)
labelsize = 1 << (7 + nsindex[i]->labelsize);
else
labelsize = 128;
if (labelsize != sizeof_namespace_label(ndd)) {
dbg(ndd, "nsindex%d labelsize %d invalid\n",
i, nsindex[i]->labelsize);
continue;
}
sum_save = le64_to_cpu(nsindex[i]->checksum);
nsindex[i]->checksum = cpu_to_le64(0);
sum = fletcher64(nsindex[i], sizeof_namespace_index(ndd), 1);
nsindex[i]->checksum = cpu_to_le64(sum_save);
if (sum != sum_save) {
dbg(ndd, "nsindex%d checksum invalid\n", i);
continue;
}
seq = le32_to_cpu(nsindex[i]->seq);
if ((seq & NSINDEX_SEQ_MASK) == 0) {
dbg(ndd, "nsindex%d sequence: %#x invalid\n", i, seq);
continue;
}
/* sanity check the index against expected values */
if (le64_to_cpu(nsindex[i]->myoff)
!= i * sizeof_namespace_index(ndd)) {
dbg(ndd, "nsindex%d myoff: %#llx invalid\n",
i, (unsigned long long)
le64_to_cpu(nsindex[i]->myoff));
continue;
}
if (le64_to_cpu(nsindex[i]->otheroff)
!= (!i) * sizeof_namespace_index(ndd)) {
dbg(ndd, "nsindex%d otheroff: %#llx invalid\n",
i, (unsigned long long)
le64_to_cpu(nsindex[i]->otheroff));
continue;
}
size = le64_to_cpu(nsindex[i]->mysize);
if (size > sizeof_namespace_index(ndd)
|| size < sizeof(struct namespace_index)) {
dbg(ndd, "nsindex%d mysize: %#zx invalid\n", i, size);
continue;
}
nslot = le32_to_cpu(nsindex[i]->nslot);
if (nslot * sizeof_namespace_label(ndd)
+ 2 * sizeof_namespace_index(ndd)
> ndd->config_size) {
dbg(ndd, "nsindex%d nslot: %u invalid, config_size: %#zx\n",
i, nslot, ndd->config_size);
continue;
}
valid[i] = true;
num_valid++;
}
switch (num_valid) {
case 0:
break;
case 1:
for (i = 0; i < num_index; i++)
if (valid[i])
return i;
/* can't have num_valid > 0 but valid[] = { false, false } */
err(ndd, "unexpected index-block parse error\n");
break;
default:
/* pick the best index... */
seq = best_seq(le32_to_cpu(nsindex[0]->seq),
le32_to_cpu(nsindex[1]->seq));
if (seq == (le32_to_cpu(nsindex[1]->seq) & NSINDEX_SEQ_MASK))
return 1;
else
return 0;
break;
}
return -1;
}
int label_validate(struct nvdimm_data *ndd)
{
/*
* In order to probe for and validate namespace index blocks we
* need to know the size of the labels, and we can't trust the
* size of the labels until we validate the index blocks.
* Resolve this dependency loop by probing for known label
* sizes, but default to v1.2 256-byte namespace labels if
* discovery fails.
*/
int label_size[] = { 128, 256 };
int i, rc;
for (i = 0; (size_t) i < ARRAY_SIZE(label_size); i++) {
ndd->nslabel_size = label_size[i];
rc = __label_validate(ndd);
if (rc >= 0)
return rc;
}
return -1;
}
static int nvdimm_set_config_data(struct nvdimm_data *ndd, size_t offset,
void *buf, size_t len)
{
struct ndctl_cmd *cmd_write;
int rc;
cmd_write = ndctl_dimm_cmd_new_cfg_write(ndd->cmd_read);
if (!cmd_write)
return -ENXIO;
rc = ndctl_cmd_cfg_write_set_data(cmd_write, buf, len, offset);
if (rc < 0)
goto out;
rc = ndctl_cmd_submit(cmd_write);
if (rc || ndctl_cmd_get_firmware_status(cmd_write))
rc = -ENXIO;
out:
ndctl_cmd_unref(cmd_write);
return rc;
}
static int label_next_nsindex(int index)
{
if (index < 0)
return -1;
return (index + 1) % 2;
}
static struct namespace_label *label_base(struct nvdimm_data *ndd)
{
char *base = (char *) to_namespace_index(ndd, 0);
base += 2 * sizeof_namespace_index(ndd);
return (struct namespace_label *) base;
}
static int label_write_index(struct nvdimm_data *ndd, int index, u32 seq)
{
struct namespace_index *nsindex;
unsigned long offset;
u64 checksum;
u32 nslot;
nsindex = to_namespace_index(ndd, index);
nslot = nvdimm_num_label_slots(ndd);
memcpy(nsindex->sig, NSINDEX_SIGNATURE, NSINDEX_SIG_LEN);
memset(nsindex->flags, 0, 3);
nsindex->labelsize = sizeof_namespace_label(ndd) >> 8;
nsindex->seq = cpu_to_le32(seq);
offset = (unsigned long) nsindex
- (unsigned long) to_namespace_index(ndd, 0);
nsindex->myoff = cpu_to_le64(offset);
nsindex->mysize = cpu_to_le64(sizeof_namespace_index(ndd));
offset = (unsigned long) to_namespace_index(ndd,
label_next_nsindex(index))
- (unsigned long) to_namespace_index(ndd, 0);
nsindex->otheroff = cpu_to_le64(offset);
offset = (unsigned long) label_base(ndd)
- (unsigned long) to_namespace_index(ndd, 0);
nsindex->labeloff = cpu_to_le64(offset);
nsindex->nslot = cpu_to_le32(nslot);
nsindex->major = cpu_to_le16(1);
if (sizeof_namespace_label(ndd) < 256)
nsindex->minor = cpu_to_le16(1);
else
nsindex->minor = cpu_to_le16(2);
nsindex->checksum = cpu_to_le64(0);
/* init label bitmap */
memset(nsindex->free, 0xff, ALIGN(nslot, BITS_PER_LONG) / 8);
checksum = fletcher64(nsindex, sizeof_namespace_index(ndd), 1);
nsindex->checksum = cpu_to_le64(checksum);
return nvdimm_set_config_data(ndd, le64_to_cpu(nsindex->myoff),
nsindex, sizeof_namespace_index(ndd));
}
static struct parameters {
const char *bus;
const char *outfile;
const char *infile;
const char *labelversion;
bool force;
bool json;
bool verbose;
} param = {
.labelversion = "1.1",
};
static int __action_init(struct ndctl_dimm *dimm, int version, int chk_only)
{
struct nvdimm_data __ndd = { 0 }, *ndd = &__ndd;
struct ndctl_cmd *cmd_read;
int rc = 0, i;
ssize_t size;
cmd_read = read_labels(dimm);
if (!cmd_read)
return -ENXIO;
size = ndctl_cmd_cfg_read_get_size(cmd_read);
if (size < 0)
return size;
ndd->data = malloc(size);
if (!ndd->data)
return -ENOMEM;
rc = ndctl_cmd_cfg_read_get_data(cmd_read, ndd->data, size, 0);
if (rc < 0)
goto out;
ndd->dimm = dimm;
ndd->cmd_read = cmd_read;
ndd->config_size = size;
ndd->nsindex_size = 0;
ndd->ns_current = -1;
ndd->ns_next = -1;
log_init(&ndd->ctx, ndctl_dimm_get_devname(dimm), "NDCTL_INIT_LABELS");
if (param.verbose)
ndd->ctx.log_priority = LOG_DEBUG;
/*
* If the region goes active after this point, i.e. we're racing
* another administrative action, the kernel will fail writes to
* the label area.
*/
if (!chk_only && ndctl_dimm_is_active(dimm)) {
err(ndd, "regions active, abort label write\n");
rc = -EBUSY;
goto out;
}
rc = label_validate(ndd);
if (chk_only) {
rc = rc >= 0 ? 0 : -ENXIO;
goto out;
}
if (rc >= 0 && !param.force) {
err(ndd, "error: labels already initialized\n");
rc = -EBUSY;
goto out;
}
/*
* We may have initialized ndd to whatever labelsize is
* currently on the dimm during label_validate(), so we reset it
* to the desired version here.
*/
if (version > 1)
ndd->nslabel_size = 256;
else
ndd->nslabel_size = 128;
for (i = 0; i < 2; i++) {
rc = label_write_index(ndd, i, i*2);
if (rc)
goto out;
}
/*
* If the dimm is already disabled the kernel is not holding a cached
* copy of the label space.
*/
if (!ndctl_dimm_is_enabled(dimm))
goto out;
rc = ndctl_dimm_disable(dimm);
if (rc)
goto out;
rc = ndctl_dimm_enable(dimm);
out:
ndctl_cmd_unref(cmd_read);
free(ndd->data);
return rc;
}
static int action_init(struct ndctl_dimm *dimm, struct action_context *actx)
{
return __action_init(dimm, actx->labelversion, 0);
}
static int action_check(struct ndctl_dimm *dimm, struct action_context *actx)
{
return __action_init(dimm, 0, 1);
}
#define BASE_OPTIONS() \
OPT_STRING('b', "bus", &param.bus, "bus-id", \
"<nmem> must be on a bus with an id/provider of <bus-id>"), \
OPT_BOOLEAN('v',"verbose", &param.verbose, "turn on debug")
#define READ_OPTIONS() \
OPT_STRING('o', "output", &param.outfile, "output-file", \
"filename to write label area contents"), \
OPT_BOOLEAN('j', "json", &param.json, "parse label data into json")
#define WRITE_OPTIONS() \
OPT_STRING('i', "input", &param.infile, "input-file", \
"filename to read label area data")
#define INIT_OPTIONS() \
OPT_BOOLEAN('f', "force", &param.force, \
"force initialization even if existing index-block present"), \
OPT_STRING('V', "--label-version", &param.labelversion, "version-number", \
"namespace label specification version (default: 1.1)")
static const struct option read_options[] = {
BASE_OPTIONS(),
READ_OPTIONS(),
OPT_END(),
};
static const struct option write_options[] = {
BASE_OPTIONS(),
WRITE_OPTIONS(),
OPT_END(),
};
static const struct option base_options[] = {
BASE_OPTIONS(),
OPT_END(),
};
static const struct option init_options[] = {
BASE_OPTIONS(),
INIT_OPTIONS(),
OPT_END(),
};
static int dimm_action(int argc, const char **argv, void *ctx,
int (*action)(struct ndctl_dimm *dimm, struct action_context *actx),
const struct option *options, const char *usage)
{
struct action_context actx = { 0 };
int i, rc = 0, count = 0, err = 0;
struct ndctl_dimm *single = NULL;
const char * const u[] = {
usage,
NULL
};
unsigned long id;
argc = parse_options(argc, argv, options, u, 0);
if (argc == 0)
usage_with_options(u, options);
for (i = 0; i < argc; i++) {
if (strcmp(argv[i], "all") == 0) {
argv[0] = "all";
argc = 1;
break;
}
if (sscanf(argv[i], "nmem%lu", &id) != 1) {
fprintf(stderr, "'%s' is not a valid dimm name\n",
argv[i]);
err++;
}
}
if (err == argc) {
usage_with_options(u, options);
return -EINVAL;
}
if (param.json) {
actx.jdimms = json_object_new_array();
if (!actx.jdimms)
return -ENOMEM;
}
if (!param.outfile)
actx.f_out = stdout;
else {
actx.f_out = fopen(param.outfile, "w+");
if (!actx.f_out) {
fprintf(stderr, "failed to open: %s: (%s)\n",
param.outfile, strerror(errno));
rc = -errno;
goto out;
}
}
if (!param.infile)
actx.f_in = stdin;
else {
actx.f_in = fopen(param.infile, "r");
if (!actx.f_in) {
fprintf(stderr, "failed to open: %s: (%s)\n",
param.infile, strerror(errno));
rc = -errno;
goto out;
}
}
if (param.verbose)
ndctl_set_log_priority(ctx, LOG_DEBUG);
if (strcmp(param.labelversion, "1.1") == 0)
actx.labelversion = 1;
else if (strcmp(param.labelversion, "v1.1") == 0)
actx.labelversion = 1;
else if (strcmp(param.labelversion, "1.2") == 0)
actx.labelversion = 2;
else if (strcmp(param.labelversion, "v1.2") == 0)
actx.labelversion = 2;
else {
fprintf(stderr, "'%s' is not a valid label version\n",
param.labelversion);
rc = -EINVAL;
goto out;
}
rc = 0;
err = 0;
count = 0;
for (i = 0; i < argc; i++) {
struct ndctl_dimm *dimm;
struct ndctl_bus *bus;
if (sscanf(argv[i], "nmem%lu", &id) != 1
&& strcmp(argv[i], "all") != 0)
continue;
ndctl_bus_foreach(ctx, bus) {
if (!util_bus_filter(bus, param.bus))
continue;
ndctl_dimm_foreach(bus, dimm) {
if (!util_dimm_filter(dimm, argv[i]))
continue;
if (action == action_write) {
single = dimm;
rc = 0;
} else
rc = action(dimm, &actx);
if (rc == 0)
count++;
else if (rc && !err)
err = rc;
}
}
}
rc = err;
if (action == action_write) {
if (count > 1) {
error("write-labels only supports writing a single dimm\n");
usage_with_options(u, options);
return -EINVAL;
} else if (single)
rc = action(single, &actx);
}
if (actx.jdimms) {
util_display_json_array(actx.f_out, actx.jdimms,
JSON_C_TO_STRING_PRETTY);
json_object_put(actx.jdimms);
}
if (actx.f_out != stdout)
fclose(actx.f_out);
if (actx.f_in != stdin)
fclose(actx.f_in);
out:
/*
* count if some actions succeeded, 0 if none were attempted,
* negative error code otherwise.
*/
if (count > 0)
return count;
return rc;
}
int cmd_write_labels(int argc, const char **argv, void *ctx)
{
int count = dimm_action(argc, argv, ctx, action_write, write_options,
"ndctl write-labels <nmem> [-i <filename>]");
fprintf(stderr, "wrote %d nmem%s\n", count >= 0 ? count : 0,
count > 1 ? "s" : "");
return count >= 0 ? 0 : EXIT_FAILURE;
}
int cmd_read_labels(int argc, const char **argv, void *ctx)
{
int count = dimm_action(argc, argv, ctx, action_read, read_options,
"ndctl read-labels <nmem0> [<nmem1>..<nmemN>] [-o <filename>]");
fprintf(stderr, "read %d nmem%s\n", count >= 0 ? count : 0,
count > 1 ? "s" : "");
return count >= 0 ? 0 : EXIT_FAILURE;
}
int cmd_zero_labels(int argc, const char **argv, void *ctx)
{
int count = dimm_action(argc, argv, ctx, action_zero, base_options,
"ndctl zero-labels <nmem0> [<nmem1>..<nmemN>] [<options>]");
fprintf(stderr, "zeroed %d nmem%s\n", count >= 0 ? count : 0,
count > 1 ? "s" : "");
return count >= 0 ? 0 : EXIT_FAILURE;
}
int cmd_init_labels(int argc, const char **argv, void *ctx)
{
int count = dimm_action(argc, argv, ctx, action_init, init_options,
"ndctl init-labels <nmem0> [<nmem1>..<nmemN>] [<options>]");
fprintf(stderr, "initialized %d nmem%s\n", count >= 0 ? count : 0,
count > 1 ? "s" : "");
return count >= 0 ? 0 : EXIT_FAILURE;
}
int cmd_check_labels(int argc, const char **argv, void *ctx)
{
int count = dimm_action(argc, argv, ctx, action_check, base_options,
"ndctl check-labels <nmem0> [<nmem1>..<nmemN>] [<options>]");
fprintf(stderr, "successfully verified %d nmem%s\n",
count >= 0 ? count : 0, count > 1 ? "s" : "");
return count >= 0 ? 0 : EXIT_FAILURE;
}
int cmd_disable_dimm(int argc, const char **argv, void *ctx)
{
int count = dimm_action(argc, argv, ctx, action_disable, base_options,
"ndctl disable-dimm <nmem0> [<nmem1>..<nmemN>] [<options>]");
fprintf(stderr, "disabled %d nmem%s\n", count >= 0 ? count : 0,
count > 1 ? "s" : "");
return count >= 0 ? 0 : EXIT_FAILURE;
}
int cmd_enable_dimm(int argc, const char **argv, void *ctx)
{
int count = dimm_action(argc, argv, ctx, action_enable, base_options,
"ndctl enable-dimm <nmem0> [<nmem1>..<nmemN>] [<options>]");
fprintf(stderr, "enabled %d nmem%s\n", count >= 0 ? count : 0,
count > 1 ? "s" : "");
return count >= 0 ? 0 : EXIT_FAILURE;
}