diff options
-rw-r--r-- | ctdb/include/ctdb_private.h | 11 | ||||
-rw-r--r-- | ctdb/include/ctdb_protocol.h | 2 | ||||
-rw-r--r-- | ctdb/server/ctdb_control.c | 9 | ||||
-rw-r--r-- | ctdb/server/ctdb_freeze.c | 182 |
4 files changed, 204 insertions, 0 deletions
diff --git a/ctdb/include/ctdb_private.h b/ctdb/include/ctdb_private.h index 83b8bd2b84e..b6f7263b8b7 100644 --- a/ctdb/include/ctdb_private.h +++ b/ctdb/include/ctdb_private.h @@ -585,6 +585,11 @@ struct ctdb_db_context { int lock_num_current; struct ctdb_call_state *pending_calls; + + enum ctdb_freeze_mode freeze_mode; + struct ctdb_db_freeze_handle *freeze_handle; + bool freeze_transaction_started; + uint32_t freeze_transaction_id; }; @@ -994,9 +999,15 @@ int32_t ctdb_control_set_recmode(struct ctdb_context *ctdb, void ctdb_request_control_reply(struct ctdb_context *ctdb, struct ctdb_req_control *c, TDB_DATA *outdata, int32_t status, const char *errormsg); +int32_t ctdb_control_db_freeze(struct ctdb_context *ctdb, + struct ctdb_req_control *c, + uint32_t db_id, bool *async_reply); +int32_t ctdb_control_db_thaw(struct ctdb_context *ctdb, uint32_t db_id); + int32_t ctdb_control_freeze(struct ctdb_context *ctdb, struct ctdb_req_control *c, bool *async_reply); int32_t ctdb_control_thaw(struct ctdb_context *ctdb, uint32_t priority, bool check_recmode); +bool ctdb_db_frozen(struct ctdb_db_context *ctdb_db); bool ctdb_db_prio_frozen(struct ctdb_context *ctdb, uint32_t priority); bool ctdb_db_all_frozen(struct ctdb_context *ctdb); diff --git a/ctdb/include/ctdb_protocol.h b/ctdb/include/ctdb_protocol.h index d9cad7c6f18..6169c8fe781 100644 --- a/ctdb/include/ctdb_protocol.h +++ b/ctdb/include/ctdb_protocol.h @@ -415,6 +415,8 @@ enum ctdb_controls {CTDB_CONTROL_PROCESS_EXISTS = 0, CTDB_CONTROL_GET_RUNSTATE = 138, CTDB_CONTROL_DB_DETACH = 139, CTDB_CONTROL_GET_NODES_FILE = 140, + CTDB_CONTROL_DB_FREEZE = 141, + CTDB_CONTROL_DB_THAW = 142, }; /* diff --git a/ctdb/server/ctdb_control.c b/ctdb/server/ctdb_control.c index 7b26ff96983..e7c76204104 100644 --- a/ctdb/server/ctdb_control.c +++ b/ctdb/server/ctdb_control.c @@ -683,6 +683,15 @@ static int32_t ctdb_control_dispatch(struct ctdb_context *ctdb, case CTDB_CONTROL_DB_DETACH: return ctdb_control_db_detach(ctdb, indata, client_id); + case CTDB_CONTROL_DB_FREEZE: + CHECK_CONTROL_DATA_SIZE(sizeof(uint32_t)); + return ctdb_control_db_freeze(ctdb, c, *(uint32_t *)indata.dptr, + async_reply); + + case CTDB_CONTROL_DB_THAW: + CHECK_CONTROL_DATA_SIZE(sizeof(uint32_t)); + return ctdb_control_db_thaw(ctdb, *(uint32_t *)indata.dptr); + default: DEBUG(DEBUG_CRIT,(__location__ " Unknown CTDB control opcode %u\n", opcode)); return -1; diff --git a/ctdb/server/ctdb_freeze.c b/ctdb/server/ctdb_freeze.c index 6b6a712e91f..5dc1b7c5826 100644 --- a/ctdb/server/ctdb_freeze.c +++ b/ctdb/server/ctdb_freeze.c @@ -101,6 +101,179 @@ static int db_transaction_commit_handler(struct ctdb_db_context *ctdb_db, } +/* a list of control requests waiting for db freeze */ +struct ctdb_db_freeze_waiter { + struct ctdb_db_freeze_waiter *next, *prev; + struct ctdb_context *ctdb; + void *private_data; + int32_t status; +}; + +/* a handle to a db freeze lock child process */ +struct ctdb_db_freeze_handle { + struct ctdb_db_context *ctdb_db; + struct lock_request *lreq; + struct ctdb_db_freeze_waiter *waiters; +}; + +/** + * Called when freeing database freeze handle + */ +static int ctdb_db_freeze_handle_destructor(struct ctdb_db_freeze_handle *h) +{ + struct ctdb_db_context *ctdb_db = h->ctdb_db; + + DEBUG(DEBUG_ERR, ("Release freeze handle for db %s\n", + ctdb_db->db_name)); + + /* Cancel any pending transactions */ + if (ctdb_db->freeze_transaction_started) { + db_transaction_cancel_handler(ctdb_db, NULL); + ctdb_db->freeze_transaction_started = false; + } + ctdb_db->freeze_mode = CTDB_FREEZE_NONE; + ctdb_db->freeze_handle = NULL; + + talloc_free(h->lreq); + return 0; +} + +/** + * Called when a database is frozen + */ +static void ctdb_db_freeze_handler(void *private_data, bool locked) +{ + struct ctdb_db_freeze_handle *h = talloc_get_type_abort( + private_data, struct ctdb_db_freeze_handle); + struct ctdb_db_freeze_waiter *w; + + if (h->ctdb_db->freeze_mode == CTDB_FREEZE_FROZEN) { + DEBUG(DEBUG_ERR, ("Freeze db child died - unfreezing\n")); + h->ctdb_db->freeze_mode = CTDB_FREEZE_NONE; + talloc_free(h); + return; + } + + if (!locked) { + DEBUG(DEBUG_ERR, ("Failed to get db lock for %s\n", + h->ctdb_db->db_name)); + h->ctdb_db->freeze_mode = CTDB_FREEZE_NONE; + talloc_free(h); + return; + } + + h->ctdb_db->freeze_mode = CTDB_FREEZE_FROZEN; + + /* notify the waiters */ + while ((w = h->waiters) != NULL) { + w->status = 0; + DLIST_REMOVE(h->waiters, w); + talloc_free(w); + } +} + +/** + * Start freeze process for a database + */ +static void ctdb_start_db_freeze(struct ctdb_db_context *ctdb_db) +{ + struct ctdb_db_freeze_handle *h; + + if (ctdb_db->freeze_mode == CTDB_FREEZE_FROZEN) { + return; + } + + if (ctdb_db->freeze_handle != NULL) { + return; + } + + DEBUG(DEBUG_ERR, ("Freeze db: %s\n", ctdb_db->db_name)); + + ctdb_stop_vacuuming(ctdb_db->ctdb); + + h = talloc_zero(ctdb_db, struct ctdb_db_freeze_handle); + CTDB_NO_MEMORY_FATAL(ctdb_db->ctdb, h); + + h->ctdb_db = ctdb_db; + h->lreq = ctdb_lock_db(h, ctdb_db, false, ctdb_db_freeze_handler, h); + CTDB_NO_MEMORY_FATAL(ctdb_db->ctdb, h->lreq); + talloc_set_destructor(h, ctdb_db_freeze_handle_destructor); + + ctdb_db->freeze_handle = h; + ctdb_db->freeze_mode = CTDB_FREEZE_PENDING; +} + +/** + * Reply to a waiter for db freeze + */ +static int ctdb_db_freeze_waiter_destructor(struct ctdb_db_freeze_waiter *w) +{ + /* 'c' pointer is talloc_memdup(), so cannot use talloc_get_type */ + struct ctdb_req_control *c = + (struct ctdb_req_control *)w->private_data; + + ctdb_request_control_reply(w->ctdb, c, NULL, w->status, NULL); + return 0; +} + +/** + * freeze a database + */ +int32_t ctdb_control_db_freeze(struct ctdb_context *ctdb, + struct ctdb_req_control *c, + uint32_t db_id, + bool *async_reply) +{ + struct ctdb_db_context *ctdb_db; + struct ctdb_db_freeze_waiter *w; + + ctdb_db = find_ctdb_db(ctdb, db_id); + if (ctdb_db == NULL) { + DEBUG(DEBUG_ERR, ("Freeze db for unknown dbid 0x%08x\n", db_id)); + return -1; + } + + if (ctdb_db->freeze_mode == CTDB_FREEZE_FROZEN) { + DEBUG(DEBUG_ERR, ("Freeze db: %s frozen\n", ctdb_db->db_name)); + return 0; + } + + ctdb_start_db_freeze(ctdb_db); + + /* add ourselves to the list of waiters */ + w = talloc(ctdb_db->freeze_handle, struct ctdb_db_freeze_waiter); + CTDB_NO_MEMORY(ctdb, w); + w->ctdb = ctdb; + w->private_data = talloc_steal(w, c); + w->status = -1; + talloc_set_destructor(w, ctdb_db_freeze_waiter_destructor); + DLIST_ADD(ctdb_db->freeze_handle->waiters, w); + + *async_reply = true; + return 0; +} + +/** + * Thaw a database + */ +int32_t ctdb_control_db_thaw(struct ctdb_context *ctdb, uint32_t db_id) +{ + struct ctdb_db_context *ctdb_db; + + ctdb_db = find_ctdb_db(ctdb, db_id); + if (ctdb_db == NULL) { + DEBUG(DEBUG_ERR, ("Thaw db for unknown dbid 0x%08x\n", db_id)); + return -1; + } + + DEBUG(DEBUG_ERR, ("Thaw db: %s\n", ctdb_db->db_name)); + + TALLOC_FREE(ctdb_db->freeze_handle); + ctdb_call_resend_db(ctdb_db); + return 0; +} + + /* a list of control requests waiting for a freeze lock child to get the database locks @@ -487,6 +660,15 @@ int32_t ctdb_control_wipe_database(struct ctdb_context *ctdb, TDB_DATA indata) return 0; } +bool ctdb_db_frozen(struct ctdb_db_context *ctdb_db) +{ + if (ctdb_db->freeze_mode != CTDB_FREEZE_FROZEN) { + return false; + } + + return true; +} + bool ctdb_db_prio_frozen(struct ctdb_context *ctdb, uint32_t priority) { if (priority == 0) { |