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. */ 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 */ 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 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 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 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 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 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