^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) * dslm.c
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Simple Disk Sleep Monitor
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) * by Bartek Kania
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Licensed under the GPL
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) #include <unistd.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) #include <stdlib.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) #include <stdio.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include <fcntl.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <errno.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <time.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <string.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <signal.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <sys/ioctl.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/hdreg.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #ifdef DEBUG
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #define D(x) x
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #define D(x)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) int endit = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) /* Check if the disk is in powersave-mode
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) * Most of the code is stolen from hdparm.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) * 1 = active, 0 = standby/sleep, -1 = unknown */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) static int check_powermode(int fd)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) unsigned char args[4] = {WIN_CHECKPOWERMODE1,0,0,0};
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) int state;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) if (ioctl(fd, HDIO_DRIVE_CMD, &args)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) && (args[0] = WIN_CHECKPOWERMODE2) /* try again with 0x98 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) && ioctl(fd, HDIO_DRIVE_CMD, &args)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) if (errno != EIO || args[0] != 0 || args[1] != 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) state = -1; /* "unknown"; */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) } else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) state = 0; /* "sleeping"; */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) state = (args[2] == 255) ? 1 : 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) D(printf(" drive state is: %d\n", state));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) return state;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) static char *state_name(int i)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) if (i == -1) return "unknown";
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) if (i == 0) return "sleeping";
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) if (i == 1) return "active";
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) return "internal error";
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) static char *myctime(time_t time)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) char *ts = ctime(&time);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) ts[strlen(ts) - 1] = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) return ts;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) static void measure(int fd)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) time_t start_time;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) int last_state;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) time_t last_time;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) int curr_state;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) time_t curr_time = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) time_t time_diff;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) time_t active_time = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) time_t sleep_time = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) time_t unknown_time = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) time_t total_time = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) int changes = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) float tmp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) printf("Starting measurements\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) last_state = check_powermode(fd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) start_time = last_time = time(0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) printf(" System is in state %s\n\n", state_name(last_state));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) while(!endit) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) sleep(1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) curr_state = check_powermode(fd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) if (curr_state != last_state || endit) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) changes++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) curr_time = time(0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) time_diff = curr_time - last_time;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) if (last_state == 1) active_time += time_diff;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) else if (last_state == 0) sleep_time += time_diff;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) else unknown_time += time_diff;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) last_state = curr_state;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) last_time = curr_time;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) printf("%s: State-change to %s\n", myctime(curr_time),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) state_name(curr_state));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) changes--; /* Compensate for SIGINT */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) total_time = time(0) - start_time;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) printf("\nTotal running time: %lus\n", curr_time - start_time);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) printf(" State changed %d times\n", changes);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) tmp = (float)sleep_time / (float)total_time * 100;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) printf(" Time in sleep state: %lus (%.2f%%)\n", sleep_time, tmp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) tmp = (float)active_time / (float)total_time * 100;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) printf(" Time in active state: %lus (%.2f%%)\n", active_time, tmp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) tmp = (float)unknown_time / (float)total_time * 100;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) printf(" Time in unknown state: %lus (%.2f%%)\n", unknown_time, tmp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) static void ender(int s)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) endit = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) static void usage(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) puts("usage: dslm [-w <time>] <disk>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) exit(0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) int main(int argc, char **argv)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) int fd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) char *disk = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) int settle_time = 60;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) /* Parse the simple command-line */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) if (argc == 2)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) disk = argv[1];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) else if (argc == 4) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) settle_time = atoi(argv[2]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) disk = argv[3];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) } else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) usage();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) if (!(fd = open(disk, O_RDONLY|O_NONBLOCK))) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) printf("Can't open %s, because: %s\n", disk, strerror(errno));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) exit(-1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) if (settle_time) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) printf("Waiting %d seconds for the system to settle down to "
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) "'normal'\n", settle_time);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) sleep(settle_time);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) } else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) puts("Not waiting for system to settle down");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) signal(SIGINT, ender);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) measure(fd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) close(fd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) }