blob: dbc1ff06737eb0ba6b0fab4d31051bb4ba47c9d1 [file] [log] [blame]
/*
* Copyright (C) 2016 Hewlett Packard Enterprise Development LP
* 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 <stdlib.h>
#include <limits.h>
#include <util/log.h>
#include <ndctl/libndctl.h>
#include "private.h"
#include "hpe1.h"
#define CMD_HPE1(_c) ((_c)->hpe1)
#define CMD_HPE1_SMART(_c) (CMD_HPE1(_c)->u.smart.data)
#define CMD_HPE1_SMART_THRESH(_c) (CMD_HPE1(_c)->u.thresh.data)
static struct ndctl_cmd *hpe1_dimm_cmd_new_smart(struct ndctl_dimm *dimm)
{
struct ndctl_bus *bus = ndctl_dimm_get_bus(dimm);
struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus);
struct ndctl_cmd *cmd;
size_t size;
struct ndn_pkg_hpe1 *hpe1;
if (!ndctl_dimm_is_cmd_supported(dimm, ND_CMD_CALL)) {
dbg(ctx, "unsupported cmd\n");
return NULL;
}
if (test_dimm_dsm(dimm, NDN_HPE1_CMD_SMART)
== DIMM_DSM_UNSUPPORTED) {
dbg(ctx, "unsupported function\n");
return NULL;
}
size = sizeof(*cmd) + sizeof(struct ndn_pkg_hpe1);
cmd = calloc(1, size);
if (!cmd)
return NULL;
cmd->dimm = dimm;
ndctl_cmd_ref(cmd);
cmd->type = ND_CMD_CALL;
cmd->size = size;
cmd->status = 1;
hpe1 = CMD_HPE1(cmd);
hpe1->gen.nd_family = NVDIMM_FAMILY_HPE1;
hpe1->gen.nd_command = NDN_HPE1_CMD_SMART;
hpe1->gen.nd_fw_size = 0;
hpe1->gen.nd_size_in = offsetof(struct ndn_hpe1_smart, status);
hpe1->gen.nd_size_out = sizeof(hpe1->u.smart);
hpe1->u.smart.status = 3;
hpe1->u.smart.in_valid_flags = 0;
hpe1->u.smart.in_valid_flags |= NDN_HPE1_SMART_HEALTH_VALID;
hpe1->u.smart.in_valid_flags |= NDN_HPE1_SMART_TEMP_VALID;
hpe1->u.smart.in_valid_flags |= NDN_HPE1_SMART_SPARES_VALID;
hpe1->u.smart.in_valid_flags |= NDN_HPE1_SMART_ALARM_VALID;
hpe1->u.smart.in_valid_flags |= NDN_HPE1_SMART_USED_VALID;
hpe1->u.smart.in_valid_flags |= NDN_HPE1_SMART_SHUTDOWN_VALID;
hpe1->u.smart.in_valid_flags |= NDN_HPE1_SMART_VENDOR_VALID;
cmd->firmware_status = &hpe1->u.smart.status;
return cmd;
}
static int hpe1_smart_valid(struct ndctl_cmd *cmd)
{
if (cmd->type != ND_CMD_CALL ||
cmd->size != sizeof(*cmd) + sizeof(struct ndn_pkg_hpe1) ||
CMD_HPE1(cmd)->gen.nd_family != NVDIMM_FAMILY_HPE1 ||
CMD_HPE1(cmd)->gen.nd_command != NDN_HPE1_CMD_SMART ||
cmd->status != 0)
return cmd->status < 0 ? cmd->status : -EINVAL;
return 0;
}
static unsigned int hpe1_cmd_smart_get_flags(struct ndctl_cmd *cmd)
{
unsigned int hpe1flags;
unsigned int flags;
if (hpe1_smart_valid(cmd) < 0)
return UINT_MAX;
hpe1flags = CMD_HPE1_SMART(cmd)->out_valid_flags;
flags = 0;
if (hpe1flags & NDN_HPE1_SMART_HEALTH_VALID)
flags |= ND_SMART_HEALTH_VALID;
if (hpe1flags & NDN_HPE1_SMART_TEMP_VALID)
flags |= ND_SMART_TEMP_VALID ;
if (hpe1flags & NDN_HPE1_SMART_SPARES_VALID)
flags |= ND_SMART_SPARES_VALID;
if (hpe1flags & NDN_HPE1_SMART_ALARM_VALID)
flags |= ND_SMART_ALARM_VALID;
if (hpe1flags & NDN_HPE1_SMART_USED_VALID)
flags |= ND_SMART_USED_VALID;
if (hpe1flags & NDN_HPE1_SMART_SHUTDOWN_VALID)
flags |= ND_SMART_SHUTDOWN_VALID;
if (hpe1flags & NDN_HPE1_SMART_VENDOR_VALID)
flags |= ND_SMART_VENDOR_VALID;
return flags;
}
static unsigned int hpe1_cmd_smart_get_health(struct ndctl_cmd *cmd)
{
unsigned char hpe1health;
unsigned int health;
if (hpe1_smart_valid(cmd) < 0)
return UINT_MAX;
hpe1health = CMD_HPE1_SMART(cmd)->stat_summary;
health = 0;
if (hpe1health & NDN_HPE1_SMART_NONCRIT_HEALTH)
health |= ND_SMART_NON_CRITICAL_HEALTH;;
if (hpe1health & NDN_HPE1_SMART_CRITICAL_HEALTH)
health |= ND_SMART_CRITICAL_HEALTH;
if (hpe1health & NDN_HPE1_SMART_FATAL_HEALTH)
health |= ND_SMART_FATAL_HEALTH;
return health;
}
static unsigned int hpe1_cmd_smart_get_media_temperature(struct ndctl_cmd *cmd)
{
if (hpe1_smart_valid(cmd) < 0)
return UINT_MAX;
return CMD_HPE1_SMART(cmd)->curr_temp;
}
static unsigned int hpe1_cmd_smart_get_spares(struct ndctl_cmd *cmd)
{
if (hpe1_smart_valid(cmd) < 0)
return UINT_MAX;
return CMD_HPE1_SMART(cmd)->spare_blocks;
}
static unsigned int hpe1_cmd_smart_get_alarm_flags(struct ndctl_cmd *cmd)
{
unsigned int hpe1flags;
unsigned int flags;
if (hpe1_smart_valid(cmd) < 0)
return UINT_MAX;
hpe1flags = CMD_HPE1_SMART(cmd)->alarm_trips;
flags = 0;
if (hpe1flags & NDN_HPE1_SMART_TEMP_TRIP)
flags |= ND_SMART_TEMP_TRIP;
if (hpe1flags & NDN_HPE1_SMART_SPARE_TRIP)
flags |= ND_SMART_SPARE_TRIP;
return flags;
}
static unsigned int hpe1_cmd_smart_get_life_used(struct ndctl_cmd *cmd)
{
if (hpe1_smart_valid(cmd) < 0)
return UINT_MAX;
return CMD_HPE1_SMART(cmd)->device_life;
}
static unsigned int hpe1_cmd_smart_get_shutdown_state(struct ndctl_cmd *cmd)
{
unsigned int shutdown;
if (hpe1_smart_valid(cmd) < 0)
return UINT_MAX;
shutdown = CMD_HPE1_SMART(cmd)->last_shutdown_stat;
if (shutdown == NDN_HPE1_SMART_LASTSAVEGOOD)
return 0;
else
return 1;
}
static unsigned int hpe1_cmd_smart_get_vendor_size(struct ndctl_cmd *cmd)
{
if (hpe1_smart_valid(cmd) < 0)
return UINT_MAX;
return CMD_HPE1_SMART(cmd)->vndr_spec_data_size;
}
static unsigned char *hpe1_cmd_smart_get_vendor_data(struct ndctl_cmd *cmd)
{
if (hpe1_smart_valid(cmd) < 0)
return NULL;
return CMD_HPE1_SMART(cmd)->vnd_spec_data;
}
static struct ndctl_cmd *hpe1_dimm_cmd_new_smart_threshold(struct ndctl_dimm *dimm)
{
struct ndctl_bus *bus = ndctl_dimm_get_bus(dimm);
struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus);
struct ndctl_cmd *cmd;
size_t size;
struct ndn_pkg_hpe1 *hpe1;
if (!ndctl_dimm_is_cmd_supported(dimm, ND_CMD_CALL)) {
dbg(ctx, "unsupported cmd\n");
return NULL;
}
if (test_dimm_dsm(dimm, NDN_HPE1_CMD_SMART_THRESHOLD)
== DIMM_DSM_UNSUPPORTED) {
dbg(ctx, "unsupported function\n");
return NULL;
}
size = sizeof(*cmd) + sizeof(struct ndn_pkg_hpe1);
cmd = calloc(1, size);
if (!cmd)
return NULL;
cmd->dimm = dimm;
ndctl_cmd_ref(cmd);
cmd->type = ND_CMD_CALL;
cmd->size = size;
cmd->status = 1;
hpe1 = CMD_HPE1(cmd);
hpe1->gen.nd_family = NVDIMM_FAMILY_HPE1;
hpe1->gen.nd_command = NDN_HPE1_CMD_SMART_THRESHOLD;
hpe1->gen.nd_fw_size = 0;
hpe1->gen.nd_size_in = offsetof(struct ndn_hpe1_smart_threshold, status);
hpe1->gen.nd_size_out = sizeof(hpe1->u.smart);
hpe1->u.thresh.status = 3;
cmd->firmware_status = &hpe1->u.thresh.status;
return cmd;
}
static int hpe1_smart_threshold_valid(struct ndctl_cmd *cmd)
{
if (cmd->type != ND_CMD_CALL ||
cmd->size != sizeof(*cmd) + sizeof(struct ndn_pkg_hpe1) ||
CMD_HPE1(cmd)->gen.nd_family != NVDIMM_FAMILY_HPE1 ||
CMD_HPE1(cmd)->gen.nd_command != NDN_HPE1_CMD_SMART_THRESHOLD ||
cmd->status != 0)
return cmd->status < 0 ? cmd->status : -EINVAL;
return 0;
}
static unsigned int hpe1_cmd_smart_threshold_get_alarm_control(struct ndctl_cmd *cmd)
{
unsigned int hpe1flags;
unsigned int flags;
if (hpe1_smart_threshold_valid(cmd) < 0)
return UINT_MAX;
hpe1flags = CMD_HPE1_SMART_THRESH(cmd)->threshold_alarm_ctl;
flags = 0;
if (hpe1flags & NDN_HPE1_SMART_TEMP_TRIP)
flags |= ND_SMART_TEMP_TRIP;
if (hpe1flags & NDN_HPE1_SMART_SPARE_TRIP)
flags |= ND_SMART_SPARE_TRIP;
return flags;
}
static unsigned int hpe1_cmd_smart_threshold_get_media_temperature(
struct ndctl_cmd *cmd)
{
if (hpe1_smart_threshold_valid(cmd) < 0)
return UINT_MAX;
return CMD_HPE1_SMART_THRESH(cmd)->temp_threshold;
}
static unsigned int hpe1_cmd_smart_threshold_get_spares(struct ndctl_cmd *cmd)
{
if (hpe1_smart_threshold_valid(cmd) < 0)
return UINT_MAX;
return CMD_HPE1_SMART_THRESH(cmd)->spare_block_threshold;
}
struct ndctl_dimm_ops * const hpe1_dimm_ops = &(struct ndctl_dimm_ops) {
.new_smart = hpe1_dimm_cmd_new_smart,
.smart_get_flags = hpe1_cmd_smart_get_flags,
.smart_get_health = hpe1_cmd_smart_get_health,
.smart_get_media_temperature = hpe1_cmd_smart_get_media_temperature,
.smart_get_spares = hpe1_cmd_smart_get_spares,
.smart_get_alarm_flags = hpe1_cmd_smart_get_alarm_flags,
.smart_get_life_used = hpe1_cmd_smart_get_life_used,
.smart_get_shutdown_state = hpe1_cmd_smart_get_shutdown_state,
.smart_get_vendor_size = hpe1_cmd_smart_get_vendor_size,
.smart_get_vendor_data = hpe1_cmd_smart_get_vendor_data,
.new_smart_threshold = hpe1_dimm_cmd_new_smart_threshold,
.smart_threshold_get_alarm_control = hpe1_cmd_smart_threshold_get_alarm_control,
.smart_threshold_get_media_temperature =
hpe1_cmd_smart_threshold_get_media_temperature,
.smart_threshold_get_spares = hpe1_cmd_smart_threshold_get_spares,
};