/* * Destructor to kill the child locking process */ static int ctdb_lock_context_destructor(struct lock_context *lock_ctx) { if (lock_ctx->request) { lock_ctx->request->lctx = NULL; } if (lock_ctx->child > 0) { ctdb_kill(lock_ctx->ctdb, lock_ctx->child, SIGTERM); if (lock_ctx->type == LOCK_RECORD) { DLIST_REMOVE(lock_ctx->ctdb_db->lock_current, lock_ctx); } else { DLIST_REMOVE(lock_ctx->ctdb->lock_current, lock_ctx); } if (lock_ctx->ctdb_db) { lock_ctx->ctdb_db->lock_num_current--; } CTDB_DECREMENT_STAT(lock_ctx->ctdb, locks.num_current); if (lock_ctx->ctdb_db) { CTDB_DECREMENT_DB_STAT(lock_ctx->ctdb_db, locks.num_current); } } else { if (lock_ctx->type == LOCK_RECORD) { DLIST_REMOVE(lock_ctx->ctdb_db->lock_pending, lock_ctx); } else { DLIST_REMOVE(lock_ctx->ctdb->lock_pending, lock_ctx); } CTDB_DECREMENT_STAT(lock_ctx->ctdb, locks.num_pending); if (lock_ctx->ctdb_db) { CTDB_DECREMENT_DB_STAT(lock_ctx->ctdb_db, locks.num_pending); } } ctdb_lock_schedule(lock_ctx->ctdb); return 0; }
/* * Destructor to kill the child locking process */ static int ctdb_lock_context_destructor(struct lock_context *lock_ctx) { if (lock_ctx->child > 0) { ctdb_kill(lock_ctx->ctdb, lock_ctx->child, SIGKILL); DLIST_REMOVE(lock_ctx->ctdb->lock_current, lock_ctx); if (lock_ctx->ctdb_db) { lock_ctx->ctdb_db->lock_num_current--; } CTDB_DECREMENT_STAT(lock_ctx->ctdb, locks.num_current); if (lock_ctx->type == LOCK_RECORD || lock_ctx->type == LOCK_DB) { CTDB_DECREMENT_DB_STAT(lock_ctx->ctdb_db, locks.num_current); } } else { DLIST_REMOVE(lock_ctx->ctdb->lock_pending, lock_ctx); lock_ctx->ctdb->lock_num_pending--; CTDB_DECREMENT_STAT(lock_ctx->ctdb, locks.num_pending); if (lock_ctx->type == LOCK_RECORD || lock_ctx->type == LOCK_DB) { CTDB_DECREMENT_DB_STAT(lock_ctx->ctdb_db, locks.num_pending); } } ctdb_lock_schedule(lock_ctx->ctdb); return 0; }
/* * Schedule a new lock child process * Set up callback handler and timeout handler */ static void ctdb_lock_schedule(struct ctdb_context *ctdb) { struct lock_context *lock_ctx, *next_ctx, *active_ctx; int ret; TALLOC_CTX *tmp_ctx; const char *helper = BINDIR "/ctdb_lock_helper"; static const char *prog = NULL; char **args; if (prog == NULL) { const char *t; t = getenv("CTDB_LOCK_HELPER"); if (t != NULL) { prog = talloc_strdup(ctdb, t); } else { prog = talloc_strdup(ctdb, helper); } CTDB_NO_MEMORY_VOID(ctdb, prog); } if (ctdb->lock_pending == NULL) { return; } /* Find a lock context with requests */ lock_ctx = ctdb->lock_pending; while (lock_ctx != NULL) { next_ctx = lock_ctx->next; if (! lock_ctx->req_queue) { DEBUG(DEBUG_INFO, ("Removing lock context without lock requests\n")); DLIST_REMOVE(ctdb->lock_pending, lock_ctx); ctdb->lock_num_pending--; CTDB_DECREMENT_STAT(ctdb, locks.num_pending); if (lock_ctx->ctdb_db) { CTDB_DECREMENT_DB_STAT(lock_ctx->ctdb_db, locks.num_pending); } talloc_free(lock_ctx); } else { active_ctx = find_lock_context(ctdb->lock_current, lock_ctx->ctdb_db, lock_ctx->key, lock_ctx->priority, lock_ctx->type); if (active_ctx == NULL) { if (lock_ctx->ctdb_db == NULL || lock_ctx->ctdb_db->lock_num_current < MAX_LOCK_PROCESSES_PER_DB) { /* Found a lock context with lock requests */ break; } } /* There is already a child waiting for the * same key. So don't schedule another child * just yet. */ } lock_ctx = next_ctx; } if (lock_ctx == NULL) { return; } lock_ctx->child = -1; ret = pipe(lock_ctx->fd); if (ret != 0) { DEBUG(DEBUG_ERR, ("Failed to create pipe in ctdb_lock_schedule\n")); return; } set_close_on_exec(lock_ctx->fd[0]); /* Create data for child process */ tmp_ctx = talloc_new(lock_ctx); if (tmp_ctx == NULL) { DEBUG(DEBUG_ERR, ("Failed to allocate memory for helper args\n")); close(lock_ctx->fd[0]); close(lock_ctx->fd[1]); return; } /* Create arguments for lock helper */ args = lock_helper_args(tmp_ctx, lock_ctx, lock_ctx->fd[1]); if (args == NULL) { DEBUG(DEBUG_ERR, ("Failed to create lock helper args\n")); close(lock_ctx->fd[0]); close(lock_ctx->fd[1]); talloc_free(tmp_ctx); return; } lock_ctx->child = vfork(); if (lock_ctx->child == (pid_t)-1) { DEBUG(DEBUG_ERR, ("Failed to create a child in ctdb_lock_schedule\n")); close(lock_ctx->fd[0]); close(lock_ctx->fd[1]); talloc_free(tmp_ctx); return; } /* Child process */ if (lock_ctx->child == 0) { ret = execv(prog, args); if (ret < 0) { DEBUG(DEBUG_ERR, ("Failed to execute helper %s (%d, %s)\n", prog, errno, strerror(errno))); } _exit(1); } /* Parent process */ ctdb_track_child(ctdb, lock_ctx->child); close(lock_ctx->fd[1]); talloc_set_destructor(lock_ctx, ctdb_lock_context_destructor); talloc_free(tmp_ctx); /* Set up timeout handler */ lock_ctx->ttimer = tevent_add_timer(ctdb->ev, lock_ctx, timeval_current_ofs(10, 0), ctdb_lock_timeout_handler, (void *)lock_ctx); if (lock_ctx->ttimer == NULL) { ctdb_kill(ctdb, lock_ctx->child, SIGKILL); lock_ctx->child = -1; talloc_set_destructor(lock_ctx, NULL); close(lock_ctx->fd[0]); return; } /* Set up callback */ lock_ctx->tfd = tevent_add_fd(ctdb->ev, lock_ctx, lock_ctx->fd[0], EVENT_FD_READ, ctdb_lock_handler, (void *)lock_ctx); if (lock_ctx->tfd == NULL) { TALLOC_FREE(lock_ctx->ttimer); ctdb_kill(ctdb, lock_ctx->child, SIGKILL); lock_ctx->child = -1; talloc_set_destructor(lock_ctx, NULL); close(lock_ctx->fd[0]); return; } tevent_fd_set_auto_close(lock_ctx->tfd); /* Move the context from pending to current */ DLIST_REMOVE(ctdb->lock_pending, lock_ctx); ctdb->lock_num_pending--; DLIST_ADD_END(ctdb->lock_current, lock_ctx, NULL); if (lock_ctx->ctdb_db) { lock_ctx->ctdb_db->lock_num_current++; CTDB_INCREMENT_STAT(lock_ctx->ctdb, locks.num_current); CTDB_INCREMENT_DB_STAT(lock_ctx->ctdb_db, locks.num_current); } }
/* * Callback routine when the required locks are obtained. * Called from parent context */ static void ctdb_lock_handler(struct tevent_context *ev, struct tevent_fd *tfd, uint16_t flags, void *private_data) { struct lock_context *lock_ctx; TALLOC_CTX *tmp_ctx = NULL; char c; bool locked; double t; int id; lock_ctx = talloc_get_type_abort(private_data, struct lock_context); /* cancel the timeout event */ if (lock_ctx->ttimer) { TALLOC_FREE(lock_ctx->ttimer); } t = timeval_elapsed(&lock_ctx->start_time); id = lock_bucket_id(t); if (lock_ctx->auto_mark) { tmp_ctx = talloc_new(ev); talloc_steal(tmp_ctx, lock_ctx); } /* Read the status from the child process */ if (read(lock_ctx->fd[0], &c, 1) != 1) { locked = false; } else { locked = (c == 0 ? true : false); } /* Update statistics */ CTDB_DECREMENT_STAT(lock_ctx->ctdb, locks.num_pending); CTDB_INCREMENT_STAT(lock_ctx->ctdb, locks.num_calls); if (lock_ctx->ctdb_db) { CTDB_DECREMENT_DB_STAT(lock_ctx->ctdb_db, locks.num_pending); CTDB_INCREMENT_DB_STAT(lock_ctx->ctdb_db, locks.num_calls); } if (locked) { if (lock_ctx->ctdb_db) { CTDB_INCREMENT_STAT(lock_ctx->ctdb, locks.buckets[id]); CTDB_UPDATE_LATENCY(lock_ctx->ctdb, lock_ctx->ctdb_db, lock_type_str[lock_ctx->type], locks.latency, lock_ctx->start_time); CTDB_UPDATE_DB_LATENCY(lock_ctx->ctdb_db, lock_type_str[lock_ctx->type], locks.latency, t); CTDB_INCREMENT_DB_STAT(lock_ctx->ctdb_db, locks.buckets[id]); } } else { CTDB_INCREMENT_STAT(lock_ctx->ctdb, locks.num_failed); if (lock_ctx->ctdb_db) { CTDB_INCREMENT_DB_STAT(lock_ctx->ctdb_db, locks.num_failed); } } process_callbacks(lock_ctx, locked); if (lock_ctx->auto_mark) { talloc_free(tmp_ctx); } }