| /* |
| * Copyright © 2013, 2015 Intel Corporation |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice (including the next |
| * paragraph) shall be included in all copies or substantial portions of the |
| * Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
| * IN THE SOFTWARE. |
| * |
| * Authors: |
| * Paulo Zanoni <paulo.r.zanoni@intel.com> |
| * David Weinehall <david.weinehall@intel.com> |
| * |
| */ |
| #include <fcntl.h> |
| #include <stdio.h> |
| #include <limits.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| |
| #include "drmtest.h" |
| #include "igt_pm.h" |
| #include "igt_aux.h" |
| |
| /** |
| * SECTION:igt_pm |
| * @short_description: Power Management related helpers |
| * @title: Power Management |
| * @include: igt.h |
| * |
| * This library provides various helpers to enable power management for, |
| * and in some cases subsequently allow restoring the old behaviour of, |
| * various external components that by default are set up in a way |
| * that interferes with the testing of our power management functionality. |
| */ |
| |
| enum { |
| POLICY_UNKNOWN = -1, |
| POLICY_MAX_PERFORMANCE = 0, |
| POLICY_MEDIUM_POWER = 1, |
| POLICY_MIN_POWER = 2 |
| }; |
| |
| #define MAX_PERFORMANCE_STR "max_performance\n" |
| #define MEDIUM_POWER_STR "medium_power\n" |
| #define MIN_POWER_STR "min_power\n" |
| /* Remember to fix this if adding longer strings */ |
| #define MAX_POLICY_STRLEN strlen(MAX_PERFORMANCE_STR) |
| |
| /** |
| * igt_pm_enable_audio_runtime_pm: |
| * |
| * We know that if we don't enable audio runtime PM, snd_hda_intel will never |
| * release its power well refcount, and we'll never reach the LPSP state. |
| * There's no guarantee that it will release the power well if we enable |
| * runtime PM, but at least we can try. |
| * |
| * We don't have any assertions on open since the user may not even have |
| * snd_hda_intel loaded, which is not a problem. |
| */ |
| void igt_pm_enable_audio_runtime_pm(void) |
| { |
| int fd; |
| |
| fd = open("/sys/module/snd_hda_intel/parameters/power_save", O_WRONLY); |
| if (fd >= 0) { |
| igt_assert_eq(write(fd, "1\n", 2), 2); |
| close(fd); |
| } |
| fd = open("/sys/bus/pci/devices/0000:00:03.0/power/control", O_WRONLY); |
| if (fd >= 0) { |
| igt_assert_eq(write(fd, "auto\n", 5), 5); |
| close(fd); |
| } |
| /* Give some time for it to react. */ |
| sleep(1); |
| } |
| |
| /** |
| * igt_pm_enable_sata_link_power_management: |
| * |
| * Enable the min_power policy for SATA link power management. |
| * Without this we cannot reach deep runtime power states. |
| * |
| * We don't have any assertions on open since the system might not have |
| * a SATA host. |
| * |
| * Returns: |
| * An opaque pointer to the data needed to restore the default values |
| * after the test has terminated, or NULL if SATA link power management |
| * is not supported. This pointer should be freed when no longer used |
| * (typically after having called restore_sata_link_power_management()). |
| */ |
| int8_t *igt_pm_enable_sata_link_power_management(void) |
| { |
| int fd, i; |
| ssize_t len; |
| char *buf; |
| char *file_name; |
| int8_t *link_pm_policies = NULL; |
| |
| file_name = malloc(PATH_MAX); |
| buf = malloc(MAX_POLICY_STRLEN + 1); |
| |
| for (i = 0; ; i++) { |
| int8_t policy; |
| |
| snprintf(file_name, PATH_MAX, |
| "/sys/class/scsi_host/host%d/link_power_management_policy", |
| i); |
| |
| fd = open(file_name, O_RDWR); |
| if (fd < 0) |
| break; |
| |
| len = read(fd, buf, MAX_POLICY_STRLEN); |
| buf[len] = '\0'; |
| |
| if (!strncmp(MAX_PERFORMANCE_STR, buf, |
| strlen(MAX_PERFORMANCE_STR))) |
| policy = POLICY_MAX_PERFORMANCE; |
| else if (!strncmp(MEDIUM_POWER_STR, buf, |
| strlen(MEDIUM_POWER_STR))) |
| policy = POLICY_MEDIUM_POWER; |
| else if (!strncmp(MIN_POWER_STR, buf, |
| strlen(MIN_POWER_STR))) |
| policy = POLICY_MIN_POWER; |
| else |
| policy = POLICY_UNKNOWN; |
| |
| if (!(i % 256)) |
| link_pm_policies = realloc(link_pm_policies, |
| (i / 256 + 1) * 256 + 1); |
| |
| link_pm_policies[i] = policy; |
| link_pm_policies[i + 1] = 0; |
| |
| /* If the policy is something we don't know about, |
| * don't touch it, since we might potentially break things. |
| * And we obviously don't need to touch anything if the |
| * setting is already correct... |
| */ |
| if (policy != POLICY_UNKNOWN && |
| policy != POLICY_MIN_POWER) { |
| lseek(fd, 0, SEEK_SET); |
| igt_assert_eq(write(fd, MIN_POWER_STR, |
| strlen(MIN_POWER_STR)), |
| strlen(MIN_POWER_STR)); |
| } |
| close(fd); |
| } |
| free(buf); |
| free(file_name); |
| |
| return link_pm_policies; |
| } |
| |
| /** |
| * igt_pm_restore_sata_link_power_management: |
| * @pm_data: An opaque pointer with saved link PM policies; |
| * If NULL is passed we force enable the "max_performance" policy. |
| * |
| * Restore the link power management policies to the values |
| * prior to enabling min_power. |
| * |
| * Caveat: If the system supports hotplugging and hotplugging takes |
| * place during our testing so that the hosts change numbers |
| * we might restore the settings to the wrong hosts. |
| */ |
| void igt_pm_restore_sata_link_power_management(int8_t *pm_data) |
| |
| { |
| int fd, i; |
| char *file_name; |
| |
| /* Disk runtime PM policies. */ |
| file_name = malloc(PATH_MAX); |
| for (i = 0; ; i++) { |
| int8_t policy; |
| |
| if (!pm_data) |
| policy = POLICY_MAX_PERFORMANCE; |
| else if (pm_data[i] == POLICY_UNKNOWN) |
| continue; |
| else |
| policy = pm_data[i]; |
| |
| snprintf(file_name, PATH_MAX, |
| "/sys/class/scsi_host/host%d/link_power_management_policy", |
| i); |
| |
| fd = open(file_name, O_WRONLY); |
| if (fd < 0) |
| break; |
| |
| switch (policy) { |
| default: |
| case POLICY_MAX_PERFORMANCE: |
| igt_assert_eq(write(fd, MAX_PERFORMANCE_STR, |
| strlen(MAX_PERFORMANCE_STR)), |
| strlen(MAX_PERFORMANCE_STR)); |
| break; |
| |
| case POLICY_MEDIUM_POWER: |
| igt_assert_eq(write(fd, MEDIUM_POWER_STR, |
| strlen(MEDIUM_POWER_STR)), |
| strlen(MEDIUM_POWER_STR)); |
| break; |
| |
| case POLICY_MIN_POWER: |
| igt_assert_eq(write(fd, MIN_POWER_STR, |
| strlen(MIN_POWER_STR)), |
| strlen(MIN_POWER_STR)); |
| break; |
| } |
| |
| close(fd); |
| } |
| free(file_name); |
| } |
| #define POWER_DIR "/sys/devices/pci0000:00/0000:00:02.0/power" |
| /* We just leak this on exit ... */ |
| int pm_status_fd = -1; |
| |
| /** |
| * igt_setup_runtime_pm: |
| * |
| * Sets up the runtime PM helper functions and enables runtime PM. To speed up |
| * tests the autosuspend delay is set to 0. |
| * |
| * Returns: |
| * True if runtime pm is available, false otherwise. |
| */ |
| bool igt_setup_runtime_pm(void) |
| { |
| int fd; |
| ssize_t size; |
| char buf[6]; |
| |
| if (pm_status_fd >= 0) |
| return true; |
| |
| igt_pm_enable_audio_runtime_pm(); |
| |
| /* Our implementation uses autosuspend. Try to set it to 0ms so the test |
| * suite goes faster and we have a higher probability of triggering race |
| * conditions. */ |
| fd = open(POWER_DIR "/autosuspend_delay_ms", O_WRONLY); |
| igt_assert_f(fd >= 0, |
| "Can't open " POWER_DIR "/autosuspend_delay_ms\n"); |
| |
| /* If we fail to write to the file, it means this system doesn't support |
| * runtime PM. */ |
| size = write(fd, "0\n", 2); |
| |
| close(fd); |
| |
| if (size != 2) |
| return false; |
| |
| /* We know we support runtime PM, let's try to enable it now. */ |
| fd = open(POWER_DIR "/control", O_RDWR); |
| igt_assert_f(fd >= 0, "Can't open " POWER_DIR "/control\n"); |
| |
| size = write(fd, "auto\n", 5); |
| igt_assert(size == 5); |
| |
| lseek(fd, 0, SEEK_SET); |
| size = read(fd, buf, ARRAY_SIZE(buf)); |
| igt_assert(size == 5); |
| igt_assert(strncmp(buf, "auto\n", 5) == 0); |
| |
| close(fd); |
| |
| pm_status_fd = open(POWER_DIR "/runtime_status", O_RDONLY); |
| igt_assert_f(pm_status_fd >= 0, |
| "Can't open " POWER_DIR "/runtime_status\n"); |
| |
| return true; |
| } |
| |
| /** |
| * igt_get_runtime_pm_status: |
| * |
| * Returns: The current runtime PM status. |
| */ |
| enum igt_runtime_pm_status igt_get_runtime_pm_status(void) |
| { |
| ssize_t n_read; |
| char buf[32]; |
| |
| lseek(pm_status_fd, 0, SEEK_SET); |
| n_read = read(pm_status_fd, buf, ARRAY_SIZE(buf)); |
| igt_assert(n_read >= 0); |
| buf[n_read] = '\0'; |
| |
| if (strncmp(buf, "suspended\n", n_read) == 0) |
| return IGT_RUNTIME_PM_STATUS_SUSPENDED; |
| else if (strncmp(buf, "active\n", n_read) == 0) |
| return IGT_RUNTIME_PM_STATUS_ACTIVE; |
| else if (strncmp(buf, "suspending\n", n_read) == 0) |
| return IGT_RUNTIME_PM_STATUS_SUSPENDING; |
| else if (strncmp(buf, "resuming\n", n_read) == 0) |
| return IGT_RUNTIME_PM_STATUS_RESUMING; |
| |
| igt_assert_f(false, "Unknown status %s\n", buf); |
| return IGT_RUNTIME_PM_STATUS_UNKNOWN; |
| } |
| |
| /** |
| * igt_wait_for_pm_status: |
| * @status: desired runtime PM status |
| * |
| * Waits until for the driver to switch to into the desired runtime PM status, |
| * with a 10 second timeout. |
| * |
| * Returns: |
| * True if the desired runtime PM status was attained, false if the operation |
| * timed out. |
| */ |
| bool igt_wait_for_pm_status(enum igt_runtime_pm_status status) |
| { |
| return igt_wait(igt_get_runtime_pm_status() == status, 10000, 100); |
| } |