[PATCH 13/24] perf daemon: Allow only one daemon over base directory

From: Jiri Olsa
Date: Sat Jan 30 2021 - 18:57:41 EST


Add 'lock' file under daemon base and flock it, so only one
perf daemon can run on top of it.

Each daemon tries to create and lock BASE/lock file, if it's
successful we are sure we're the only daemon running over
the BASE.

Once daemon is finished, file descriptor to lock file is
closed and lock is released.

Example:

# cat ~/.perfconfig
[daemon]
base=/opt/perfdata

[session-cycles]
run = -m 10M -e cycles --overwrite --switch-output -a

[session-sched]
run = -m 20M -e sched:* --overwrite --switch-output -a

Starting the daemon:

# perf daemon start

And try once more:

# perf daemon start
failed: another perf daemon (pid 775594) owns /opt/perfdata

will end up with an error, because there's already one running
on top of /opt/perfdata.

Signed-off-by: Jiri Olsa <jolsa@xxxxxxxxxx>
---
tools/perf/builtin-daemon.c | 58 +++++++++++++++++++++++++++++++++++++
1 file changed, 58 insertions(+)

diff --git a/tools/perf/builtin-daemon.c b/tools/perf/builtin-daemon.c
index 468ed2af8b3f..4ebeb524b16e 100644
--- a/tools/perf/builtin-daemon.c
+++ b/tools/perf/builtin-daemon.c
@@ -1,10 +1,12 @@
// SPDX-License-Identifier: GPL-2.0
#include <subcmd/parse-options.h>
#include <api/fd/array.h>
+#include <api/fs/fs.h>
#include <linux/zalloc.h>
#include <linux/string.h>
#include <linux/limits.h>
#include <string.h>
+#include <sys/file.h>
#include <signal.h>
#include <stdlib.h>
#include <time.h>
@@ -529,12 +531,18 @@ static int cmd_session_list(struct daemon *daemon, union cmd *cmd, FILE *out)
/* output */
csv_sep, daemon->base, SESSION_OUTPUT);

+ fprintf(out, "%c%s/%s",
+ /* lock */
+ csv_sep, daemon->base, "lock");
+
fprintf(out, "\n");
} else {
fprintf(out, "[%d:daemon] base: %s\n", getpid(), daemon->base);
if (cmd->list.verbose) {
fprintf(out, " output: %s/%s\n",
daemon->base, SESSION_OUTPUT);
+ fprintf(out, " lock: %s/lock\n",
+ daemon->base);
}
}

@@ -864,6 +872,50 @@ static int setup_config(struct daemon *daemon)
return daemon->config_real ? 0 : -1;
}

+/*
+ * Each daemon tries to create and lock BASE/lock file,
+ * if it's successful we are sure we're the only daemon
+ * running over the BASE.
+ *
+ * Once daemon is finished, file descriptor to lock file
+ * is closed and lock is released.
+ */
+static int check_lock(struct daemon *daemon)
+{
+ char path[PATH_MAX];
+ char buf[20];
+ int fd, pid;
+ ssize_t len;
+
+ scnprintf(path, sizeof(path), "%s/lock", daemon->base);
+
+ fd = open(path, O_RDWR|O_CREAT|O_CLOEXEC, 0640);
+ if (fd < 0)
+ return -1;
+
+ if (lockf(fd, F_TLOCK, 0) < 0) {
+ filename__read_int(path, &pid);
+ fprintf(stderr, "failed: another perf daemon (pid %d) owns %s\n",
+ pid, daemon->base);
+ return -1;
+ }
+
+ scnprintf(buf, sizeof(buf), "%d", getpid());
+ len = strlen(buf);
+
+ if (write(fd, buf, len) != len) {
+ perror("failed: write");
+ return -1;
+ }
+
+ if (ftruncate(fd, len)) {
+ perror("failed: ftruncate");
+ return -1;
+ }
+
+ return 0;
+}
+
static int go_background(struct daemon *daemon)
{
int pid, fd;
@@ -878,6 +930,9 @@ static int go_background(struct daemon *daemon)
if (setsid() < 0)
return -1;

+ if (check_lock(daemon))
+ return -1;
+
umask(0);

if (chdir(daemon->base)) {
@@ -946,6 +1001,9 @@ static int __cmd_start(struct daemon *daemon, struct option parent_options[],
if (setup_server_config(daemon))
return -1;

+ if (foreground && check_lock(daemon))
+ return -1;
+
if (!foreground) {
err = go_background(daemon);
if (err) {
--
2.29.2