Compare commits

...
8 changed files with 123 additions and 60 deletions
+1
View File
@@ -313,6 +313,7 @@ void osd_t::parse_config(bool init)
pg_reshard_chunk_pause_ms = config["pg_reshard_chunk_pause_ms"].uint64_value(); pg_reshard_chunk_pause_ms = config["pg_reshard_chunk_pause_ms"].uint64_value();
if (!pg_reshard_chunk_pause_ms) if (!pg_reshard_chunk_pause_ms)
pg_reshard_chunk_pause_ms = 100; pg_reshard_chunk_pause_ms = 100;
use_degraded_write = !json_is_false(config["use_degraded_write"]);
if (!old_auto_scrub && auto_scrub) if (!old_auto_scrub && auto_scrub)
{ {
// Schedule scrubbing // Schedule scrubbing
+3 -1
View File
@@ -111,6 +111,7 @@ class osd_t
bool no_rebalance = false; bool no_rebalance = false;
bool no_recovery = false; bool no_recovery = false;
bool no_scrub = false; bool no_scrub = false;
bool use_degraded_write = true;
bool allow_net_split = false; bool allow_net_split = false;
std::vector<std::string> cfg_bind_addresses; std::vector<std::string> cfg_bind_addresses;
int bind_port, listen_backlog = 128; int bind_port, listen_backlog = 128;
@@ -343,6 +344,7 @@ class osd_t
void continue_primary_sync(osd_op_t *cur_op); void continue_primary_sync(osd_op_t *cur_op);
void continue_primary_del(osd_op_t *cur_op); void continue_primary_del(osd_op_t *cur_op);
bool check_write_queue(osd_op_t *cur_op, pg_t & pg); bool check_write_queue(osd_op_t *cur_op, pg_t & pg);
bool wr_degraded(osd_op_t *cur_op);
pg_osd_set_state_t* add_object_to_set(pg_t & pg, const object_id oid, const pg_osd_set_t & osd_set, pg_osd_set_state_t* add_object_to_set(pg_t & pg, const object_id oid, const pg_osd_set_t & osd_set,
uint64_t old_pg_state, int log_at_level); uint64_t old_pg_state, int log_at_level);
bool remove_object_from_state(object_id & oid, pg_osd_set_state_t **object_state, pg_t &pg, bool report = true); bool remove_object_from_state(object_id & oid, pg_osd_set_state_t **object_state, pg_t &pg, bool report = true);
@@ -352,7 +354,7 @@ class osd_t
osd_rmw_stripe_t *stripes, bool ref); osd_rmw_stripe_t *stripes, bool ref);
pg_osd_set_state_t *mark_partial_write(pg_t & pg, osd_op_t *cur_op); pg_osd_set_state_t *mark_partial_write(pg_t & pg, osd_op_t *cur_op);
void deref_object_state(pg_t & pg, pg_osd_set_state_t **object_state, bool deref); void deref_object_state(pg_t & pg, pg_osd_set_state_t **object_state, bool deref);
bool remember_unstable_write(osd_op_t *cur_op, pg_t & pg, pg_osd_set_t & loc_set, int base_state); bool remember_unstable_write(osd_op_t *cur_op, pg_t & pg, uint64_t *cur_set, int base_state);
void handle_primary_subop(osd_op_t *subop, osd_op_t *cur_op); void handle_primary_subop(osd_op_t *subop, osd_op_t *cur_op);
void handle_primary_bs_subop(osd_op_t *subop); void handle_primary_bs_subop(osd_op_t *subop);
void add_bs_subop_stats(osd_op_t *subop, bool recovery_related = false); void add_bs_subop_stats(osd_op_t *subop, bool recovery_related = false);
+1
View File
@@ -294,6 +294,7 @@ void osd_t::submit_recovery_op(osd_recovery_op_t *op)
.inode = op->oid.inode, .inode = op->oid.inode,
.offset = op->oid.stripe, .offset = op->oid.stripe,
.len = 0, .len = 0,
.flags = OSD_OP_RECOVERY_RELATED,
}, },
}; };
if (log_level > 2) if (log_level > 2)
+9
View File
@@ -335,8 +335,10 @@ pg_osd_set_state_t* pg_t::add_object_to_state(const object_id oid, const uint64_
{ {
std::vector<osd_num_t> read_target; std::vector<osd_num_t> read_target;
bool found = false; bool found = false;
bool has_extra;
uint32_t bad_mask = (LOC_OUTDATED | LOC_CORRUPTED); uint32_t bad_mask = (LOC_OUTDATED | LOC_CORRUPTED);
retry: retry:
has_extra = false;
if (scheme == POOL_SCHEME_REPLICATED) if (scheme == POOL_SCHEME_REPLICATED)
{ {
for (auto & o: osd_set) for (auto & o: osd_set)
@@ -352,6 +354,10 @@ retry:
// FIXME: This is because we then use .data() and assume it's at least <pg_size> long // FIXME: This is because we then use .data() and assume it's at least <pg_size> long
read_target.resize(pg_size); read_target.resize(pg_size);
} }
else if (read_target.size() > pg_size)
{
has_extra = true;
}
} }
else else
{ {
@@ -360,6 +366,8 @@ retry:
{ {
if (!(o.loc_bad & bad_mask)) if (!(o.loc_bad & bad_mask))
{ {
if (read_target[o.role])
has_extra = true;
read_target[o.role] = o.osd_num; read_target[o.role] = o.osd_num;
found = true; found = true;
} }
@@ -375,6 +383,7 @@ retry:
state_dict[osd_set] = { state_dict[osd_set] = {
.read_target = read_target, .read_target = read_target,
.osd_set = osd_set, .osd_set = osd_set,
.has_extra = has_extra,
.state = state, .state = state,
.object_count = 1, .object_count = 1,
}; };
+1
View File
@@ -29,6 +29,7 @@ struct pg_osd_set_state_t
std::vector<osd_num_t> read_target; std::vector<osd_num_t> read_target;
// full OSD set including additional OSDs where the object is misplaced // full OSD set including additional OSDs where the object is misplaced
pg_osd_set_t osd_set; pg_osd_set_t osd_set;
bool has_extra = false;
uint64_t state = 0; uint64_t state = 0;
uint64_t object_count = 0; uint64_t object_count = 0;
uint64_t ref_count = 0; uint64_t ref_count = 0;
+1 -1
View File
@@ -490,7 +490,7 @@ void osd_t::handle_primary_subop(osd_op_t *subop, osd_op_t *cur_op)
} }
if ((op_data->errors + op_data->done) >= op_data->n_subops) if ((op_data->errors + op_data->done) >= op_data->n_subops)
{ {
if (!op_data->errors || !op_data->done || opcode != OSD_OP_SEC_WRITE && opcode != OSD_OP_SEC_WRITE_STABLE) if (opcode != OSD_OP_SEC_WRITE && opcode != OSD_OP_SEC_WRITE_STABLE)
{ {
delete[] op_data->subops; delete[] op_data->subops;
op_data->subops = NULL; op_data->subops = NULL;
+98 -58
View File
@@ -37,6 +37,11 @@ bool osd_t::check_write_queue(osd_op_t *cur_op, pg_t & pg)
return true; return true;
} }
bool osd_t::wr_degraded(osd_op_t *cur_op)
{
return use_degraded_write && !(cur_op->req.rw.flags & OSD_OP_RECOVERY_RELATED);
}
void osd_t::continue_primary_write(osd_op_t *cur_op) void osd_t::continue_primary_write(osd_op_t *cur_op)
{ {
if (!cur_op->op_data && !prepare_primary_rw(cur_op)) if (!cur_op->op_data && !prepare_primary_rw(cur_op))
@@ -62,10 +67,13 @@ void osd_t::continue_primary_write(osd_op_t *cur_op)
{ {
return; return;
} }
if ((cur_op->req.rw.flags & OSD_OP_RECOVERY_RELATED) && cur_op->client_id != 0)
{
cur_op->req.rw.flags = 0;
}
resume_1: resume_1:
// Determine blocks to read and write // Determine blocks to read and write
// Missing chunks are allowed to be overwritten even in incomplete objects // Missing chunks are allowed to be overwritten even in incomplete objects
// FIXME: Allow to do small writes to the old (degraded/misplaced) OSD set for lower performance impact
op_data->prev_set = get_object_osd_set(pg, op_data->oid, &op_data->object_state); op_data->prev_set = get_object_osd_set(pg, op_data->oid, &op_data->object_state);
if (op_data->object_state) if (op_data->object_state)
{ {
@@ -88,18 +96,23 @@ retry_1:
cur_op->reply.hdr.retval = -EIO; cur_op->reply.hdr.retval = -EIO;
goto continue_others; goto continue_others;
} }
// Object is degraded/misplaced and will be moved to <write_osd_set> if (!wr_degraded(cur_op))
op_data->stripes[0].read_start = 0; {
op_data->stripes[0].read_end = bs_block_size; // Object is degraded/misplaced and will be moved to <write_osd_set>
assert(!cur_op->rmw_buf); op_data->stripes[0].read_start = 0;
cur_op->rmw_buf = op_data->stripes[0].read_buf = memalign_or_die(MEM_ALIGNMENT, bs_block_size); op_data->stripes[0].read_end = bs_block_size;
assert(!cur_op->rmw_buf);
cur_op->rmw_buf = op_data->stripes[0].read_buf = memalign_or_die(MEM_ALIGNMENT, bs_block_size);
}
} }
} }
else else
{ {
assert(!cur_op->rmw_buf); assert(!cur_op->rmw_buf);
cur_op->rmw_buf = calc_rmw(cur_op->buf, op_data->stripes, op_data->prev_set, cur_op->rmw_buf = calc_rmw(cur_op->buf, op_data->stripes, op_data->prev_set,
pg.pg_size, pg.pg_data_size, pg.pg_cursize, pg.cur_set.data(), bs_block_size, clean_entry_bitmap_size); pg.pg_size, pg.pg_data_size, pg.pg_cursize,
wr_degraded(cur_op) ? op_data->prev_set : pg.cur_set.data(),
bs_block_size, clean_entry_bitmap_size);
if (!cur_op->rmw_buf) if (!cur_op->rmw_buf)
{ {
// Refuse partial overwrite of an incomplete object // Refuse partial overwrite of an incomplete object
@@ -152,8 +165,7 @@ resume_3:
bitmap_set(op_data->stripes[0].bmp_buf, op_data->stripes[0].write_start, bitmap_set(op_data->stripes[0].bmp_buf, op_data->stripes[0].write_start,
op_data->stripes[0].write_end-op_data->stripes[0].write_start, bs_bitmap_granularity); op_data->stripes[0].write_end-op_data->stripes[0].write_start, bs_bitmap_granularity);
// Possibly copy new data from the request into the recovery buffer // Possibly copy new data from the request into the recovery buffer
if (pg.cur_set.data() != op_data->prev_set && (op_data->stripes[0].write_start != 0 || if (cur_op->rmw_buf)
op_data->stripes[0].write_end != bs_block_size))
{ {
memcpy( memcpy(
(uint8_t*)op_data->stripes[0].read_buf + op_data->stripes[0].req_start, (uint8_t*)op_data->stripes[0].read_buf + op_data->stripes[0].req_start,
@@ -173,11 +185,15 @@ resume_3:
// Recover missing stripes, calculate parity // Recover missing stripes, calculate parity
if (pg.scheme == POOL_SCHEME_XOR) if (pg.scheme == POOL_SCHEME_XOR)
{ {
calc_rmw_parity_xor(op_data->stripes, pg.pg_size, op_data->prev_set, pg.cur_set.data(), bs_block_size, clean_entry_bitmap_size); calc_rmw_parity_xor(op_data->stripes, pg.pg_size, op_data->prev_set,
wr_degraded(cur_op) ? op_data->prev_set : pg.cur_set.data(),
bs_block_size, clean_entry_bitmap_size);
} }
else if (pg.scheme == POOL_SCHEME_EC) else if (pg.scheme == POOL_SCHEME_EC)
{ {
calc_rmw_parity_ec(op_data->stripes, pg.pg_size, pg.pg_data_size, op_data->prev_set, pg.cur_set.data(), bs_block_size, clean_entry_bitmap_size); calc_rmw_parity_ec(op_data->stripes, pg.pg_size, pg.pg_data_size, op_data->prev_set,
wr_degraded(cur_op) ? op_data->prev_set : pg.cur_set.data(),
bs_block_size, clean_entry_bitmap_size);
} }
} }
// Send writes // Send writes
@@ -204,9 +220,10 @@ resume_3:
{ {
// Check that current OSD set is in history and/or add it there // Check that current OSD set is in history and/or add it there
std::vector<osd_num_t> history_set; std::vector<osd_num_t> history_set;
for (auto peer_osd: pg.cur_set) auto new_set = wr_degraded(cur_op) ? op_data->prev_set : pg.cur_set.data();
if (peer_osd != 0) for (int i = 0; i < pg.pg_size; i++)
history_set.push_back(peer_osd); if (new_set[i] != 0)
history_set.push_back(new_set[i]);
std::sort(history_set.begin(), history_set.end()); std::sort(history_set.begin(), history_set.end());
auto it = std::lower_bound(pg.target_history.begin(), pg.target_history.end(), history_set); auto it = std::lower_bound(pg.target_history.begin(), pg.target_history.end(), history_set);
if (it == pg.target_history.end() || *it != history_set) if (it == pg.target_history.end() || *it != history_set)
@@ -229,7 +246,8 @@ resume_10:
pg_cancel_write_queue(pg, cur_op, op_data->oid, -EPIPE); pg_cancel_write_queue(pg, cur_op, op_data->oid, -EPIPE);
return; return;
} }
submit_primary_subops(SUBMIT_WRITE, op_data->target_ver, pg.cur_set.data(), cur_op); submit_primary_subops(SUBMIT_WRITE, op_data->target_ver,
wr_degraded(cur_op) ? op_data->prev_set : pg.cur_set.data(), cur_op);
resume_4: resume_4:
if (op_data->n_subops > 0) if (op_data->n_subops > 0)
{ {
@@ -248,7 +266,8 @@ resume_5:
{ {
if (pg.scheme != POOL_SCHEME_REPLICATED) if (pg.scheme != POOL_SCHEME_REPLICATED)
{ {
submit_primary_rollback_subops(cur_op, pg.cur_set.data()); submit_primary_rollback_subops(cur_op,
wr_degraded(cur_op) ? op_data->prev_set : pg.cur_set.data());
resume_11: resume_11:
if (op_data->n_subops > 0) if (op_data->n_subops > 0)
{ {
@@ -283,12 +302,24 @@ resume_12:
pg_cancel_write_queue(pg, cur_op, op_data->oid, op_data->errcode); pg_cancel_write_queue(pg, cur_op, op_data->oid, op_data->errcode);
return; return;
} }
if (op_data->object_state &&
op_data->object_state->has_extra &&
wr_degraded(cur_op))
{
// Mark possible extra copies which were not overwritten as outdated
mark_partial_write(pg, cur_op);
}
if (op_data->subops)
{
delete[] op_data->subops;
op_data->subops = NULL;
}
if (pg.scheme != POOL_SCHEME_REPLICATED) if (pg.scheme != POOL_SCHEME_REPLICATED)
{ {
// Remove version override just after the write, but before stabilizing // Remove version override just after the write, but before stabilizing
pg.ver_override.erase(op_data->oid); pg.ver_override.erase(op_data->oid);
} }
if (op_data->object_state) if (op_data->object_state && !wr_degraded(cur_op))
{ {
// Any kind of a non-clean object can have extra chunks, because we don't record objects // Any kind of a non-clean object can have extra chunks, because we don't record objects
// as degraded & misplaced or incomplete & misplaced at the same time. So try to remove extra chunks // as degraded & misplaced or incomplete & misplaced at the same time. So try to remove extra chunks
@@ -339,8 +370,7 @@ resume_12:
} }
resume_6: resume_6:
resume_7: resume_7:
op_data->n_subops = 0; if (!remember_unstable_write(cur_op, pg, wr_degraded(cur_op) ? op_data->prev_set : pg.cur_set.data(), 6))
if (!remember_unstable_write(cur_op, pg, pg.cur_loc_set, 6))
{ {
return; return;
} }
@@ -350,7 +380,7 @@ resume_7:
pg.clean_count++; pg.clean_count++;
pg.total_count++; pg.total_count++;
} }
if (op_data->object_state) if (op_data->object_state && !wr_degraded(cur_op))
{ {
{ {
int recovery_type = op_data->object_state->state & (OBJ_DEGRADED|OBJ_INCOMPLETE) ? 0 : 1; int recovery_type = op_data->object_state->state & (OBJ_DEGRADED|OBJ_INCOMPLETE) ? 0 : 1;
@@ -375,16 +405,21 @@ resume_7:
); );
recovery_stat[recovery_type].usec += usec; recovery_stat[recovery_type].usec += usec;
} }
if (immediate_commit == IMMEDIATE_ALL) if (immediate_commit != IMMEDIATE_ALL)
{
// Do not delete anything, it will be deleted as part of copies_to_delete_after_sync
deref_object_state(pg, &op_data->object_state, true);
}
else
{ {
submit_primary_del_subops(cur_op, pg.cur_set.data(), pg.pg_size, op_data->object_state->osd_set); submit_primary_del_subops(cur_op, pg.cur_set.data(), pg.pg_size, op_data->object_state->osd_set);
} deref_object_state(pg, &op_data->object_state, true);
deref_object_state(pg, &op_data->object_state, true); if (op_data->n_subops > 0)
if (op_data->n_subops > 0) {
{
resume_8: resume_8:
op_data->st = 8; op_data->st = 8;
return; return;
}
resume_9: resume_9:
if (op_data->errors > 0) if (op_data->errors > 0)
{ {
@@ -463,17 +498,13 @@ void osd_t::on_change_pg_history_hook(pool_id_t pool_id, pg_num_t pg_num)
} }
} }
bool osd_t::remember_unstable_write(osd_op_t *cur_op, pg_t & pg, pg_osd_set_t & loc_set, int base_state) bool osd_t::remember_unstable_write(osd_op_t *cur_op, pg_t & pg, uint64_t *cur_set, int base_state)
{ {
osd_primary_op_data_t *op_data = cur_op->op_data; osd_primary_op_data_t *op_data = cur_op->op_data;
if (op_data->st == base_state) if (op_data->st == base_state)
{
goto resume_6; goto resume_6;
}
else if (op_data->st == base_state+1) else if (op_data->st == base_state+1)
{
goto resume_7; goto resume_7;
}
if (immediate_commit == IMMEDIATE_ALL) if (immediate_commit == IMMEDIATE_ALL)
{ {
immediate: immediate:
@@ -481,24 +512,27 @@ immediate:
{ {
// Send STABILIZE ops immediately // Send STABILIZE ops immediately
op_data->unstable_write_osds = new std::vector<unstable_osd_num_t>(); op_data->unstable_write_osds = new std::vector<unstable_osd_num_t>();
op_data->unstable_writes = new obj_ver_id[loc_set.size()]; op_data->unstable_writes = new obj_ver_id[pg.pg_size];
{ {
int last_start = 0; int last_start = 0;
for (auto & chunk: loc_set) for (int role = 0; role < pg.pg_size; role++)
{ {
op_data->unstable_writes[last_start] = (obj_ver_id){ if (cur_set[role] != 0)
.oid = { {
.inode = op_data->oid.inode, op_data->unstable_writes[last_start] = (obj_ver_id){
.stripe = op_data->oid.stripe | chunk.role, .oid = {
}, .inode = op_data->oid.inode,
.version = op_data->fact_ver, .stripe = op_data->oid.stripe | role,
}; },
op_data->unstable_write_osds->push_back((unstable_osd_num_t){ .version = op_data->fact_ver,
.osd_num = chunk.osd_num, };
.start = last_start, op_data->unstable_write_osds->push_back((unstable_osd_num_t){
.len = 1, .osd_num = cur_set[role],
}); .start = last_start,
last_start++; .len = 1,
});
last_start++;
}
} }
} }
submit_primary_stab_subops(cur_op); submit_primary_stab_subops(cur_op);
@@ -542,24 +576,30 @@ lazy:
if (pg.scheme != POOL_SCHEME_REPLICATED) if (pg.scheme != POOL_SCHEME_REPLICATED)
{ {
// Remember version as unstable for EC/XOR // Remember version as unstable for EC/XOR
for (auto & chunk: loc_set) for (int role = 0; role < pg.pg_size; role++)
{ {
this->dirty_osds.insert(chunk.osd_num); if (cur_set[role] != 0)
this->unstable_writes[(osd_object_id_t){ {
.osd_num = chunk.osd_num, this->dirty_osds.insert(cur_set[role]);
.oid = { this->unstable_writes[(osd_object_id_t){
.inode = op_data->oid.inode, .osd_num = cur_set[role],
.stripe = op_data->oid.stripe | chunk.role, .oid = {
}, .inode = op_data->oid.inode,
}] = op_data->fact_ver; .stripe = op_data->oid.stripe | role,
},
}] = op_data->fact_ver;
}
} }
} }
else else
{ {
// Only remember to sync OSDs for replicated pools // Only remember to sync OSDs for replicated pools
for (auto & chunk: loc_set) for (int role = 0; role < pg.pg_size; role++)
{ {
this->dirty_osds.insert(chunk.osd_num); if (cur_set[role] != 0)
{
this->dirty_osds.insert(cur_set[role]);
}
} }
} }
// Remember PG as dirty to drop the connection when PG goes offline // Remember PG as dirty to drop the connection when PG goes offline
+9
View File
@@ -1,10 +1,19 @@
#!/bin/bash -ex #!/bin/bash -ex
OSD_SIZE=200 OSD_SIZE=200
if [[ $SCHEME = replicated ]]; then
OSD_COUNT=${OSD_COUNT:-2}
fi
GLOBAL_CONFIG=',"client_retry_enospc":false' GLOBAL_CONFIG=',"client_retry_enospc":false'
. `dirname $0`/run_3osds.sh . `dirname $0`/run_3osds.sh
kill -9 $OSD1_PID
truncate -s 0 ./testdata/bin/test_osd1.bin
dd if=/dev/zero of=./testdata/bin/test_osd1.bin bs=1024 count=1 seek=$((300*1024-1))
$ETCDCTL del /vitastor/osd/state/1
start_osd 1
# Should fail with ENOSPC # Should fail with ENOSPC
if $VITASTOR_FIO -bs=1M -direct=1 -iodepth=4 \ if $VITASTOR_FIO -bs=1M -direct=1 -iodepth=4 \
-rw=write -pool=1 -inode=1 -size=500M -cluster_log_level=10; then -rw=write -pool=1 -inode=1 -size=500M -cluster_log_level=10; then