18c329057SIan Rogers // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
28c329057SIan Rogers #include "debug.h"
3531ee0fdSIan Rogers #include "evlist.h"
48c329057SIan Rogers #include "hwmon_pmu.h"
5531ee0fdSIan Rogers #include "parse-events.h"
68c329057SIan Rogers #include "tests.h"
7531ee0fdSIan Rogers #include <fcntl.h>
8531ee0fdSIan Rogers #include <sys/stat.h>
98c329057SIan Rogers #include <linux/compiler.h>
108c329057SIan Rogers #include <linux/kernel.h>
118c329057SIan Rogers #include <linux/string.h>
128c329057SIan Rogers
13531ee0fdSIan Rogers static const struct test_event {
14531ee0fdSIan Rogers const char *name;
15531ee0fdSIan Rogers const char *alias;
16531ee0fdSIan Rogers long config;
17531ee0fdSIan Rogers } test_events[] = {
18531ee0fdSIan Rogers {
19531ee0fdSIan Rogers "temp_test_hwmon_event1",
20531ee0fdSIan Rogers "temp1",
21531ee0fdSIan Rogers 0xA0001,
22531ee0fdSIan Rogers },
23531ee0fdSIan Rogers {
24531ee0fdSIan Rogers "temp_test_hwmon_event2",
25531ee0fdSIan Rogers "temp2",
26531ee0fdSIan Rogers 0xA0002,
27531ee0fdSIan Rogers },
28531ee0fdSIan Rogers };
29531ee0fdSIan Rogers
30531ee0fdSIan Rogers /* Cleanup test PMU directory. */
test_pmu_put(const char * dir,struct perf_pmu * hwm)31531ee0fdSIan Rogers static int test_pmu_put(const char *dir, struct perf_pmu *hwm)
32531ee0fdSIan Rogers {
33531ee0fdSIan Rogers char buf[PATH_MAX + 20];
34531ee0fdSIan Rogers int ret;
35531ee0fdSIan Rogers
36531ee0fdSIan Rogers if (scnprintf(buf, sizeof(buf), "rm -fr %s", dir) < 0) {
37531ee0fdSIan Rogers pr_err("Failure to set up buffer for \"%s\"\n", dir);
38531ee0fdSIan Rogers return -EINVAL;
39531ee0fdSIan Rogers }
40531ee0fdSIan Rogers ret = system(buf);
41531ee0fdSIan Rogers if (ret)
42531ee0fdSIan Rogers pr_err("Failure to \"%s\"\n", buf);
43531ee0fdSIan Rogers
446d78089dSIan Rogers list_del(&hwm->list);
45531ee0fdSIan Rogers perf_pmu__delete(hwm);
46531ee0fdSIan Rogers return ret;
47531ee0fdSIan Rogers }
48531ee0fdSIan Rogers
49531ee0fdSIan Rogers /*
50531ee0fdSIan Rogers * Prepare test PMU directory data, normally exported by kernel at
51531ee0fdSIan Rogers * /sys/class/hwmon/hwmon<number>/. Give as input a buffer to hold the file
52531ee0fdSIan Rogers * path, the result is PMU loaded using that directory.
53531ee0fdSIan Rogers */
test_pmu_get(char * dir,size_t sz)54531ee0fdSIan Rogers static struct perf_pmu *test_pmu_get(char *dir, size_t sz)
55531ee0fdSIan Rogers {
56531ee0fdSIan Rogers const char *test_hwmon_name_nl = "A test hwmon PMU\n";
57531ee0fdSIan Rogers const char *test_hwmon_name = "A test hwmon PMU";
58531ee0fdSIan Rogers /* Simulated hwmon items. */
59531ee0fdSIan Rogers const struct test_item {
60531ee0fdSIan Rogers const char *name;
61531ee0fdSIan Rogers const char *value;
62531ee0fdSIan Rogers } test_items[] = {
63531ee0fdSIan Rogers { "temp1_label", "test hwmon event1\n", },
64531ee0fdSIan Rogers { "temp1_input", "40000\n", },
65531ee0fdSIan Rogers { "temp2_label", "test hwmon event2\n", },
66531ee0fdSIan Rogers { "temp2_input", "50000\n", },
67531ee0fdSIan Rogers };
68*d4e17a32SIan Rogers int hwmon_dirfd = -1, test_dirfd = -1, file;
69531ee0fdSIan Rogers struct perf_pmu *hwm = NULL;
70531ee0fdSIan Rogers ssize_t len;
71531ee0fdSIan Rogers
72531ee0fdSIan Rogers /* Create equivalent of sysfs mount point. */
73531ee0fdSIan Rogers scnprintf(dir, sz, "/tmp/perf-hwmon-pmu-test-XXXXXX");
74531ee0fdSIan Rogers if (!mkdtemp(dir)) {
75531ee0fdSIan Rogers pr_err("mkdtemp failed\n");
76531ee0fdSIan Rogers dir[0] = '\0';
77531ee0fdSIan Rogers return NULL;
78531ee0fdSIan Rogers }
79*d4e17a32SIan Rogers test_dirfd = open(dir, O_PATH|O_DIRECTORY);
80*d4e17a32SIan Rogers if (test_dirfd < 0) {
81531ee0fdSIan Rogers pr_err("Failed to open test directory \"%s\"\n", dir);
82531ee0fdSIan Rogers goto err_out;
83531ee0fdSIan Rogers }
84531ee0fdSIan Rogers
85531ee0fdSIan Rogers /* Create the test hwmon directory and give it a name. */
86*d4e17a32SIan Rogers if (mkdirat(test_dirfd, "hwmon1234", 0755) < 0) {
87531ee0fdSIan Rogers pr_err("Failed to mkdir hwmon directory\n");
88531ee0fdSIan Rogers goto err_out;
89531ee0fdSIan Rogers }
90*d4e17a32SIan Rogers hwmon_dirfd = openat(test_dirfd, "hwmon1234", O_DIRECTORY);
91*d4e17a32SIan Rogers if (hwmon_dirfd < 0) {
92*d4e17a32SIan Rogers pr_err("Failed to open test hwmon directory \"%s/hwmon1234\"\n", dir);
93*d4e17a32SIan Rogers goto err_out;
94*d4e17a32SIan Rogers }
95*d4e17a32SIan Rogers file = openat(hwmon_dirfd, "name", O_WRONLY | O_CREAT, 0600);
96*d4e17a32SIan Rogers if (file < 0) {
97531ee0fdSIan Rogers pr_err("Failed to open for writing file \"name\"\n");
98531ee0fdSIan Rogers goto err_out;
99531ee0fdSIan Rogers }
100531ee0fdSIan Rogers len = strlen(test_hwmon_name_nl);
101531ee0fdSIan Rogers if (write(file, test_hwmon_name_nl, len) < len) {
102531ee0fdSIan Rogers close(file);
103531ee0fdSIan Rogers pr_err("Failed to write to 'name' file\n");
104531ee0fdSIan Rogers goto err_out;
105531ee0fdSIan Rogers }
106531ee0fdSIan Rogers close(file);
107531ee0fdSIan Rogers
108531ee0fdSIan Rogers /* Create test hwmon files. */
109531ee0fdSIan Rogers for (size_t i = 0; i < ARRAY_SIZE(test_items); i++) {
110531ee0fdSIan Rogers const struct test_item *item = &test_items[i];
111531ee0fdSIan Rogers
112*d4e17a32SIan Rogers file = openat(hwmon_dirfd, item->name, O_WRONLY | O_CREAT, 0600);
113*d4e17a32SIan Rogers if (file < 0) {
114531ee0fdSIan Rogers pr_err("Failed to open for writing file \"%s\"\n", item->name);
115531ee0fdSIan Rogers goto err_out;
116531ee0fdSIan Rogers }
117531ee0fdSIan Rogers
118531ee0fdSIan Rogers if (write(file, item->value, strlen(item->value)) < 0) {
119531ee0fdSIan Rogers pr_err("Failed to write to file \"%s\"\n", item->name);
120531ee0fdSIan Rogers close(file);
121531ee0fdSIan Rogers goto err_out;
122531ee0fdSIan Rogers }
123531ee0fdSIan Rogers close(file);
124531ee0fdSIan Rogers }
125531ee0fdSIan Rogers
126531ee0fdSIan Rogers /* Make the PMU reading the files created above. */
127*d4e17a32SIan Rogers hwm = perf_pmus__add_test_hwmon_pmu(hwmon_dirfd, "hwmon1234", test_hwmon_name);
128531ee0fdSIan Rogers if (!hwm)
129531ee0fdSIan Rogers pr_err("Test hwmon creation failed\n");
130531ee0fdSIan Rogers
131531ee0fdSIan Rogers err_out:
132531ee0fdSIan Rogers if (!hwm) {
133531ee0fdSIan Rogers test_pmu_put(dir, hwm);
134*d4e17a32SIan Rogers if (hwmon_dirfd >= 0)
135*d4e17a32SIan Rogers close(hwmon_dirfd);
136531ee0fdSIan Rogers }
137*d4e17a32SIan Rogers if (test_dirfd >= 0)
138*d4e17a32SIan Rogers close(test_dirfd);
139531ee0fdSIan Rogers return hwm;
140531ee0fdSIan Rogers }
141531ee0fdSIan Rogers
do_test(size_t i,bool with_pmu,bool with_alias)142531ee0fdSIan Rogers static int do_test(size_t i, bool with_pmu, bool with_alias)
143531ee0fdSIan Rogers {
144531ee0fdSIan Rogers const char *test_event = with_alias ? test_events[i].alias : test_events[i].name;
145531ee0fdSIan Rogers struct evlist *evlist = evlist__new();
146531ee0fdSIan Rogers struct evsel *evsel;
147531ee0fdSIan Rogers struct parse_events_error err;
148531ee0fdSIan Rogers int ret;
149531ee0fdSIan Rogers char str[128];
150531ee0fdSIan Rogers bool found = false;
151531ee0fdSIan Rogers
152531ee0fdSIan Rogers if (!evlist) {
153531ee0fdSIan Rogers pr_err("evlist allocation failed\n");
154531ee0fdSIan Rogers return TEST_FAIL;
155531ee0fdSIan Rogers }
156531ee0fdSIan Rogers
157531ee0fdSIan Rogers if (with_pmu)
1586d78089dSIan Rogers snprintf(str, sizeof(str), "hwmon_a_test_hwmon_pmu/%s/", test_event);
159531ee0fdSIan Rogers else
160531ee0fdSIan Rogers strlcpy(str, test_event, sizeof(str));
161531ee0fdSIan Rogers
162531ee0fdSIan Rogers pr_debug("Testing '%s'\n", str);
163531ee0fdSIan Rogers parse_events_error__init(&err);
164531ee0fdSIan Rogers ret = parse_events(evlist, str, &err);
165531ee0fdSIan Rogers if (ret) {
166531ee0fdSIan Rogers pr_debug("FAILED %s:%d failed to parse event '%s', err %d\n",
167531ee0fdSIan Rogers __FILE__, __LINE__, str, ret);
168531ee0fdSIan Rogers parse_events_error__print(&err, str);
169531ee0fdSIan Rogers ret = TEST_FAIL;
170531ee0fdSIan Rogers goto out;
171531ee0fdSIan Rogers }
172531ee0fdSIan Rogers
173531ee0fdSIan Rogers ret = TEST_OK;
174531ee0fdSIan Rogers if (with_pmu ? (evlist->core.nr_entries != 1) : (evlist->core.nr_entries < 1)) {
175531ee0fdSIan Rogers pr_debug("FAILED %s:%d Unexpected number of events for '%s' of %d\n",
176531ee0fdSIan Rogers __FILE__, __LINE__, str, evlist->core.nr_entries);
177531ee0fdSIan Rogers ret = TEST_FAIL;
178531ee0fdSIan Rogers goto out;
179531ee0fdSIan Rogers }
180531ee0fdSIan Rogers
181531ee0fdSIan Rogers evlist__for_each_entry(evlist, evsel) {
182fc26637dSIan Rogers if (!evsel->pmu || !evsel->pmu->name ||
183fc26637dSIan Rogers strcmp(evsel->pmu->name, "hwmon_a_test_hwmon_pmu"))
184531ee0fdSIan Rogers continue;
185531ee0fdSIan Rogers
186531ee0fdSIan Rogers if (evsel->core.attr.config != (u64)test_events[i].config) {
187531ee0fdSIan Rogers pr_debug("FAILED %s:%d Unexpected config for '%s', %lld != %ld\n",
188531ee0fdSIan Rogers __FILE__, __LINE__, str,
189531ee0fdSIan Rogers evsel->core.attr.config,
190531ee0fdSIan Rogers test_events[i].config);
191531ee0fdSIan Rogers ret = TEST_FAIL;
192531ee0fdSIan Rogers goto out;
193531ee0fdSIan Rogers }
194531ee0fdSIan Rogers found = true;
195531ee0fdSIan Rogers }
196531ee0fdSIan Rogers
197531ee0fdSIan Rogers if (!found) {
198531ee0fdSIan Rogers pr_debug("FAILED %s:%d Didn't find hwmon event '%s' in parsed evsels\n",
199531ee0fdSIan Rogers __FILE__, __LINE__, str);
200531ee0fdSIan Rogers ret = TEST_FAIL;
201531ee0fdSIan Rogers }
202531ee0fdSIan Rogers
203531ee0fdSIan Rogers out:
204db26a8c9SIan Rogers parse_events_error__exit(&err);
205531ee0fdSIan Rogers evlist__delete(evlist);
206531ee0fdSIan Rogers return ret;
207531ee0fdSIan Rogers }
208531ee0fdSIan Rogers
test__hwmon_pmu(bool with_pmu)209531ee0fdSIan Rogers static int test__hwmon_pmu(bool with_pmu)
210531ee0fdSIan Rogers {
211531ee0fdSIan Rogers char dir[PATH_MAX];
212531ee0fdSIan Rogers struct perf_pmu *pmu = test_pmu_get(dir, sizeof(dir));
213531ee0fdSIan Rogers int ret = TEST_OK;
214531ee0fdSIan Rogers
215531ee0fdSIan Rogers if (!pmu)
216531ee0fdSIan Rogers return TEST_FAIL;
217531ee0fdSIan Rogers
218531ee0fdSIan Rogers for (size_t i = 0; i < ARRAY_SIZE(test_events); i++) {
219531ee0fdSIan Rogers ret = do_test(i, with_pmu, /*with_alias=*/false);
220531ee0fdSIan Rogers
221531ee0fdSIan Rogers if (ret != TEST_OK)
222531ee0fdSIan Rogers break;
223531ee0fdSIan Rogers
224531ee0fdSIan Rogers ret = do_test(i, with_pmu, /*with_alias=*/true);
225531ee0fdSIan Rogers
226531ee0fdSIan Rogers if (ret != TEST_OK)
227531ee0fdSIan Rogers break;
228531ee0fdSIan Rogers }
229531ee0fdSIan Rogers test_pmu_put(dir, pmu);
230531ee0fdSIan Rogers return ret;
231531ee0fdSIan Rogers }
232531ee0fdSIan Rogers
test__hwmon_pmu_without_pmu(struct test_suite * test __maybe_unused,int subtest __maybe_unused)233531ee0fdSIan Rogers static int test__hwmon_pmu_without_pmu(struct test_suite *test __maybe_unused,
234531ee0fdSIan Rogers int subtest __maybe_unused)
235531ee0fdSIan Rogers {
236531ee0fdSIan Rogers return test__hwmon_pmu(/*with_pmu=*/false);
237531ee0fdSIan Rogers }
238531ee0fdSIan Rogers
test__hwmon_pmu_with_pmu(struct test_suite * test __maybe_unused,int subtest __maybe_unused)239531ee0fdSIan Rogers static int test__hwmon_pmu_with_pmu(struct test_suite *test __maybe_unused,
240531ee0fdSIan Rogers int subtest __maybe_unused)
241531ee0fdSIan Rogers {
2426d78089dSIan Rogers return test__hwmon_pmu(/*with_pmu=*/true);
243531ee0fdSIan Rogers }
244531ee0fdSIan Rogers
test__parse_hwmon_filename(struct test_suite * test __maybe_unused,int subtest __maybe_unused)2458c329057SIan Rogers static int test__parse_hwmon_filename(struct test_suite *test __maybe_unused,
2468c329057SIan Rogers int subtest __maybe_unused)
2478c329057SIan Rogers {
2488c329057SIan Rogers const struct hwmon_parse_test {
2498c329057SIan Rogers const char *filename;
2508c329057SIan Rogers enum hwmon_type type;
2518c329057SIan Rogers int number;
2528c329057SIan Rogers enum hwmon_item item;
2538c329057SIan Rogers bool alarm;
2548c329057SIan Rogers bool parse_ok;
2558c329057SIan Rogers } tests[] = {
2568c329057SIan Rogers {
2578c329057SIan Rogers .filename = "cpu0_accuracy",
2588c329057SIan Rogers .type = HWMON_TYPE_CPU,
2598c329057SIan Rogers .number = 0,
2608c329057SIan Rogers .item = HWMON_ITEM_ACCURACY,
2618c329057SIan Rogers .alarm = false,
2628c329057SIan Rogers .parse_ok = true,
2638c329057SIan Rogers },
2648c329057SIan Rogers {
2658c329057SIan Rogers .filename = "temp1_input",
2668c329057SIan Rogers .type = HWMON_TYPE_TEMP,
2678c329057SIan Rogers .number = 1,
2688c329057SIan Rogers .item = HWMON_ITEM_INPUT,
2698c329057SIan Rogers .alarm = false,
2708c329057SIan Rogers .parse_ok = true,
2718c329057SIan Rogers },
2728c329057SIan Rogers {
2738c329057SIan Rogers .filename = "fan2_vid",
2748c329057SIan Rogers .type = HWMON_TYPE_FAN,
2758c329057SIan Rogers .number = 2,
2768c329057SIan Rogers .item = HWMON_ITEM_VID,
2778c329057SIan Rogers .alarm = false,
2788c329057SIan Rogers .parse_ok = true,
2798c329057SIan Rogers },
2808c329057SIan Rogers {
2818c329057SIan Rogers .filename = "power3_crit_alarm",
2828c329057SIan Rogers .type = HWMON_TYPE_POWER,
2838c329057SIan Rogers .number = 3,
2848c329057SIan Rogers .item = HWMON_ITEM_CRIT,
2858c329057SIan Rogers .alarm = true,
2868c329057SIan Rogers .parse_ok = true,
2878c329057SIan Rogers },
2888c329057SIan Rogers {
2898c329057SIan Rogers .filename = "intrusion4_average_interval_min_alarm",
2908c329057SIan Rogers .type = HWMON_TYPE_INTRUSION,
2918c329057SIan Rogers .number = 4,
2928c329057SIan Rogers .item = HWMON_ITEM_AVERAGE_INTERVAL_MIN,
2938c329057SIan Rogers .alarm = true,
2948c329057SIan Rogers .parse_ok = true,
2958c329057SIan Rogers },
2968c329057SIan Rogers {
2978c329057SIan Rogers .filename = "badtype5_baditem",
2988c329057SIan Rogers .type = HWMON_TYPE_NONE,
2998c329057SIan Rogers .number = 5,
3008c329057SIan Rogers .item = HWMON_ITEM_NONE,
3018c329057SIan Rogers .alarm = false,
3028c329057SIan Rogers .parse_ok = false,
3038c329057SIan Rogers },
3048c329057SIan Rogers {
3058c329057SIan Rogers .filename = "humidity6_baditem",
3068c329057SIan Rogers .type = HWMON_TYPE_NONE,
3078c329057SIan Rogers .number = 6,
3088c329057SIan Rogers .item = HWMON_ITEM_NONE,
3098c329057SIan Rogers .alarm = false,
3108c329057SIan Rogers .parse_ok = false,
3118c329057SIan Rogers },
3128c329057SIan Rogers };
3138c329057SIan Rogers
3148c329057SIan Rogers for (size_t i = 0; i < ARRAY_SIZE(tests); i++) {
3158c329057SIan Rogers enum hwmon_type type;
3168c329057SIan Rogers int number;
3178c329057SIan Rogers enum hwmon_item item;
3188c329057SIan Rogers bool alarm;
3198c329057SIan Rogers
3208c329057SIan Rogers TEST_ASSERT_EQUAL("parse_hwmon_filename",
3218c329057SIan Rogers parse_hwmon_filename(
3228c329057SIan Rogers tests[i].filename,
3238c329057SIan Rogers &type,
3248c329057SIan Rogers &number,
3258c329057SIan Rogers &item,
3268c329057SIan Rogers &alarm),
3278c329057SIan Rogers tests[i].parse_ok
3288c329057SIan Rogers );
3298c329057SIan Rogers if (tests[i].parse_ok) {
3308c329057SIan Rogers TEST_ASSERT_EQUAL("parse_hwmon_filename type", type, tests[i].type);
3318c329057SIan Rogers TEST_ASSERT_EQUAL("parse_hwmon_filename number", number, tests[i].number);
3328c329057SIan Rogers TEST_ASSERT_EQUAL("parse_hwmon_filename item", item, tests[i].item);
3338c329057SIan Rogers TEST_ASSERT_EQUAL("parse_hwmon_filename alarm", alarm, tests[i].alarm);
3348c329057SIan Rogers }
3358c329057SIan Rogers }
3368c329057SIan Rogers return TEST_OK;
3378c329057SIan Rogers }
3388c329057SIan Rogers
3398c329057SIan Rogers static struct test_case tests__hwmon_pmu[] = {
3408c329057SIan Rogers TEST_CASE("Basic parsing test", parse_hwmon_filename),
341531ee0fdSIan Rogers TEST_CASE("Parsing without PMU name", hwmon_pmu_without_pmu),
342531ee0fdSIan Rogers TEST_CASE("Parsing with PMU name", hwmon_pmu_with_pmu),
3438c329057SIan Rogers { .name = NULL, }
3448c329057SIan Rogers };
3458c329057SIan Rogers
3468c329057SIan Rogers struct test_suite suite__hwmon_pmu = {
3478c329057SIan Rogers .desc = "Hwmon PMU",
3488c329057SIan Rogers .test_cases = tests__hwmon_pmu,
3498c329057SIan Rogers };
350