Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F111883714
D18735.id.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
9 KB
Referenced Files
None
Subscribers
None
D18735.id.diff
View Options
Index: head/sys/dev/nvdimm/nvdimm.c
===================================================================
--- head/sys/dev/nvdimm/nvdimm.c
+++ head/sys/dev/nvdimm/nvdimm.c
@@ -36,6 +36,7 @@
#include <sys/param.h>
#include <sys/bio.h>
+#include <sys/bitstring.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/lock.h>
@@ -51,10 +52,240 @@
#define _COMPONENT ACPI_OEM
ACPI_MODULE_NAME("NVDIMM")
+static struct uuid intel_nvdimm_dsm_uuid =
+ {0x4309AC30,0x0D11,0x11E4,0x91,0x91,{0x08,0x00,0x20,0x0C,0x9A,0x66}};
+#define INTEL_NVDIMM_DSM_REV 1
+#define INTEL_NVDIMM_DSM_GET_LABEL_SIZE 4
+#define INTEL_NVDIMM_DSM_GET_LABEL_DATA 5
+
static devclass_t nvdimm_devclass;
static devclass_t nvdimm_root_devclass;
MALLOC_DEFINE(M_NVDIMM, "nvdimm", "NVDIMM driver memory");
+static int
+read_label_area_size(struct nvdimm_dev *nv)
+{
+ ACPI_OBJECT *result_buffer;
+ ACPI_HANDLE handle;
+ ACPI_STATUS status;
+ ACPI_BUFFER result;
+ uint32_t *out;
+ int error;
+
+ handle = nvdimm_root_get_acpi_handle(nv->nv_dev);
+ if (handle == NULL)
+ return (ENODEV);
+ result.Length = ACPI_ALLOCATE_BUFFER;
+ result.Pointer = NULL;
+ status = acpi_EvaluateDSM(handle, (uint8_t *)&intel_nvdimm_dsm_uuid,
+ INTEL_NVDIMM_DSM_REV, INTEL_NVDIMM_DSM_GET_LABEL_SIZE, NULL,
+ &result);
+ error = ENXIO;
+ if (ACPI_SUCCESS(status) && result.Pointer != NULL &&
+ result.Length >= sizeof(ACPI_OBJECT)) {
+ result_buffer = result.Pointer;
+ if (result_buffer->Type == ACPI_TYPE_BUFFER &&
+ result_buffer->Buffer.Length >= 12) {
+ out = (uint32_t *)result_buffer->Buffer.Pointer;
+ nv->label_area_size = out[1];
+ nv->max_label_xfer = out[2];
+ error = 0;
+ }
+ }
+ if (result.Pointer != NULL)
+ AcpiOsFree(result.Pointer);
+ return (error);
+}
+
+static int
+read_label_area(struct nvdimm_dev *nv, uint8_t *dest, off_t offset,
+ off_t length)
+{
+ ACPI_BUFFER result;
+ ACPI_HANDLE handle;
+ ACPI_OBJECT params_pkg, params_buf, *result_buf;
+ ACPI_STATUS status;
+ uint32_t params[2];
+ off_t to_read;
+ int error;
+
+ error = 0;
+ handle = nvdimm_root_get_acpi_handle(nv->nv_dev);
+ if (offset < 0 || length <= 0 ||
+ offset + length > nv->label_area_size ||
+ handle == NULL)
+ return (ENODEV);
+ params_pkg.Type = ACPI_TYPE_PACKAGE;
+ params_pkg.Package.Count = 1;
+ params_pkg.Package.Elements = ¶ms_buf;
+ params_buf.Type = ACPI_TYPE_BUFFER;
+ params_buf.Buffer.Length = sizeof(params);
+ params_buf.Buffer.Pointer = (UINT8 *)params;
+ while (length > 0) {
+ to_read = MIN(length, nv->max_label_xfer);
+ params[0] = offset;
+ params[1] = to_read;
+ result.Length = ACPI_ALLOCATE_BUFFER;
+ result.Pointer = NULL;
+ status = acpi_EvaluateDSM(handle,
+ (uint8_t *)&intel_nvdimm_dsm_uuid, INTEL_NVDIMM_DSM_REV,
+ INTEL_NVDIMM_DSM_GET_LABEL_DATA, ¶ms_pkg, &result);
+ if (ACPI_FAILURE(status) ||
+ result.Length < sizeof(ACPI_OBJECT) ||
+ result.Pointer == NULL) {
+ error = ENXIO;
+ break;
+ }
+ result_buf = (ACPI_OBJECT *)result.Pointer;
+ if (result_buf->Type != ACPI_TYPE_BUFFER ||
+ result_buf->Buffer.Pointer == NULL ||
+ result_buf->Buffer.Length != 4 + to_read ||
+ ((uint16_t *)result_buf->Buffer.Pointer)[0] != 0) {
+ error = ENXIO;
+ break;
+ }
+ bcopy(result_buf->Buffer.Pointer + 4, dest, to_read);
+ dest += to_read;
+ offset += to_read;
+ length -= to_read;
+ if (result.Pointer != NULL) {
+ AcpiOsFree(result.Pointer);
+ result.Pointer = NULL;
+ }
+ }
+ if (result.Pointer != NULL)
+ AcpiOsFree(result.Pointer);
+ return (error);
+}
+
+static uint64_t
+fletcher64(const void *data, size_t length)
+{
+ size_t i;
+ uint32_t a, b;
+ const uint32_t *d;
+
+ a = 0;
+ b = 0;
+ d = (const uint32_t *)data;
+ length = length / sizeof(uint32_t);
+ for (i = 0; i < length; i++) {
+ a += d[i];
+ b += a;
+ }
+ return ((uint64_t)b << 32 | a);
+}
+
+static bool
+label_index_is_valid(struct nvdimm_label_index *index, uint32_t max_labels,
+ size_t size, size_t offset)
+{
+ uint64_t checksum;
+
+ index = (struct nvdimm_label_index *)((uint8_t *)index + offset);
+ if (strcmp(index->signature, NVDIMM_INDEX_BLOCK_SIGNATURE) != 0)
+ return false;
+ checksum = index->checksum;
+ index->checksum = 0;
+ if (checksum != fletcher64(index, size) ||
+ index->this_offset != size * offset || index->this_size != size ||
+ index->other_offset != size * (offset == 0 ? 1 : 0) ||
+ index->seq == 0 || index->seq > 3 || index->slot_cnt > max_labels ||
+ index->label_size != 1)
+ return false;
+ return true;
+}
+
+static int
+read_label(struct nvdimm_dev *nv, int num)
+{
+ struct nvdimm_label_entry *entry, *i, *next;
+ uint64_t checksum;
+ off_t offset;
+ int error;
+
+ offset = nv->label_index->label_offset +
+ num * (128 << nv->label_index->label_size);
+ entry = malloc(sizeof(*entry), M_NVDIMM, M_WAITOK);
+ error = read_label_area(nv, (uint8_t *)&entry->label, offset,
+ sizeof(struct nvdimm_label));
+ if (error != 0) {
+ free(entry, M_NVDIMM);
+ return (error);
+ }
+ checksum = entry->label.checksum;
+ entry->label.checksum = 0;
+ if (checksum != fletcher64(&entry->label, sizeof(entry->label)) ||
+ entry->label.slot != num) {
+ free(entry, M_NVDIMM);
+ return (ENXIO);
+ }
+
+ /* Insertion ordered by dimm_phys_addr */
+ if (SLIST_EMPTY(&nv->labels) ||
+ entry->label.dimm_phys_addr <=
+ SLIST_FIRST(&nv->labels)->label.dimm_phys_addr) {
+ SLIST_INSERT_HEAD(&nv->labels, entry, link);
+ return (0);
+ }
+ SLIST_FOREACH_SAFE(i, &nv->labels, link, next) {
+ if (next == NULL ||
+ entry->label.dimm_phys_addr <= next->label.dimm_phys_addr) {
+ SLIST_INSERT_AFTER(i, entry, link);
+ return (0);
+ }
+ }
+ __unreachable();
+}
+
+static int
+read_labels(struct nvdimm_dev *nv)
+{
+ struct nvdimm_label_index *indices;
+ size_t bitfield_size, index_size, num_labels;
+ int error, n;
+ bool index_0_valid, index_1_valid;
+
+ for (index_size = 256; ; index_size += 256) {
+ num_labels = 8 * (index_size -
+ sizeof(struct nvdimm_label_index));
+ if (index_size + num_labels * sizeof(struct nvdimm_label) >=
+ nv->label_area_size)
+ break;
+ }
+ num_labels = (nv->label_area_size - index_size) /
+ sizeof(struct nvdimm_label);
+ bitfield_size = roundup2(num_labels, 8) / 8;
+ indices = malloc(2 * index_size, M_NVDIMM, M_WAITOK);
+ error = read_label_area(nv, (void *)indices, 0, 2 * index_size);
+ if (error != 0) {
+ free(indices, M_NVDIMM);
+ return (error);
+ }
+ index_0_valid = label_index_is_valid(indices, num_labels, index_size,
+ 0);
+ index_1_valid = label_index_is_valid(indices, num_labels, index_size,
+ 1);
+ if (!index_0_valid && !index_1_valid) {
+ free(indices, M_NVDIMM);
+ return (ENXIO);
+ }
+ if (index_0_valid && index_1_valid &&
+ (indices[1].seq > indices[0].seq ||
+ (indices[1].seq == 1 && indices[0].seq == 3)))
+ index_0_valid = false;
+ nv->label_index = malloc(index_size, M_NVDIMM, M_WAITOK);
+ bcopy(indices + (index_0_valid ? 0 : 1), nv->label_index, index_size);
+ free(indices, M_NVDIMM);
+ for (bit_ffc_at((bitstr_t *)nv->label_index->free, 0, num_labels, &n);
+ n >= 0;
+ bit_ffc_at((bitstr_t *)nv->label_index->free, n + 1, num_labels,
+ &n)) {
+ read_label(nv, n);
+ }
+ return (0);
+}
+
struct nvdimm_dev *
nvdimm_find_by_handle(nfit_handle_t nv_handle)
{
@@ -90,6 +321,7 @@
ACPI_TABLE_NFIT *nfitbl;
ACPI_HANDLE handle;
ACPI_STATUS status;
+ int error;
nv = device_get_softc(dev);
handle = nvdimm_root_get_acpi_handle(dev);
@@ -107,6 +339,14 @@
acpi_nfit_get_flush_addrs(nfitbl, nv->nv_handle, &nv->nv_flush_addr,
&nv->nv_flush_addr_cnt);
AcpiPutTable(&nfitbl->Header);
+ error = read_label_area_size(nv);
+ if (error == 0) {
+ /*
+ * Ignoring errors reading labels. Not all NVDIMMs
+ * support labels and namespaces.
+ */
+ read_labels(nv);
+ }
return (0);
}
@@ -114,9 +354,15 @@
nvdimm_detach(device_t dev)
{
struct nvdimm_dev *nv;
+ struct nvdimm_label_entry *label, *next;
nv = device_get_softc(dev);
free(nv->nv_flush_addr, M_NVDIMM);
+ free(nv->label_index, M_NVDIMM);
+ SLIST_FOREACH_SAFE(label, &nv->labels, link, next) {
+ SLIST_REMOVE_HEAD(&nv->labels, link);
+ free(label, M_NVDIMM);
+ }
return (0);
}
Index: head/sys/dev/nvdimm/nvdimm_var.h
===================================================================
--- head/sys/dev/nvdimm/nvdimm_var.h
+++ head/sys/dev/nvdimm/nvdimm_var.h
@@ -33,6 +33,51 @@
#ifndef __DEV_NVDIMM_VAR_H__
#define __DEV_NVDIMM_VAR_H__
+#define NVDIMM_INDEX_BLOCK_SIGNATURE "NAMESPACE_INDEX"
+
+struct nvdimm_label_index {
+ char signature[16];
+ uint8_t flags[3];
+ uint8_t label_size;
+ uint32_t seq;
+ uint64_t this_offset;
+ uint64_t this_size;
+ uint64_t other_offset;
+ uint64_t label_offset;
+ uint32_t slot_cnt;
+ uint16_t rev_major;
+ uint16_t rev_minor;
+ uint64_t checksum;
+ uint8_t free[0];
+};
+
+struct nvdimm_label {
+ struct uuid uuid;
+ char name[64];
+ uint32_t flags;
+ uint16_t nlabel;
+ uint16_t position;
+ uint64_t set_cookie;
+ uint64_t lba_size;
+ uint64_t dimm_phys_addr;
+ uint64_t raw_size;
+ uint32_t slot;
+ uint8_t alignment;
+ uint8_t reserved[3];
+ struct uuid type_guid;
+ struct uuid address_abstraction_guid;
+ uint8_t reserved1[88];
+ uint64_t checksum;
+};
+
+struct nvdimm_label_entry {
+ SLIST_ENTRY(nvdimm_label_entry) link;
+ struct nvdimm_label label;
+};
+
+_Static_assert(sizeof(struct nvdimm_label_index) == 72, "Incorrect layout");
+_Static_assert(sizeof(struct nvdimm_label) == 256, "Incorrect layout");
+
typedef uint32_t nfit_handle_t;
enum nvdimm_root_ivar {
@@ -53,6 +98,10 @@
nfit_handle_t nv_handle;
uint64_t **nv_flush_addr;
int nv_flush_addr_cnt;
+ uint32_t label_area_size;
+ uint32_t max_label_xfer;
+ struct nvdimm_label_index *label_index;
+ SLIST_HEAD(, nvdimm_label_entry) labels;
};
enum SPA_mapping_type {
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Mar 10, 7:06 PM (13 h, 55 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
17088182
Default Alt Text
D18735.id.diff (9 KB)
Attached To
Mode
D18735: nvdimm: Read NVDIMM namespace labels
Attached
Detach File
Event Timeline
Log In to Comment