summaryrefslogtreecommitdiffstats
path: root/plugins/solidigm/solidigm-garbage-collection.c
blob: 3828b9e67e1de01f2a38ed0c6b128a6b469897d1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Copyright (c) 2022 Solidigm.
 *
 * Author: leonardo.da.cunha@solidigm.com
 */

#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <inttypes.h>

#include "common.h"
#include "nvme.h"
#include "libnvme.h"
#include "plugin.h"
#include "linux/types.h"
#include "nvme-print.h"
#include "solidigm-garbage-collection.h"
#include "solidigm-util.h"

typedef struct __attribute__((packed)) gc_item {
	__le32 timer_type;
	__le64 timestamp;
} gc_item_t;

#define VU_GC_MAX_ITEMS 100
typedef struct garbage_control_collection_log {
	__le16 version_major;
	__le16 version_minor;
	gc_item_t item[VU_GC_MAX_ITEMS];
	__u8 reserved[2892];
} garbage_control_collection_log_t;

static void vu_gc_log_show_json(garbage_control_collection_log_t *payload, const char *devname)
{
	struct json_object *gc_entries = json_create_array();

	for (int i = 0; i < VU_GC_MAX_ITEMS; i++) {
		gc_item_t item = payload->item[i];
		struct json_object *entry = json_create_object();
		json_object_add_value_int(entry, "timestamp", le64_to_cpu(item.timestamp));
		json_object_add_value_int(entry, "timer_type", le32_to_cpu(item.timer_type));
		json_array_add_value_object(gc_entries, entry);
	}

	json_print_object(gc_entries, NULL);
	json_free_object(gc_entries);
}

static void vu_gc_log_show(garbage_control_collection_log_t *payload, const char *devname,
			   __u8 uuid_index)
{
	printf("Solidigm Garbage Collection Log for NVME device:%s UUID-idx:%d\n", devname,
	       uuid_index);
	printf("Timestamp     Timer Type\n");

	for (int i = 0; i < VU_GC_MAX_ITEMS; i++) {
		gc_item_t item = payload->item[i];
		printf("%-13" PRIu64 " %d\n", le64_to_cpu(item.timestamp), le32_to_cpu(item.timer_type));
	}
}

int solidigm_get_garbage_collection_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
{
	const char *desc = "Get and parse Solidigm vendor specific garbage collection event log.";
	struct nvme_dev *dev;
	int err;
	__u8 uuid_index;

	struct config {
		char	*output_format;
	};

	struct config cfg = {
		.output_format	= "normal",
	};

	OPT_ARGS(opts) = {
		OPT_FMT("output-format",   'o', &cfg.output_format,  output_format),
		OPT_END()
	};

	err = parse_and_open(&dev, argc, argv, desc, opts);
	if (err)
		return err;

	enum nvme_print_flags flags = validate_output_format(cfg.output_format);
	if (flags == -EINVAL) {
		fprintf(stderr, "Invalid output format '%s'\n", cfg.output_format);
		dev_close(dev);
		return EINVAL;
	}

	uuid_index = solidigm_get_vu_uuid_index(dev);

	garbage_control_collection_log_t gc_log;
	const int solidigm_vu_gc_log_id = 0xfd;
	struct nvme_get_log_args args = {
		.lpo = 0,
		.result = NULL,
		.log = &gc_log,
		.args_size = sizeof(args),
		.fd = dev_fd(dev),
		.timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
		.lid = solidigm_vu_gc_log_id,
		.len = sizeof(gc_log),
		.nsid = NVME_NSID_ALL,
		.csi = NVME_CSI_NVM,
		.lsi = NVME_LOG_LSI_NONE,
		.lsp = NVME_LOG_LSP_NONE,
		.uuidx = uuid_index,
		.rae = false,
		.ot = false,
	};

	err =  nvme_get_log(&args);
	if (!err) {
		if (flags & BINARY)	{
			d_raw((unsigned char *)&gc_log, sizeof(gc_log));
		} else if (flags & JSON) {
			vu_gc_log_show_json(&gc_log, dev->name);
		} else {
			vu_gc_log_show(&gc_log, dev->name, uuid_index);
		}
	}
	else if (err > 0) {
		nvme_show_status(err);
	}

	/* Redundant close() to make static code analysis happy */
	close(dev->direct.fd);
	dev_close(dev);
	return err;
}