2017-01-08 12:13:18 +03:00
|
|
|
|
2017-03-20 02:56:34 +03:00
|
|
|
#include "bcachefs.h"
|
2017-01-08 12:13:18 +03:00
|
|
|
#include "alloc.h"
|
|
|
|
#include "btree_iter.h"
|
|
|
|
#include "buckets.h"
|
|
|
|
#include "clock.h"
|
|
|
|
#include "extents.h"
|
|
|
|
#include "io.h"
|
|
|
|
#include "move.h"
|
2016-10-04 06:22:17 +03:00
|
|
|
#include "super-io.h"
|
2017-01-08 12:13:18 +03:00
|
|
|
#include "tier.h"
|
|
|
|
|
|
|
|
#include <linux/freezer.h>
|
|
|
|
#include <linux/kthread.h>
|
2017-03-20 02:56:34 +03:00
|
|
|
#include <trace/events/bcachefs.h>
|
2017-01-08 12:13:18 +03:00
|
|
|
|
2017-12-14 00:01:18 +03:00
|
|
|
static bool tiering_pred(void *arg, struct bkey_s_c_extent e)
|
2017-01-08 12:13:18 +03:00
|
|
|
{
|
2017-12-14 00:01:18 +03:00
|
|
|
struct bch_tier *tier = arg;
|
|
|
|
struct bch_fs *c = container_of(tier, struct bch_fs, tiers[tier->idx]);
|
|
|
|
const struct bch_extent_ptr *ptr;
|
|
|
|
unsigned replicas = 0;
|
2017-01-08 12:13:18 +03:00
|
|
|
|
2017-12-14 00:01:18 +03:00
|
|
|
/* Make sure we have room to add a new pointer: */
|
|
|
|
if (bkey_val_u64s(e.k) + BKEY_EXTENT_PTR_U64s_MAX >
|
|
|
|
BKEY_EXTENT_VAL_U64s_MAX)
|
|
|
|
return false;
|
2017-01-08 12:13:18 +03:00
|
|
|
|
2017-12-14 00:01:18 +03:00
|
|
|
extent_for_each_ptr(e, ptr)
|
2017-12-22 02:00:30 +03:00
|
|
|
if (bch_dev_bkey_exists(c, ptr->dev)->mi.tier >= tier->idx)
|
2017-12-14 00:01:18 +03:00
|
|
|
replicas++;
|
2017-01-08 12:13:18 +03:00
|
|
|
|
2017-12-14 00:01:18 +03:00
|
|
|
return replicas < c->opts.data_replicas;
|
2017-01-08 12:13:18 +03:00
|
|
|
}
|
|
|
|
|
2017-03-20 02:56:34 +03:00
|
|
|
static int bch2_tiering_thread(void *arg)
|
2017-01-08 12:13:18 +03:00
|
|
|
{
|
2017-03-01 13:45:15 +03:00
|
|
|
struct bch_tier *tier = arg;
|
2017-03-11 00:40:01 +03:00
|
|
|
struct bch_fs *c = container_of(tier, struct bch_fs, tiers[tier->idx]);
|
2017-01-08 12:13:18 +03:00
|
|
|
struct io_clock *clock = &c->io_clock[WRITE];
|
2017-03-11 00:40:01 +03:00
|
|
|
struct bch_dev *ca;
|
2017-12-14 00:01:18 +03:00
|
|
|
u64 tier_capacity, available_sectors, keys_moved, sectors_moved;
|
2017-01-08 12:13:18 +03:00
|
|
|
unsigned long last;
|
2017-12-14 00:01:18 +03:00
|
|
|
unsigned i, nr_devices;
|
2017-01-08 12:13:18 +03:00
|
|
|
|
|
|
|
set_freezable();
|
|
|
|
|
|
|
|
while (!kthread_should_stop()) {
|
|
|
|
if (kthread_wait_freezable(c->tiering_enabled &&
|
2017-12-14 00:01:18 +03:00
|
|
|
(nr_devices = dev_mask_nr(&tier->devs))))
|
2017-01-08 12:13:18 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
while (1) {
|
2017-03-01 13:45:15 +03:00
|
|
|
struct bch_tier *faster_tier;
|
2017-01-08 12:13:18 +03:00
|
|
|
|
|
|
|
last = atomic_long_read(&clock->now);
|
|
|
|
|
|
|
|
tier_capacity = available_sectors = 0;
|
2017-03-01 13:45:15 +03:00
|
|
|
for (faster_tier = c->tiers;
|
2017-01-08 12:13:18 +03:00
|
|
|
faster_tier != tier;
|
|
|
|
faster_tier++) {
|
2017-10-06 01:41:44 +03:00
|
|
|
rcu_read_lock();
|
|
|
|
for_each_member_device_rcu(ca, c, i,
|
|
|
|
&faster_tier->devs) {
|
2017-01-08 12:13:18 +03:00
|
|
|
tier_capacity +=
|
2017-10-06 01:41:44 +03:00
|
|
|
bucket_to_sector(ca,
|
|
|
|
ca->mi.nbuckets -
|
|
|
|
ca->mi.first_bucket);
|
2017-01-08 12:13:18 +03:00
|
|
|
available_sectors +=
|
2017-10-06 01:41:44 +03:00
|
|
|
bucket_to_sector(ca,
|
2017-12-14 00:01:18 +03:00
|
|
|
dev_buckets_available(c, ca));
|
2017-01-08 12:13:18 +03:00
|
|
|
}
|
2017-10-06 01:41:44 +03:00
|
|
|
rcu_read_unlock();
|
2017-01-08 12:13:18 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (available_sectors < (tier_capacity >> 1))
|
|
|
|
break;
|
|
|
|
|
2017-03-20 02:56:34 +03:00
|
|
|
bch2_kthread_io_clock_wait(clock,
|
2017-01-08 12:13:18 +03:00
|
|
|
last +
|
|
|
|
available_sectors -
|
|
|
|
(tier_capacity >> 1));
|
|
|
|
if (kthread_should_stop())
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-12-14 00:01:18 +03:00
|
|
|
bch2_move_data(c, &tier->pd.rate,
|
|
|
|
SECTORS_IN_FLIGHT_PER_DEVICE * nr_devices,
|
|
|
|
&tier->devs,
|
|
|
|
writepoint_ptr(&tier->wp),
|
|
|
|
0,
|
|
|
|
-1,
|
|
|
|
tiering_pred, tier,
|
|
|
|
&keys_moved,
|
|
|
|
§ors_moved);
|
2017-01-08 12:13:18 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-03-20 02:56:34 +03:00
|
|
|
static void __bch2_tiering_stop(struct bch_tier *tier)
|
2017-01-08 12:13:18 +03:00
|
|
|
{
|
2017-03-01 13:45:15 +03:00
|
|
|
tier->pd.rate.rate = UINT_MAX;
|
2017-03-20 02:56:34 +03:00
|
|
|
bch2_ratelimit_reset(&tier->pd.rate);
|
2017-03-01 13:45:15 +03:00
|
|
|
|
|
|
|
if (tier->migrate)
|
|
|
|
kthread_stop(tier->migrate);
|
|
|
|
|
|
|
|
tier->migrate = NULL;
|
|
|
|
}
|
|
|
|
|
2017-03-20 02:56:34 +03:00
|
|
|
void bch2_tiering_stop(struct bch_fs *c)
|
2017-03-01 13:45:15 +03:00
|
|
|
{
|
|
|
|
struct bch_tier *tier;
|
|
|
|
|
|
|
|
for (tier = c->tiers; tier < c->tiers + ARRAY_SIZE(c->tiers); tier++)
|
2017-03-20 02:56:34 +03:00
|
|
|
__bch2_tiering_stop(tier);
|
2017-03-01 13:45:15 +03:00
|
|
|
}
|
|
|
|
|
2017-03-20 02:56:34 +03:00
|
|
|
static int __bch2_tiering_start(struct bch_tier *tier)
|
2017-03-01 13:45:15 +03:00
|
|
|
{
|
|
|
|
if (!tier->migrate) {
|
|
|
|
struct task_struct *p =
|
2017-03-20 02:56:34 +03:00
|
|
|
kthread_create(bch2_tiering_thread, tier,
|
2017-03-01 13:45:15 +03:00
|
|
|
"bch_tier[%u]", tier->idx);
|
|
|
|
if (IS_ERR(p))
|
|
|
|
return PTR_ERR(p);
|
|
|
|
|
|
|
|
tier->migrate = p;
|
|
|
|
}
|
|
|
|
|
|
|
|
wake_up_process(tier->migrate);
|
|
|
|
return 0;
|
2017-01-08 12:13:18 +03:00
|
|
|
}
|
|
|
|
|
2017-03-20 02:56:34 +03:00
|
|
|
int bch2_tiering_start(struct bch_fs *c)
|
2017-01-08 12:13:18 +03:00
|
|
|
{
|
2017-03-01 13:45:15 +03:00
|
|
|
struct bch_tier *tier;
|
|
|
|
bool have_faster_tier = false;
|
2017-01-08 12:13:18 +03:00
|
|
|
|
2017-02-02 06:16:42 +03:00
|
|
|
if (c->opts.nochanges)
|
|
|
|
return 0;
|
|
|
|
|
2017-03-01 13:45:15 +03:00
|
|
|
for (tier = c->tiers; tier < c->tiers + ARRAY_SIZE(c->tiers); tier++) {
|
2017-10-06 01:41:44 +03:00
|
|
|
if (!dev_mask_nr(&tier->devs))
|
2017-03-01 13:45:15 +03:00
|
|
|
continue;
|
2017-01-08 12:13:18 +03:00
|
|
|
|
2017-03-01 13:45:15 +03:00
|
|
|
if (have_faster_tier) {
|
2017-03-20 02:56:34 +03:00
|
|
|
int ret = __bch2_tiering_start(tier);
|
2017-03-01 13:45:15 +03:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
} else {
|
2017-03-20 02:56:34 +03:00
|
|
|
__bch2_tiering_stop(tier);
|
2017-03-01 13:45:15 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
have_faster_tier = true;
|
|
|
|
}
|
2017-01-08 12:13:18 +03:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-03-20 02:56:34 +03:00
|
|
|
void bch2_fs_tiering_init(struct bch_fs *c)
|
2017-01-08 12:13:18 +03:00
|
|
|
{
|
2017-03-01 13:45:15 +03:00
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(c->tiers); i++) {
|
|
|
|
c->tiers[i].idx = i;
|
2017-03-20 02:56:34 +03:00
|
|
|
bch2_pd_controller_init(&c->tiers[i].pd);
|
2017-01-08 12:13:18 +03:00
|
|
|
}
|
|
|
|
}
|