/* called when a CTDB_REPLY_DMASTER packet comes in This packet comes in from the lmaster response to a CTDB_REQ_CALL request packet. It means that the current dmaster wants to give us the dmaster role */ void ctdb_reply_dmaster(struct ctdb_context *ctdb, struct ctdb_req_header *hdr) { struct ctdb_reply_dmaster *c = (struct ctdb_reply_dmaster *)hdr; struct ctdb_call_state *state; struct ctdb_db_context *ctdb_db; TDB_DATA data; int ret; state = idr_find_type(ctdb->idr, hdr->reqid, struct ctdb_call_state); if (state == NULL) { return; } ctdb_db = state->ctdb_db; ret = ctdb_ltdb_lock_requeue(ctdb_db, state->call.key, hdr, ctdb_recv_raw_pkt, ctdb); if (ret == -2) { return; } if (ret != 0) { DEBUG(0,(__location__ " Failed to get lock in ctdb_reply_dmaster\n")); return; } data.dptr = c->data; data.dsize = c->datalen; talloc_steal(state, c); /* we're now the dmaster - update our local ltdb with new header and data */ state->header.dmaster = ctdb->vnn; if (ctdb_ltdb_store(ctdb_db, state->call.key, &state->header, data) != 0) { ctdb_ltdb_unlock(ctdb_db, state->call.key); ctdb_fatal(ctdb, "ctdb_reply_dmaster store failed\n"); return; } ctdb_call_local(ctdb_db, &state->call, &state->header, &data, ctdb->vnn); ctdb_ltdb_unlock(ctdb_db, state->call.key); talloc_steal(state, state->call.reply_data.dptr); state->state = CTDB_CALL_DONE; if (state->async.fn) { state->async.fn(state); } }
/* fetch a record from the ltdb, separating out the header information and returning the body of the record. A valid (initial) header is returned if the record is not present */ int ctdb_ltdb_fetch(struct ctdb_db_context *ctdb_db, TDB_DATA key, struct ctdb_ltdb_header *header, TALLOC_CTX *mem_ctx, TDB_DATA *data) { TDB_DATA rec; struct ctdb_context *ctdb = ctdb_db->ctdb; rec = tdb_fetch(ctdb_db->ltdb->tdb, key); if (rec.dsize < sizeof(*header)) { /* return an initial header */ if (rec.dptr) free(rec.dptr); if (ctdb->vnn_map == NULL) { /* called from the client */ ZERO_STRUCTP(data); header->dmaster = (uint32_t)-1; return -1; } ltdb_initial_header(ctdb_db, key, header); if (data) { *data = tdb_null; } if (ctdb_db->persistent || header->dmaster == ctdb_db->ctdb->pnn) { if (ctdb_ltdb_store(ctdb_db, key, header, tdb_null) != 0) { DEBUG(DEBUG_NOTICE, (__location__ "failed to store initial header\n")); } } return 0; } *header = *(struct ctdb_ltdb_header *)rec.dptr; if (data) { data->dsize = rec.dsize - sizeof(struct ctdb_ltdb_header); data->dptr = talloc_memdup(mem_ctx, sizeof(struct ctdb_ltdb_header)+rec.dptr, data->dsize); } free(rec.dptr); if (data) { CTDB_NO_MEMORY(ctdb, data->dptr); } return 0; }
/* fetch a record from the ltdb, separating out the header information and returning the body of the record. A valid (initial) header is returned if the record is not present */ int ctdb_ltdb_fetch(struct ctdb_db_context *ctdb_db, TDB_DATA key, struct ctdb_ltdb_header *header, TALLOC_CTX *mem_ctx, TDB_DATA *data) { TDB_DATA rec; struct ctdb_context *ctdb = ctdb_db->ctdb; rec = tdb_fetch(ctdb_db->ltdb->tdb, key); if (rec.dsize < sizeof(*header)) { TDB_DATA d2; /* return an initial header */ if (rec.dptr) free(rec.dptr); if (ctdb->vnn_map == NULL) { /* called from the client */ ZERO_STRUCTP(data); header->dmaster = (uint32_t)-1; return -1; } ltdb_initial_header(ctdb_db, key, header); ZERO_STRUCT(d2); if (data) { *data = d2; } ctdb_ltdb_store(ctdb_db, key, header, d2); return 0; } *header = *(struct ctdb_ltdb_header *)rec.dptr; if (data) { data->dsize = rec.dsize - sizeof(struct ctdb_ltdb_header); data->dptr = talloc_memdup(mem_ctx, sizeof(struct ctdb_ltdb_header)+rec.dptr, data->dsize); } free(rec.dptr); if (data) { CTDB_NO_MEMORY(ctdb, data->dptr); } return 0; }
/* send a dmaster request (give another node the dmaster for a record) This is always sent to the lmaster, which ensures that the lmaster always knows who the dmaster is. The lmaster will then send a CTDB_REPLY_DMASTER to the new dmaster */ static void ctdb_call_send_dmaster(struct ctdb_db_context *ctdb_db, struct ctdb_req_call *c, struct ctdb_ltdb_header *header, TDB_DATA *key, TDB_DATA *data) { struct ctdb_req_dmaster *r; struct ctdb_context *ctdb = ctdb_db->ctdb; int len; len = offsetof(struct ctdb_req_dmaster, data) + key->dsize + data->dsize; r = ctdb->methods->allocate_pkt(ctdb, len); CTDB_NO_MEMORY_FATAL(ctdb, r); talloc_set_name_const(r, "send_dmaster packet"); r->hdr.length = len; r->hdr.ctdb_magic = CTDB_MAGIC; r->hdr.ctdb_version = CTDB_VERSION; r->hdr.operation = CTDB_REQ_DMASTER; r->hdr.destnode = ctdb_lmaster(ctdb, key); r->hdr.srcnode = ctdb->vnn; r->hdr.reqid = c->hdr.reqid; r->db_id = c->db_id; r->dmaster = c->hdr.srcnode; r->keylen = key->dsize; r->datalen = data->dsize; memcpy(&r->data[0], key->dptr, key->dsize); memcpy(&r->data[key->dsize], data->dptr, data->dsize); /* XXX - probably not necessary when lmaster==dmaster update the ltdb to record the new dmaster */ header->dmaster = r->hdr.destnode; ctdb_ltdb_store(ctdb_db, *key, header, *data); ctdb_queue_packet(ctdb, &r->hdr); talloc_free(r); }
/* local version of ctdb_call */ int ctdb_call_local(struct ctdb_db_context *ctdb_db, struct ctdb_call *call, struct ctdb_ltdb_header *header, TDB_DATA *data, uint32_t caller) { struct ctdb_call_info *c; struct ctdb_registered_call *fn; struct ctdb_context *ctdb = ctdb_db->ctdb; c = talloc(ctdb, struct ctdb_call_info); CTDB_NO_MEMORY(ctdb, c); c->key = call->key; c->call_data = &call->call_data; c->record_data.dptr = talloc_memdup(c, data->dptr, data->dsize); c->record_data.dsize = data->dsize; CTDB_NO_MEMORY(ctdb, c->record_data.dptr); c->new_data = NULL; c->reply_data = NULL; c->status = 0; for (fn=ctdb_db->calls;fn;fn=fn->next) { if (fn->id == call->call_id) break; } if (fn == NULL) { ctdb_set_error(ctdb, "Unknown call id %u\n", call->call_id); talloc_free(c); return -1; } if (fn->fn(c) != 0) { ctdb_set_error(ctdb, "ctdb_call %u failed\n", call->call_id); talloc_free(c); return -1; } if (header->laccessor != caller) { header->lacount = 0; } header->laccessor = caller; header->lacount++; /* we need to force the record to be written out if this was a remote access, so that the lacount is updated */ if (c->new_data == NULL && header->laccessor != ctdb->vnn) { c->new_data = &c->record_data; } if (c->new_data) { if (ctdb_ltdb_store(ctdb_db, call->key, header, *c->new_data) != 0) { ctdb_set_error(ctdb, "ctdb_call tdb_store failed\n"); talloc_free(c); return -1; } } if (c->reply_data) { call->reply_data = *c->reply_data; talloc_steal(ctdb, call->reply_data.dptr); talloc_set_name_const(call->reply_data.dptr, __location__); } else { call->reply_data.dptr = NULL; call->reply_data.dsize = 0; } call->status = c->status; talloc_free(c); return 0; }
/* called when a CTDB_REQ_DMASTER packet comes in this comes into the lmaster for a record when the current dmaster wants to give up the dmaster role and give it to someone else */ void ctdb_request_dmaster(struct ctdb_context *ctdb, struct ctdb_req_header *hdr) { struct ctdb_req_dmaster *c = (struct ctdb_req_dmaster *)hdr; struct ctdb_reply_dmaster *r; TDB_DATA key, data, data2; struct ctdb_ltdb_header header; struct ctdb_db_context *ctdb_db; int ret, len; TALLOC_CTX *tmp_ctx; key.dptr = c->data; key.dsize = c->keylen; data.dptr = c->data + c->keylen; data.dsize = c->datalen; ctdb_db = find_ctdb_db(ctdb, c->db_id); if (!ctdb_db) { ctdb_send_error(ctdb, hdr, -1, "Unknown database in request. db_id==0x%08x", c->db_id); return; } /* fetch the current record */ ret = ctdb_ltdb_lock_fetch_requeue(ctdb_db, key, &header, hdr, &data2, ctdb_recv_raw_pkt, ctdb); if (ret == -1) { ctdb_fatal(ctdb, "ctdb_req_dmaster failed to fetch record"); return; } if (ret == -2) { DEBUG(2,(__location__ " deferring ctdb_request_dmaster\n")); return; } /* its a protocol error if the sending node is not the current dmaster */ if (header.dmaster != hdr->srcnode && hdr->srcnode != ctdb_lmaster(ctdb_db->ctdb, &key)) { ctdb_fatal(ctdb, "dmaster request from non-master"); return; } header.dmaster = c->dmaster; ret = ctdb_ltdb_store(ctdb_db, key, &header, data); ctdb_ltdb_unlock(ctdb_db, key); if (ret != 0) { ctdb_fatal(ctdb, "ctdb_req_dmaster unable to update dmaster"); return; } /* put the packet on a temporary context, allowing us to safely free it below even if ctdb_reply_dmaster() has freed it already */ tmp_ctx = talloc_new(ctdb); /* send the CTDB_REPLY_DMASTER */ len = offsetof(struct ctdb_reply_dmaster, data) + data.dsize; r = ctdb->methods->allocate_pkt(tmp_ctx, len); CTDB_NO_MEMORY_FATAL(ctdb, r); talloc_set_name_const(r, "reply_dmaster packet"); r->hdr.length = len; r->hdr.ctdb_magic = CTDB_MAGIC; r->hdr.ctdb_version = CTDB_VERSION; r->hdr.operation = CTDB_REPLY_DMASTER; r->hdr.destnode = c->dmaster; r->hdr.srcnode = ctdb->vnn; r->hdr.reqid = hdr->reqid; r->datalen = data.dsize; memcpy(&r->data[0], data.dptr, data.dsize); ctdb_queue_packet(ctdb, &r->hdr); talloc_free(tmp_ctx); }