Add umount_partition_by_kill() 14/173014/8
authorHyotaek Shim <hyotaek.shim@samsung.com>
Mon, 19 Mar 2018 07:45:59 +0000 (16:45 +0900)
committerHyotaek Shim <hyotaek.shim@samsung.com>
Tue, 20 Mar 2018 06:53:56 +0000 (15:53 +0900)
For better filesystem reliability,
this api is used to umount writable partitions (/opt/usr, /opt, and etc.)
by killing processes with open files

1) lazy-umount partitions
   MNT_DETACH (since Linux 2.4.11)
   Perform a lazy unmount: make the mount point unavailable for new accesses,
   and actually perform the unmount when the mount point ceases to be busy.

2) kill processes (SIGTERM and SIGKILL)
   Restarted processes cannot open files from the detached partition.
   So, the number of processes that have opened files on the partition is solely reduced.

3) check # of remaining processes and repeat 2)~3) a fixed number of times.

Change-Id: I5d93de15b58e31715ec058f2ce154a03808da6e3
Signed-off-by: Hyotaek Shim <hyotaek.shim@samsung.com>
src/core/common.c
src/core/common.h
src/power/power-handler.c

index 6e5a192..1adb267 100644 (file)
@@ -16,7 +16,7 @@
  * limitations under the License.
  */
 
-
+#define _GNU_SOURCE
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdbool.h>
@@ -343,7 +343,7 @@ int sys_set_str(char *fname, char *val)
        return r;
 }
 
-int terminate_process(const char *partition, bool force)
+static int terminate_processes_on_partition(const char *partition, bool force)
 {
        const char *argv[7] = {"/usr/bin/fuser", "-m", "-k", "-s", NULL, NULL, NULL};
        int argc;
@@ -352,8 +352,10 @@ int terminate_process(const char *partition, bool force)
                argv[4] = "-SIGKILL";
        else
                argv[4] = "-SIGTERM";
+
        argv[5] = partition;
        argc = sizeof(argv) / sizeof(argv[0]);
+
        return run_child(argc, argv);
 }
 
@@ -383,44 +385,82 @@ int mount_check(const char *path)
        return ret;
 }
 
