/* * Copyright (c) 2010 David Teigland * All Rights Reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License V2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it would be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libdlm.h" /* * The general idea of this test is * - dlm_lock on node1 gets EX * - dlm_lock on node2 blocks on EX * - dlm_lock on node1 is killed * - dlm_lock on node2 should get EX * repeat * * The dlm_lock process is intentionally killed without an explicit * unlock to test the lock cleanup done by the kernel when a process * exits while holding locks. */ /* even/odd limits this to two instances, but requires no config; "offset max" args (like alternate) would allow multiple */ static dlm_lshandle_t *dh; static struct dlm_lksb counter_lksb; static char counter_lvb[32]; static uint32_t counter; static int we_are_even; static int do_release(void) { dh = dlm_create_lockspace("dlm_lock_alt", 0600); if (!dh) { printf("alt create lockspace error\n"); return -1; } dlm_release_lockspace("dlm_lock_alt", dh, 1); return 0; } static int get_counter(void) { int rv; dh = dlm_create_lockspace("dlm_lock_alt", 0600); if (!dh) { return -1; } rv = dlm_ls_pthread_init(dh); if (rv < 0) { return -1; } memset(&counter_lksb, 0, sizeof(counter_lksb)); memset(&counter_lvb, 0, sizeof(counter_lvb)); counter_lksb.sb_lvbptr = counter_lvb; printf("alt counter request NL ...\n"); rv = dlm_ls_lock_wait(dh, LKM_NLMODE, &counter_lksb, LKF_VALBLK, "counter", strlen("counter"), 0, NULL, NULL, NULL); printf("alt counter request NL done status %d lkid %x\n", counter_lksb.sb_status, counter_lksb.sb_lkid); if (rv) { return -1; } return 0; } /* increment lvb of counter_lock */ /* if we are even, counter++ should make counter odd */ static int inc_counter(void) { int rv; /* NL->EX */ rv = dlm_ls_lock_wait(dh, LKM_EXMODE, &counter_lksb, LKF_VALBLK | LKF_CONVERT, "counter", strlen("counter"), 0, NULL, NULL, NULL); if (rv) { return -1; } counter++; memcpy(&counter_lvb, &counter, sizeof(counter)); /* EX->NL */ rv = dlm_ls_lock_wait(dh, LKM_NLMODE, &counter_lksb, LKF_VALBLK | LKF_CONVERT, "counter", strlen("counter"), 0, NULL, NULL, NULL); if (rv) { return -1; } return 0; } /* if we are even, wait for counter lock to be even */ static int wait_counter(void) { int rv; while (1) { /* NL->PR */ rv = dlm_ls_lock_wait(dh, LKM_PRMODE, &counter_lksb, LKF_VALBLK | LKF_CONVERT, "counter", strlen("counter"), 0, NULL, NULL, NULL); if (rv) { return -1; } memcpy(&counter, &counter_lvb, sizeof(counter)); /* PR->NL */ rv = dlm_ls_lock_wait(dh, LKM_NLMODE, &counter_lksb, LKF_VALBLK | LKF_CONVERT, "counter", strlen("counter"), 0, NULL, NULL, NULL); if (rv) { return -1; } if (!counter) { we_are_even = 1; break; } if (we_are_even && !(counter % 2)) break; if (!we_are_even && (counter % 2)) break; usleep(500000); } return 0; } static int rand_int(int a, int b) { return a + (int) (((float)(b - a + 1)) * random() / (RAND_MAX+1.0)); } #define LINE_LEN 64 static int wait_child_lock(int pr_fd) { char *done_str = "dlm_lock convert EX done"; char line[LINE_LEN], prog[32], act[32], mode[32], done[32], status[32]; FILE *file; int n, val, rv = -1; file = fdopen(pr_fd, "r"); if (!file) { printf("alt fdopen error %d\n", errno); return -1; } while (fgets(line, LINE_LEN, file)) { printf("%s", line); fflush(stdout); n = sscanf(line, "%s %s %s %s %s %d", prog, act, mode, done, status, &val); if (n < 6) continue; if (!strncmp(line, done_str, strlen(done_str))) { if (!strcmp(status, "status") && val == 0) rv = 0; break; } } fclose(file); return rv; } int main(int argc, char *argv[]) { int fda[2]; int pr_fd, cw_fd; /* parent read fd, child write fd */ int pid, status, n, rv; printf("alt pid %d\n", getpid()); if (argc > 1 && !strcmp(argv[1], "release")) { do_release(); return 0; } rv = get_counter(); if (rv < 0) { return -1; } while (1) { /* when this returns, it's our turn (other node has EX) */ printf("alt wait counter %s %u\n", we_are_even ? "even" : "odd", counter); rv = wait_counter(); if (rv < 0) { printf("alt wait_counter error\n"); break; } pipe(fda); pr_fd = fda[0]; cw_fd = fda[1]; pid = fork(); if (!pid) { /* child's stdout and stderr sent to parent's pr_fd */ close(STDOUT_FILENO); dup(cw_fd); close(STDERR_FILENO); dup(cw_fd); close(STDIN_FILENO); execl("./dlm_lock", "./dlm_lock", NULL); exit(EXIT_FAILURE); } else { printf("alt fork pid %d\n", pid); /* wait for child to get EX */ printf("alt wait child lock\n"); rv = wait_child_lock(pr_fd); close(pr_fd); close(cw_fd); if (rv < 0) { printf("alt child lock error\n"); break; } /* let other node start another dlm_lock process */ rv = inc_counter(); if (rv < 0) { printf("alt inc_counter error\n"); break; } /* sleep random number of seconds */ n = rand_int(0, 4); printf("alt sleep %d\n", n); sleep(n); /* kill child */ printf("alt kill pid %d\n", pid); kill(pid, SIGKILL); /* wait for child to exit */ printf("alt wait pid %d\n", pid); waitpid(pid, &status, 0); /* sleep random number of seconds */ n = rand_int(0, 2); printf("alt sleep %d\n", n); sleep(n); } } return 0; }