Define and add a new uapi header for papr_scm describing device
specific methods (DSMs) and structs for libndctl. PAPR-SCM specific
implementation in libndctl will use these commands/structs to interact
with papr_scm kernel module. Currently only DSMs to retrieve health and
performance statistics information of a dimm are defined.

DSM Envelope
=============

The ioctl ND_CMD_CALL transfers data between user-space and kernel via
'envelopes'. An envelope consists of a header and user-defined payload
section. The primary structure describing this envelope is 'struct
nd_papr_scm_cmd_pkg' which expects a payload at the end of the envelop
pointed to by 'nd_papr_scm_cmd_pkg.payload_offset'. Currently two
payloads are defined 'struct nd_papr_scm_dimm_health_stat' and 'struct
nd_papr_scm_perf_stats'. These can be used to retrieve dimm-health and
performance stats respectively.

The header is defined as 'struct nd_cmd_pkg' which in return is
wrapped in a user defined struct called 'struct
nd_papr_scm_cmd_pkg'. This relationship is illustrated below:

     64-Bytes         8-Bytes
 +-------------+-------------------+------------------------------+
 | nd_family   |                   |                              |
 |             |                   |                              |
 | nd_size_out | cmd_status        |                              |
 |             |                   |                              |
 | nd_size_in  | payload_version   |           PAYLOAD            |
 |             |                   |                              |
 | nd_command  | payload_offset    |                              |
 |             |      |            |                              |
 | nd_fw_size  |      +----------> |                              |
 +-------------+-------------------+------------------------------+
 \ nd_cmd_pkg /                   /                              /
  \----------/                   /                              /
   \    nd_papr_scm_cmd_pkg     /                              /
    \--------------------------/                              /
     \                            Envelope                   /
      \-----------------------------------------------------/

Important fields to note in above illustration are:

* 'nd_command'  : DSM command sent by libndctl
* 'nd_family'   : Id for newly introduced DSM family NVDIMM_FAMILY_PAPR_SCM
* 'nd_fw_size'  : Number of bytes that kernel wanted to copy to the
  payload but may not have copied due to limited size of the envelope.
* 'nd_size_in/out' : Number of bytes that kernel needs to copy from
  user-space (in) and copy-back to user-space (out).
* 'cmd_status'  : Out variable indicating any error encountered while
  servicing the DSM.
* 'payload_version': Version number associated with the payload.
* 'payload_offset': Offset of the payload from start of the envelope.

libnvdimm enforces a hard limit of 256 bytes on the envelope size,
which leaves around 184 bytes for the envelope payload (ignoring any
padding that the compiler may silently introduce).

Envelope Payload Layout
=======================

The layout of the DSM Payload is defined by various structs defined in
'papr_scm_dsm.h'. Definition of these structs are shared between
papr_scm and libndctl so that contents of payload can be
interpreted. This patch-set introduces two such structs namely
'nd_papr_scm_dimm_health_stat' and 'nd_papr_scm_perf_stats' that can
be used to exchange dimm health and performance stats between papr_scm
and libndctl.

During servicing of a DSM the papr_scm module will read input args
from the payload field by casting its contents to an appropriate
struct pointer based on the DSM command. Similarly the output of
servicing the DSM command will be copied to the payload field using
the same struct.

Payload Version
===============

Since the structs associated with each DSM can evolve over time adding
more data and the definitions of these structs known to papr_scm and
libndctl may differ, hence the version number is associated with each
iteration of the struct. This version number is exchanged between
papr_scm <-> libndctl via the 'payload_version' of the DSM envelope.

When libndctl sends an envelope to papr_scm it populates the
'payload_version' field with the version number of the struct it had
copied and/or expects in the payload area. The papr_scm module when
servicing the DSM envelop checks the 'payload_version', if required
changes it to a different version number that it knows about and then
use the DSM struct associated with the new version number to process
the DSM (i.e read the args and copy the results to the payload
area). Libndctl on receiving the envelop back from papr_scm again
checks the 'payload_version' field and based on it use the appropriate
version dsm struct to parse the results.

Above scheme of exchanging different versioned DSM struct between
libndctl and papr_scm should work until following two assumptions
hold:

Let T(X) = { set of attributes of DSM struct 'T' versioned X }

1. T(X) is a proper subset of T(Y) if X > Y.
   i.e Each new version of DSM struct should retain existing struct
   attributes.

2. If an entity (libndctl or papr_scm) supports a DSM struct T(X) then
   it should also support T(1), T(2)...T(X - 1).
   i.e When adding support for new version of a DSM struct, libndctl
   and papr_scm should retain support of the existing DSM struct
   version they support.

Signed-off-by: Vaibhav Jain <vaib...@linux.ibm.com>
---
 arch/powerpc/include/uapi/asm/papr_scm_dsm.h | 143 +++++++++++++++++++
 1 file changed, 143 insertions(+)
 create mode 100644 arch/powerpc/include/uapi/asm/papr_scm_dsm.h