-int umount_partition(const char *path, const bool force)
+static int get_num_processes_on_partition(const char *part_path)
 {
-       int retry = 0, ret = -1;
-       struct timespec time = {0,};
-
-       sync();
-       while (ret && retry < 2) {
-               switch (retry++) {
-               case 0:
-                       /* Second, kill app with SIGTERM */
-                       _I("Kill app with SIGTERM");
-                       terminate_process(path, false);
-                       time.tv_nsec = 500 * NANO_SECOND_MULTIPLIER;
-                       nanosleep(&time, NULL);
-                       ret = umount2(path, 0);
-                       break;
-               case 1:
-                       /* Last time, kill app with SIGKILL */
-                       _I("Kill app with SIGKILL");
-                       terminate_process(path, true);
-                       time.tv_nsec = 200 * NANO_SECOND_MULTIPLIER;
-                       nanosleep(&time, NULL);
-                       ret = umount2(path, 0);
-                       break;
-               }
-               _I("ret %d retry %d", ret, retry);
+       FILE *fp;
+       char *cmd = NULL;
+       char *line = NULL;
+       size_t len = 0;
+       int num_processes = 0;
+
+       asprintf(&cmd, "fuser -m %s | grep -o '[0-9]*'", part_path);
+       printf("cmd=%s\n", cmd);
+       fp = popen(cmd, "r");
+       free(cmd);
+       if (fp == NULL)
+               return -1;
+
+       while (getline(&line, &len, fp) != -1) {
+               num_processes++;
        }
-       if (ret) {
-               if (force)
-                       ret = umount2(path, MNT_DETACH);
-               else
-                       ret = umount2(path, 0);
+
+       free(line);
+       pclose(fp);
+
+       return num_processes;
+}
+
+void umount_partition_by_kill(const char *path, const int max_retry)
+{
+       FILE *fp;
+       char *part_path = NULL;
+       size_t len = 0;
+       int ret = 0, retry = 0;
+       int remain;
+       char *cmd = NULL;
+
+       /* if the path is not a mountpoint, do not perform umounting and killing */
+       if (!mount_check(path))
+               return;
+
+       ret = asprintf(&cmd, "mount | grep \" on %s \" | awk '{print $1}'", path);
+       if (ret == -1)
+               return;
+
+       fp = popen(cmd, "r");
+       free(cmd);
+       if (!fp)
+               return;
+
+       ret = getline(&part_path, &len, fp);
+       if (ret == -1 || !part_path) {
+               pclose(fp);
+               return;
+       } else if (ret > 0 && *(part_path + ret - 1) == '\n') {
+               *(part_path + ret -1) = '\0';
        }
-       if (ret)
-               _I("Failed to unmount %s", path);
-       else
-               _I("%s unmounted successfully", path);
-       return ret;
+
+       umount2(path, MNT_DETACH);
+
+       do {
+               sync();
+
+               /* Kill processes with SIGTERM */
+               terminate_processes_on_partition(part_path, false);
+               usleep((useconds_t)MSEC_TO_USEC(500));
+
+               /* Kill processes with SIGKILL */
+               terminate_processes_on_partition(part_path, true);
+               usleep((useconds_t)MSEC_TO_USEC(200));
+
+               remain = get_num_processes_on_partition(part_path);
+
+               retry++;
+
+       } while (remain > 0 && retry < max_retry);
+
+       pclose(fp);
+       return;
 }
 
 void print_time(const char *prefix)
index 372f73e..cf86002 100644 (file)
 #define USEC_TO_MSEC(x)                ((double)x/1000)
 #endif
 
-#define DATA_VALUE_INT(x)       (*(int *)(x))
-#define DATA_VALUE_BOOL(x)      (*(bool *)(x))
-
-#define NANO_SECOND_MULTIPLIER  1000000 /* 1ms = 1,000,000 nsec */
+#define DATA_VALUE_INT(x)      (*(int *)(x))
+#define DATA_VALUE_BOOL(x)     (*(bool *)(x))
 
 #ifndef safe_free
 #define safe_free(x) safe_free_memory((void**)&(x))
index 02b350a..e7e0181 100755 (executable)
 
 #define POWEROFF_DURATION              2
 #define MAX_RETRY                      2
-#define POWEROFF_WAIT_RESOURCED (0.5*1000) /* 0.5 seconds */
-#define POWEROFF_WAIT_MAX      10 /* 10 seconds */
+#define POWEROFF_WAIT_RESOURCED                (0.5*1000) /* 0.5 seconds */
+#define POWEROFF_WAIT_MAX              10 /* 10 seconds */
 
-#define SIGNAL_POWEROFF_STATE  "ChangeState"
+#define SIGNAL_POWEROFF_STATE          "ChangeState"
 
-#define UMOUNT_RW_PATH "/opt/usr"
+#define UMOUNT_RW_PATH_USER            "/opt/usr"
+#define UMOUNT_RW_PATH_SYSTEM          "/opt"
+#define MAX_UMOUNT_KILL_RETRY          4
 
-#define POWER_FLAG_POWEROFF "/run/"POWER_POWEROFF
-#define POWER_FLAG_REBOOT   "/run/"POWER_REBOOT
+#define POWER_FLAG_POWEROFF            "/run/"POWER_POWEROFF
+#define POWER_FLAG_REBOOT              "/run/"POWER_REBOOT
 
-#define POWER_CONF_FILE "/etc/deviced/power.conf"
+#define POWER_CONF_FILE                        "/etc/deviced/power.conf"
 
 struct power_flag {
        enum poweroff_type type;
@@ -193,7 +195,8 @@ static void stop_systemd_service(void)
 /* umount usr data partition */
 static void unmount_rw_partition(void)
 {
-       umount_partition(UMOUNT_RW_PATH, true);
+       umount_partition_by_kill(UMOUNT_RW_PATH_USER, MAX_UMOUNT_KILL_RETRY);
+       umount_partition_by_kill(UMOUNT_RW_PATH_SYSTEM, MAX_UMOUNT_KILL_RETRY);
 }
 
 static void powerdown(void)