static void nvdimm_release(struct device *dev) { struct nvdimm *nvdimm = to_nvdimm(dev); ida_simple_remove(&dimm_ida, nvdimm->id); kfree(nvdimm); }
int nvdimm_init_config_data(struct nvdimm_drvdata *ndd) { struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(ndd->dev); struct nd_cmd_get_config_data_hdr *cmd; struct nvdimm_bus_descriptor *nd_desc; int rc = validate_dimm(ndd); u32 max_cmd_size, config_size; size_t offset; if (rc) return rc; if (ndd->data) return 0; if (ndd->nsarea.status || ndd->nsarea.max_xfer == 0 || ndd->nsarea.config_size < ND_LABEL_MIN_SIZE) { dev_dbg(ndd->dev, "failed to init config data area: (%d:%d)\n", ndd->nsarea.max_xfer, ndd->nsarea.config_size); return -ENXIO; } ndd->data = kmalloc(ndd->nsarea.config_size, GFP_KERNEL); if (!ndd->data) ndd->data = vmalloc(ndd->nsarea.config_size); if (!ndd->data) return -ENOMEM; max_cmd_size = min_t(u32, PAGE_SIZE, ndd->nsarea.max_xfer); cmd = kzalloc(max_cmd_size + sizeof(*cmd), GFP_KERNEL); if (!cmd) return -ENOMEM; nd_desc = nvdimm_bus->nd_desc; for (config_size = ndd->nsarea.config_size, offset = 0; config_size; config_size -= cmd->in_length, offset += cmd->in_length) { cmd->in_length = min(config_size, max_cmd_size); cmd->in_offset = offset; rc = nd_desc->ndctl(nd_desc, to_nvdimm(ndd->dev), ND_CMD_GET_CONFIG_DATA, cmd, cmd->in_length + sizeof(*cmd), NULL); if (rc || cmd->status) { rc = -ENXIO; break; } memcpy(ndd->data + offset, cmd->out_buf, cmd->in_length); } dev_dbg(ndd->dev, "%s: len: %zu rc: %d\n", __func__, offset, rc); kfree(cmd); return rc; }
static ssize_t state_show(struct device *dev, struct device_attribute *attr, char *buf) { struct nvdimm *nvdimm = to_nvdimm(dev); /* * The state may be in the process of changing, userspace should * quiesce probing if it wants a static answer */ nvdimm_bus_lock(dev); nvdimm_bus_unlock(dev); return sprintf(buf, "%s\n", atomic_read(&nvdimm->busy) ? "active" : "idle"); }
/* * Retrieve bus and dimm handle and return if this bus supports * get_config_data commands */ int nvdimm_check_config_data(struct device *dev) { struct nvdimm *nvdimm = to_nvdimm(dev); if (!nvdimm->cmd_mask || !test_bit(ND_CMD_GET_CONFIG_DATA, &nvdimm->cmd_mask)) { if (nvdimm->flags & NDD_ALIASING) return -ENXIO; else return -ENOTTY; } return 0; }
static ssize_t commands_show(struct device *dev, struct device_attribute *attr, char *buf) { struct nvdimm *nvdimm = to_nvdimm(dev); int cmd, len = 0; if (!nvdimm->cmd_mask) return sprintf(buf, "\n"); for_each_set_bit(cmd, &nvdimm->cmd_mask, BITS_PER_LONG) len += sprintf(buf + len, "%s ", nvdimm_cmd_name(cmd)); len += sprintf(buf + len, "\n"); return len; }
int nvdimm_set_config_data(struct nvdimm_drvdata *ndd, size_t offset, void *buf, size_t len) { int rc = validate_dimm(ndd); size_t max_cmd_size, buf_offset; struct nd_cmd_set_config_hdr *cmd; struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(ndd->dev); struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc; if (rc) return rc; if (!ndd->data) return -ENXIO; if (offset + len > ndd->nsarea.config_size) return -ENXIO; max_cmd_size = min_t(u32, PAGE_SIZE, len); max_cmd_size = min_t(u32, max_cmd_size, ndd->nsarea.max_xfer); cmd = kzalloc(max_cmd_size + sizeof(*cmd) + sizeof(u32), GFP_KERNEL); if (!cmd) return -ENOMEM; for (buf_offset = 0; len; len -= cmd->in_length, buf_offset += cmd->in_length) { size_t cmd_size; u32 *status; cmd->in_offset = offset + buf_offset; cmd->in_length = min(max_cmd_size, len); memcpy(cmd->in_buf, buf + buf_offset, cmd->in_length); /* status is output in the last 4-bytes of the command buffer */ cmd_size = sizeof(*cmd) + cmd->in_length + sizeof(u32); status = ((void *) cmd) + cmd_size - sizeof(u32); rc = nd_desc->ndctl(nd_desc, to_nvdimm(ndd->dev), ND_CMD_SET_CONFIG_DATA, cmd, cmd_size, NULL); if (rc || *status) { rc = rc ? rc : -ENXIO; break; } } kfree(cmd); return rc; }
/* * Retrieve bus and dimm handle and return if this bus supports * get_config_data commands */ static int __validate_dimm(struct nvdimm_drvdata *ndd) { struct nvdimm *nvdimm; if (!ndd) return -EINVAL; nvdimm = to_nvdimm(ndd->dev); if (!nvdimm->dsm_mask) return -ENXIO; if (!test_bit(ND_CMD_GET_CONFIG_DATA, nvdimm->dsm_mask)) return -ENXIO; return 0; }
/** * nvdimm_init_nsarea - determine the geometry of a dimm's namespace area * @nvdimm: dimm to initialize */ int nvdimm_init_nsarea(struct nvdimm_drvdata *ndd) { struct nd_cmd_get_config_size *cmd = &ndd->nsarea; struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(ndd->dev); struct nvdimm_bus_descriptor *nd_desc; int rc = validate_dimm(ndd); if (rc) return rc; if (cmd->config_size) return 0; /* already valid */ memset(cmd, 0, sizeof(*cmd)); nd_desc = nvdimm_bus->nd_desc; return nd_desc->ndctl(nd_desc, to_nvdimm(ndd->dev), ND_CMD_GET_CONFIG_SIZE, cmd, sizeof(*cmd), NULL); }
void nvdimm_set_aliasing(struct device *dev) { struct nvdimm *nvdimm = to_nvdimm(dev); nvdimm->flags |= NDD_ALIASING; }