diff --git a/arch/powerpc/include/uapi/asm/papr_scm_dsm.h 
b/arch/powerpc/include/uapi/asm/papr_scm_dsm.h
new file mode 100644
index 000000000000..aacced453579
--- /dev/null
+++ b/arch/powerpc/include/uapi/asm/papr_scm_dsm.h
@@ -0,0 +1,143 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+/*
+ * PAPR SCM Device specific methods for libndctl and ndctl
+ *
+ * (C) Copyright IBM 2020
+ *
+ * Author: Vaibhav Jain <vaibhav at linux.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _UAPI_ASM_POWERPC_PAPR_SCM_DSM_H_
+#define _UAPI_ASM_POWERPC_PAPR_SCM_DSM_H_
+
+#include <linux/types.h>
+
+#ifdef __KERNEL__
+#include <linux/ndctl.h>
+#else
+#include <ndctl.h>
+#endif
+
+/*
+ * Sub commands for ND_CMD_CALL. To prevent overlap from ND_CMD_*, values for
+ * these enums start at 0x10000. These values are then returned from
+ * cmd_to_func() making it easy to implement the switch-case block in
+ * papr_scm_ndctl()
+ */
+enum dsm_papr_scm {
+       DSM_PAPR_SCM_MIN =  0x10000,
+       DSM_PAPR_SCM_HEALTH,
+       DSM_PAPR_SCM_STATS,
+       DSM_PAPR_SCM_MAX,
+};
+
+enum dsm_papr_scm_dimm_health {
+       DSM_PAPR_SCM_DIMM_HEALTHY,
+       DSM_PAPR_SCM_DIMM_UNHEALTHY,
+       DSM_PAPR_SCM_DIMM_CRITICAL,
+       DSM_PAPR_SCM_DIMM_FATAL,
+};
+
+/* Papr-scm-header + payload expected with ND_CMD_CALL ioctl from libnvdimm */
+struct nd_papr_scm_cmd_pkg {
+       struct nd_cmd_pkg hdr;          /* Package header containing sub-cmd */
+       int32_t cmd_status;             /* Out: Sub-cmd status returned back */
+       uint16_t payload_offset;        /* In: offset from start of struct */
+       uint16_t payload_version;       /* In/Out: version of the payload */
+       uint8_t payload[];              /* In/Out: Sub-cmd data buffer */
+};
+
+/* Helpers to evaluate the size of PAPR_SCM envelope */
+/* Calculate the papr_scm-header size */
+#define ND_PAPR_SCM_ENVELOPE_CONTENT_HDR_SIZE \
+       (sizeof(struct nd_papr_scm_cmd_pkg) - sizeof(struct nd_cmd_pkg))
+/*
+ * Given a type calculate the envelope size
+ * (nd-header + papr_scm-header + payload)
+ */
+#define ND_PAPR_SCM_ENVELOPE_SIZE(_type_)      \
+       (sizeof(_type_) + sizeof(struct nd_papr_scm_cmd_pkg))
+
+/* Given a type envelope-content size (papr_scm-header + payload) */
+#define ND_PAPR_SCM_ENVELOPE_CONTENT_SIZE(_type_)      \
+       (sizeof(_type_) + ND_PAPR_SCM_ENVELOPE_CONTENT_HDR_SIZE)
+
+/*
+ * Struct exchanged between kernel & ndctl in for PAPR_DSM_PAPR_SMART_HEALTH
+ * Various bitflags indicate the health status of the dimm.
+ */
+struct nd_papr_scm_dimm_health_stat_v1 {
+       /* Dimm not armed. So contents wont persist */
+       bool dimm_unarmed;
+       /* Previous shutdown did not persist contents */
+       bool dimm_bad_shutdown;
+       /* Contents from previous shutdown werent restored */
+       bool dimm_bad_restore;
+       /* Contents of the dimm have been scrubbed */
+       bool dimm_scrubbed;
+       /* Contents of the dimm cant be modified until CEC reboot */
+       bool dimm_locked;
+       /* Contents of dimm are encrypted */
+       bool dimm_encrypted;
+
+       enum dsm_papr_scm_dimm_health dimm_health;
+};
+
+/*
+ * Typedef the current struct for dimm_health so that any application
+ * or kernel recompiled after introducing a new version autometically
+ * supports the new version.
+ */
+#define nd_papr_scm_dimm_health_stat nd_papr_scm_dimm_health_stat_v1
+
+/* Current version number for the dimm health struct */
+#define ND_PAPR_SCM_DIMM_HEALTH_VERSION 1
+
+/* Struct holding a single performance metric */
+struct nd_papr_scm_perf_stat {
+       u64 statistic_id;
+       u64 statistic_value;
+};
+
+/* Struct exchanged between kernel and ndctl reporting drc perf stats */
+struct nd_papr_scm_perf_stats_v1 {
+       /* Number of stats following */
+       u32 num_statistics;
+
+       /* zero or more performance matrics */
+       struct nd_papr_scm_perf_stat scm_statistics[];
+};
+
+/*
+ * Typedef the current struct for dimm_stats so that any application
+ * or kernel recompiled after introducing a new version autometically
+ * supports the new version.
+ */
+#define nd_papr_scm_perf_stats nd_papr_scm_perf_stats_v1
+#define ND_PAPR_SCM_DIMM_PERF_STATS_VERSION 1
+
+/* Convert a libnvdimm nd_cmd_pkg to papr_scm specific pkg */
+static struct nd_papr_scm_cmd_pkg *nd_to_papr_cmd_pkg(struct nd_cmd_pkg *cmd)
+{
+       return (struct nd_papr_scm_cmd_pkg *) cmd;
+}
+
+/* Return the payload pointer for a given pcmd */
+static void *papr_scm_pcmd_to_payload(struct nd_papr_scm_cmd_pkg *pcmd)
+{
+       if (pcmd->hdr.nd_size_in == 0 && pcmd->hdr.nd_size_out == 0)
+               return NULL;
+       else
+               return (void *)((u8 *) pcmd + pcmd->payload_offset);
+}
+#endif /* _UAPI_ASM_POWERPC_PAPR_SCM_DSM_H_ */
-- 
2.24.1

Reply via email to