drbd: RCU for rs_plan_s
This removes the issue with using peer_seq_lock out of different contexts. Signed-off-by: Philipp Reisner <philipp.reisner@linbit.com> Signed-off-by: Lars Ellenberg <lars.ellenberg@linbit.com>
This commit is contained in:
parent
d589a21e5d
commit
813472ced7
|
@ -998,7 +998,7 @@ struct drbd_conf {
|
||||||
int rs_last_events; /* counter of read or write "events" (unit sectors)
|
int rs_last_events; /* counter of read or write "events" (unit sectors)
|
||||||
* on the lower level device when we last looked. */
|
* on the lower level device when we last looked. */
|
||||||
int c_sync_rate; /* current resync rate after syncer throttle magic */
|
int c_sync_rate; /* current resync rate after syncer throttle magic */
|
||||||
struct fifo_buffer *rs_plan_s; /* correction values of resync planer */
|
struct fifo_buffer *rs_plan_s; /* correction values of resync planer (RCU, tconn->conn_update) */
|
||||||
int rs_in_flight; /* resync sectors in flight (to proxy, in proxy and from proxy) */
|
int rs_in_flight; /* resync sectors in flight (to proxy, in proxy and from proxy) */
|
||||||
atomic_t ap_in_flight; /* App sectors in flight (waiting for ack) */
|
atomic_t ap_in_flight; /* App sectors in flight (waiting for ack) */
|
||||||
int peer_max_bio_size;
|
int peer_max_bio_size;
|
||||||
|
|
|
@ -1115,7 +1115,7 @@ int drbd_adm_disk_opts(struct sk_buff *skb, struct genl_info *info)
|
||||||
enum drbd_ret_code retcode;
|
enum drbd_ret_code retcode;
|
||||||
struct drbd_conf *mdev;
|
struct drbd_conf *mdev;
|
||||||
struct disk_conf *new_disk_conf, *old_disk_conf;
|
struct disk_conf *new_disk_conf, *old_disk_conf;
|
||||||
struct fifo_buffer *rs_plan_s = NULL;
|
struct fifo_buffer *old_plan = NULL, *new_plan = NULL;
|
||||||
int err, fifo_size;
|
int err, fifo_size;
|
||||||
|
|
||||||
retcode = drbd_adm_prepare(skb, info, DRBD_ADM_NEED_MINOR);
|
retcode = drbd_adm_prepare(skb, info, DRBD_ADM_NEED_MINOR);
|
||||||
|
@ -1158,8 +1158,8 @@ int drbd_adm_disk_opts(struct sk_buff *skb, struct genl_info *info)
|
||||||
|
|
||||||
fifo_size = (new_disk_conf->c_plan_ahead * 10 * SLEEP_TIME) / HZ;
|
fifo_size = (new_disk_conf->c_plan_ahead * 10 * SLEEP_TIME) / HZ;
|
||||||
if (fifo_size != mdev->rs_plan_s->size) {
|
if (fifo_size != mdev->rs_plan_s->size) {
|
||||||
rs_plan_s = fifo_alloc(fifo_size);
|
new_plan = fifo_alloc(fifo_size);
|
||||||
if (!rs_plan_s) {
|
if (!new_plan) {
|
||||||
dev_err(DEV, "kmalloc of fifo_buffer failed");
|
dev_err(DEV, "kmalloc of fifo_buffer failed");
|
||||||
retcode = ERR_NOMEM;
|
retcode = ERR_NOMEM;
|
||||||
goto fail_unlock;
|
goto fail_unlock;
|
||||||
|
@ -1188,13 +1188,10 @@ int drbd_adm_disk_opts(struct sk_buff *skb, struct genl_info *info)
|
||||||
if (retcode != NO_ERROR)
|
if (retcode != NO_ERROR)
|
||||||
goto fail_unlock;
|
goto fail_unlock;
|
||||||
|
|
||||||
spin_lock(&mdev->peer_seq_lock);
|
if (new_plan) {
|
||||||
if (rs_plan_s) {
|
old_plan = mdev->rs_plan_s;
|
||||||
kfree(mdev->rs_plan_s);
|
rcu_assign_pointer(mdev->rs_plan_s, new_plan);
|
||||||
mdev->rs_plan_s = rs_plan_s;
|
|
||||||
rs_plan_s = NULL;
|
|
||||||
}
|
}
|
||||||
spin_unlock(&mdev->peer_seq_lock);
|
|
||||||
|
|
||||||
drbd_md_sync(mdev);
|
drbd_md_sync(mdev);
|
||||||
|
|
||||||
|
@ -1204,13 +1201,14 @@ int drbd_adm_disk_opts(struct sk_buff *skb, struct genl_info *info)
|
||||||
mutex_unlock(&mdev->tconn->conf_update);
|
mutex_unlock(&mdev->tconn->conf_update);
|
||||||
synchronize_rcu();
|
synchronize_rcu();
|
||||||
kfree(old_disk_conf);
|
kfree(old_disk_conf);
|
||||||
|
kfree(old_plan);
|
||||||
goto success;
|
goto success;
|
||||||
|
|
||||||
fail_unlock:
|
fail_unlock:
|
||||||
mutex_unlock(&mdev->tconn->conf_update);
|
mutex_unlock(&mdev->tconn->conf_update);
|
||||||
fail:
|
fail:
|
||||||
kfree(new_disk_conf);
|
kfree(new_disk_conf);
|
||||||
kfree(rs_plan_s);
|
kfree(new_plan);
|
||||||
success:
|
success:
|
||||||
put_ldev(mdev);
|
put_ldev(mdev);
|
||||||
out:
|
out:
|
||||||
|
|
|
@ -3157,9 +3157,9 @@ static int receive_SyncParam(struct drbd_tconn *tconn, struct packet_info *pi)
|
||||||
struct crypto_hash *verify_tfm = NULL;
|
struct crypto_hash *verify_tfm = NULL;
|
||||||
struct crypto_hash *csums_tfm = NULL;
|
struct crypto_hash *csums_tfm = NULL;
|
||||||
struct net_conf *old_net_conf, *new_net_conf = NULL;
|
struct net_conf *old_net_conf, *new_net_conf = NULL;
|
||||||
struct disk_conf *old_disk_conf, *new_disk_conf = NULL;
|
struct disk_conf *old_disk_conf = NULL, *new_disk_conf = NULL;
|
||||||
const int apv = tconn->agreed_pro_version;
|
const int apv = tconn->agreed_pro_version;
|
||||||
struct fifo_buffer *rs_plan_s = NULL;
|
struct fifo_buffer *old_plan = NULL, *new_plan = NULL;
|
||||||
int fifo_size = 0;
|
int fifo_size = 0;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
@ -3200,18 +3200,22 @@ static int receive_SyncParam(struct drbd_tconn *tconn, struct packet_info *pi)
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
new_disk_conf = kzalloc(sizeof(struct disk_conf), GFP_KERNEL);
|
|
||||||
if (!new_disk_conf) {
|
|
||||||
dev_err(DEV, "Allocation of new disk_conf failed\n");
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
mutex_lock(&mdev->tconn->conf_update);
|
mutex_lock(&mdev->tconn->conf_update);
|
||||||
old_net_conf = mdev->tconn->net_conf;
|
old_net_conf = mdev->tconn->net_conf;
|
||||||
old_disk_conf = mdev->ldev->disk_conf;
|
if (get_ldev(mdev)) {
|
||||||
*new_disk_conf = *old_disk_conf;
|
new_disk_conf = kzalloc(sizeof(struct disk_conf), GFP_KERNEL);
|
||||||
|
if (!new_disk_conf) {
|
||||||
|
put_ldev(mdev);
|
||||||
|
mutex_unlock(&mdev->tconn->conf_update);
|
||||||
|
dev_err(DEV, "Allocation of new disk_conf failed\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
new_disk_conf->resync_rate = be32_to_cpu(p->rate);
|
old_disk_conf = mdev->ldev->disk_conf;
|
||||||
|
*new_disk_conf = *old_disk_conf;
|
||||||
|
|
||||||
|
new_disk_conf->resync_rate = be32_to_cpu(p->rate);
|
||||||
|
}
|
||||||
|
|
||||||
if (apv >= 88) {
|
if (apv >= 88) {
|
||||||
if (apv == 88) {
|
if (apv == 88) {
|
||||||
|
@ -3219,15 +3223,13 @@ static int receive_SyncParam(struct drbd_tconn *tconn, struct packet_info *pi)
|
||||||
dev_err(DEV, "verify-alg too long, "
|
dev_err(DEV, "verify-alg too long, "
|
||||||
"peer wants %u, accepting only %u byte\n",
|
"peer wants %u, accepting only %u byte\n",
|
||||||
data_size, SHARED_SECRET_MAX);
|
data_size, SHARED_SECRET_MAX);
|
||||||
mutex_unlock(&mdev->tconn->conf_update);
|
err = -EIO;
|
||||||
return -EIO;
|
goto reconnect;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = drbd_recv_all(mdev->tconn, p->verify_alg, data_size);
|
err = drbd_recv_all(mdev->tconn, p->verify_alg, data_size);
|
||||||
if (err) {
|
if (err)
|
||||||
mutex_unlock(&mdev->tconn->conf_update);
|
goto reconnect;
|
||||||
return err;
|
|
||||||
}
|
|
||||||
/* we expect NUL terminated string */
|
/* we expect NUL terminated string */
|
||||||
/* but just in case someone tries to be evil */
|
/* but just in case someone tries to be evil */
|
||||||
D_ASSERT(p->verify_alg[data_size-1] == 0);
|
D_ASSERT(p->verify_alg[data_size-1] == 0);
|
||||||
|
@ -3270,7 +3272,7 @@ static int receive_SyncParam(struct drbd_tconn *tconn, struct packet_info *pi)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (apv > 94) {
|
if (apv > 94 && new_disk_conf) {
|
||||||
new_disk_conf->c_plan_ahead = be32_to_cpu(p->c_plan_ahead);
|
new_disk_conf->c_plan_ahead = be32_to_cpu(p->c_plan_ahead);
|
||||||
new_disk_conf->c_delay_target = be32_to_cpu(p->c_delay_target);
|
new_disk_conf->c_delay_target = be32_to_cpu(p->c_delay_target);
|
||||||
new_disk_conf->c_fill_target = be32_to_cpu(p->c_fill_target);
|
new_disk_conf->c_fill_target = be32_to_cpu(p->c_fill_target);
|
||||||
|
@ -3278,8 +3280,8 @@ static int receive_SyncParam(struct drbd_tconn *tconn, struct packet_info *pi)
|
||||||
|
|
||||||
fifo_size = (new_disk_conf->c_plan_ahead * 10 * SLEEP_TIME) / HZ;
|
fifo_size = (new_disk_conf->c_plan_ahead * 10 * SLEEP_TIME) / HZ;
|
||||||
if (fifo_size != mdev->rs_plan_s->size) {
|
if (fifo_size != mdev->rs_plan_s->size) {
|
||||||
rs_plan_s = fifo_alloc(fifo_size);
|
new_plan = fifo_alloc(fifo_size);
|
||||||
if (!rs_plan_s) {
|
if (!new_plan) {
|
||||||
dev_err(DEV, "kmalloc of fifo_buffer failed");
|
dev_err(DEV, "kmalloc of fifo_buffer failed");
|
||||||
put_ldev(mdev);
|
put_ldev(mdev);
|
||||||
goto disconnect;
|
goto disconnect;
|
||||||
|
@ -3314,24 +3316,39 @@ static int receive_SyncParam(struct drbd_tconn *tconn, struct packet_info *pi)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rcu_assign_pointer(mdev->ldev->disk_conf, new_disk_conf);
|
if (new_disk_conf) {
|
||||||
spin_lock(&mdev->peer_seq_lock);
|
rcu_assign_pointer(mdev->ldev->disk_conf, new_disk_conf);
|
||||||
if (rs_plan_s) {
|
put_ldev(mdev);
|
||||||
kfree(mdev->rs_plan_s);
|
}
|
||||||
mdev->rs_plan_s = rs_plan_s;
|
|
||||||
|
if (new_plan) {
|
||||||
|
old_plan = mdev->rs_plan_s;
|
||||||
|
rcu_assign_pointer(mdev->rs_plan_s, new_plan);
|
||||||
}
|
}
|
||||||
spin_unlock(&mdev->peer_seq_lock);
|
|
||||||
|
|
||||||
mutex_unlock(&mdev->tconn->conf_update);
|
mutex_unlock(&mdev->tconn->conf_update);
|
||||||
synchronize_rcu();
|
synchronize_rcu();
|
||||||
if (new_net_conf)
|
if (new_net_conf)
|
||||||
kfree(old_net_conf);
|
kfree(old_net_conf);
|
||||||
kfree(old_disk_conf);
|
kfree(old_disk_conf);
|
||||||
|
kfree(old_plan);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
reconnect:
|
||||||
|
if (new_disk_conf) {
|
||||||
|
put_ldev(mdev);
|
||||||
|
kfree(new_disk_conf);
|
||||||
|
}
|
||||||
|
mutex_unlock(&mdev->tconn->conf_update);
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
disconnect:
|
disconnect:
|
||||||
kfree(rs_plan_s);
|
kfree(new_plan);
|
||||||
|
if (new_disk_conf) {
|
||||||
|
put_ldev(mdev);
|
||||||
|
kfree(new_disk_conf);
|
||||||
|
}
|
||||||
mutex_unlock(&mdev->tconn->conf_update);
|
mutex_unlock(&mdev->tconn->conf_update);
|
||||||
/* just for completeness: actually not needed,
|
/* just for completeness: actually not needed,
|
||||||
* as this is not reached if csums_tfm was ok. */
|
* as this is not reached if csums_tfm was ok. */
|
||||||
|
|
|
@ -460,15 +460,15 @@ static int drbd_rs_controller(struct drbd_conf *mdev)
|
||||||
int steps; /* Number of time steps to plan ahead */
|
int steps; /* Number of time steps to plan ahead */
|
||||||
int curr_corr;
|
int curr_corr;
|
||||||
int max_sect;
|
int max_sect;
|
||||||
|
struct fifo_buffer *plan;
|
||||||
|
|
||||||
sect_in = atomic_xchg(&mdev->rs_sect_in, 0); /* Number of sectors that came in */
|
sect_in = atomic_xchg(&mdev->rs_sect_in, 0); /* Number of sectors that came in */
|
||||||
mdev->rs_in_flight -= sect_in;
|
mdev->rs_in_flight -= sect_in;
|
||||||
|
|
||||||
spin_lock(&mdev->peer_seq_lock); /* get an atomic view on mdev->rs_plan_s */
|
|
||||||
rcu_read_lock();
|
|
||||||
dc = rcu_dereference(mdev->ldev->disk_conf);
|
dc = rcu_dereference(mdev->ldev->disk_conf);
|
||||||
|
plan = rcu_dereference(mdev->rs_plan_s);
|
||||||
|
|
||||||
steps = mdev->rs_plan_s->size; /* (dc->c_plan_ahead * 10 * SLEEP_TIME) / HZ; */
|
steps = plan->size; /* (dc->c_plan_ahead * 10 * SLEEP_TIME) / HZ; */
|
||||||
|
|
||||||
if (mdev->rs_in_flight + sect_in == 0) { /* At start of resync */
|
if (mdev->rs_in_flight + sect_in == 0) { /* At start of resync */
|
||||||
want = ((dc->resync_rate * 2 * SLEEP_TIME) / HZ) * steps;
|
want = ((dc->resync_rate * 2 * SLEEP_TIME) / HZ) * steps;
|
||||||
|
@ -477,16 +477,16 @@ static int drbd_rs_controller(struct drbd_conf *mdev)
|
||||||
sect_in * dc->c_delay_target * HZ / (SLEEP_TIME * 10);
|
sect_in * dc->c_delay_target * HZ / (SLEEP_TIME * 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
correction = want - mdev->rs_in_flight - mdev->rs_plan_s->total;
|
correction = want - mdev->rs_in_flight - plan->total;
|
||||||
|
|
||||||
/* Plan ahead */
|
/* Plan ahead */
|
||||||
cps = correction / steps;
|
cps = correction / steps;
|
||||||
fifo_add_val(mdev->rs_plan_s, cps);
|
fifo_add_val(plan, cps);
|
||||||
mdev->rs_plan_s->total += cps * steps;
|
plan->total += cps * steps;
|
||||||
|
|
||||||
/* What we do in this step */
|
/* What we do in this step */
|
||||||
curr_corr = fifo_push(mdev->rs_plan_s, 0);
|
curr_corr = fifo_push(plan, 0);
|
||||||
mdev->rs_plan_s->total -= curr_corr;
|
plan->total -= curr_corr;
|
||||||
|
|
||||||
req_sect = sect_in + curr_corr;
|
req_sect = sect_in + curr_corr;
|
||||||
if (req_sect < 0)
|
if (req_sect < 0)
|
||||||
|
@ -501,8 +501,6 @@ static int drbd_rs_controller(struct drbd_conf *mdev)
|
||||||
sect_in, mdev->rs_in_flight, want, correction,
|
sect_in, mdev->rs_in_flight, want, correction,
|
||||||
steps, cps, mdev->rs_planed, curr_corr, req_sect);
|
steps, cps, mdev->rs_planed, curr_corr, req_sect);
|
||||||
*/
|
*/
|
||||||
rcu_read_unlock();
|
|
||||||
spin_unlock(&mdev->peer_seq_lock);
|
|
||||||
|
|
||||||
return req_sect;
|
return req_sect;
|
||||||
}
|
}
|
||||||
|
@ -510,15 +508,16 @@ static int drbd_rs_controller(struct drbd_conf *mdev)
|
||||||
static int drbd_rs_number_requests(struct drbd_conf *mdev)
|
static int drbd_rs_number_requests(struct drbd_conf *mdev)
|
||||||
{
|
{
|
||||||
int number;
|
int number;
|
||||||
if (mdev->rs_plan_s->size) { /* rcu_dereference(mdev->ldev->disk_conf)->c_plan_ahead */
|
|
||||||
|
rcu_read_lock();
|
||||||
|
if (rcu_dereference(mdev->rs_plan_s)->size) {
|
||||||
number = drbd_rs_controller(mdev) >> (BM_BLOCK_SHIFT - 9);
|
number = drbd_rs_controller(mdev) >> (BM_BLOCK_SHIFT - 9);
|
||||||
mdev->c_sync_rate = number * HZ * (BM_BLOCK_SIZE / 1024) / SLEEP_TIME;
|
mdev->c_sync_rate = number * HZ * (BM_BLOCK_SIZE / 1024) / SLEEP_TIME;
|
||||||
} else {
|
} else {
|
||||||
rcu_read_lock();
|
|
||||||
mdev->c_sync_rate = rcu_dereference(mdev->ldev->disk_conf)->resync_rate;
|
mdev->c_sync_rate = rcu_dereference(mdev->ldev->disk_conf)->resync_rate;
|
||||||
rcu_read_unlock();
|
|
||||||
number = SLEEP_TIME * mdev->c_sync_rate / ((BM_BLOCK_SIZE / 1024) * HZ);
|
number = SLEEP_TIME * mdev->c_sync_rate / ((BM_BLOCK_SIZE / 1024) * HZ);
|
||||||
}
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
/* ignore the amount of pending requests, the resync controller should
|
/* ignore the amount of pending requests, the resync controller should
|
||||||
* throttle down to incoming reply rate soon enough anyways. */
|
* throttle down to incoming reply rate soon enough anyways. */
|
||||||
|
@ -1468,13 +1467,21 @@ void drbd_sync_after_changed(struct drbd_conf *mdev)
|
||||||
|
|
||||||
void drbd_rs_controller_reset(struct drbd_conf *mdev)
|
void drbd_rs_controller_reset(struct drbd_conf *mdev)
|
||||||
{
|
{
|
||||||
|
struct fifo_buffer *plan;
|
||||||
|
|
||||||
atomic_set(&mdev->rs_sect_in, 0);
|
atomic_set(&mdev->rs_sect_in, 0);
|
||||||
atomic_set(&mdev->rs_sect_ev, 0);
|
atomic_set(&mdev->rs_sect_ev, 0);
|
||||||
mdev->rs_in_flight = 0;
|
mdev->rs_in_flight = 0;
|
||||||
mdev->rs_plan_s->total = 0;
|
|
||||||
spin_lock(&mdev->peer_seq_lock);
|
/* Updating the RCU protected object in place is necessary since
|
||||||
fifo_set(mdev->rs_plan_s, 0);
|
this function gets called from atomic context.
|
||||||
spin_unlock(&mdev->peer_seq_lock);
|
It is valid since all other updates also lead to an completely
|
||||||
|
empty fifo */
|
||||||
|
rcu_read_lock();
|
||||||
|
plan = rcu_dereference(mdev->rs_plan_s);
|
||||||
|
plan->total = 0;
|
||||||
|
fifo_set(plan, 0);
|
||||||
|
rcu_read_unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void start_resync_timer_fn(unsigned long data)
|
void start_resync_timer_fn(unsigned long data)
|
||||||
|
|
Loading…
Reference in New Issue