summaryrefslogtreecommitdiffstats
path: root/plugins/solidigm/solidigm-garbage-collection.c
blob: a37e9c5847187222f8569fd5fa36c63d241d1b2b (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
138
// 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"

struct __packed gc_item {
	__le32 timer_type;
	__le64 timestamp;
};

#define VU_GC_MAX_ITEMS 100
struct garbage_control_collection_log {
	__le16 version_major;
	__le16 version_minor;
	struct __packed gc_item item[VU_GC_MAX_ITEMS];
	__u8 reserved[2892];
};

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

	for (int i = 0; i < VU_GC_MAX_ITEMS; i++) {
		struct __packed gc_item 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(struct garbage_control_collection_log *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++) {
		struct __packed gc_item 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.";
	enum nvme_print_flags flags;
	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;

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

	uuid_index = solidigm_get_vu_uuid_index(dev);

	struct garbage_control_collection_log 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;
}