// SPDX-License-Identifier: GPL-2.0/* * bcache sysfs interfaces * * Copyright 2010, 2011 Kent Overstreet <kent.overstreet@gmail.com> * Copyright 2012 Google, Inc. */ #include "bcache.h"#include "sysfs.h"#include "btree.h"#include "request.h"#include "writeback.h"#include "features.h" #include <linux/blkdev.h>#include <linux/sort.h>#include <linux/sched/clock.h> extern bool bcache_is_reboot; /* Default is 0 ("writethrough") */static const char * const bch_cache_modes[] = { "writethrough", "writeback", "writearound", "none", NULL}; static const char * const bch_reada_cache_policies[] = { "all", "meta-only", NULL}; /* Default is 0 ("auto") */static const char * const bch_stop_on_failure_modes[] = { "auto", "always", NULL}; static const char * const cache_replacement_policies[] = { "lru", "fifo", "random", NULL}; static const char * const error_actions[] = { "unregister", "panic", NULL}; write_attribute(attach);write_attribute(detach);write_attribute(unregister);write_attribute(stop);write_attribute(clear_stats);write_attribute(trigger_gc);write_attribute(prune_cache);write_attribute(flash_vol_create); read_attribute(bucket_size);read_attribute(block_size);read_attribute(nbuckets);read_attribute(tree_depth);read_attribute(root_usage_percent);read_attribute(priority_stats);read_attribute(btree_cache_size);read_attribute(btree_cache_max_chain);read_attribute(cache_available_percent);read_attribute(written);read_attribute(btree_written);read_attribute(metadata_written);read_attribute(active_journal_entries);read_attribute(backing_dev_name);read_attribute(backing_dev_uuid); sysfs_time_stats_attribute(btree_gc, sec, ms);sysfs_time_stats_attribute(btree_split, sec, us);sysfs_time_stats_attribute(btree_sort, ms, us);sysfs_time_stats_attribute(btree_read, ms, us); read_attribute(btree_nodes);read_attribute(btree_used_percent);read_attribute(average_key_size);read_attribute(dirty_data);read_attribute(bset_tree_stats);read_attribute(feature_compat);read_attribute(feature_ro_compat);read_attribute(feature_incompat); read_attribute(state);read_attribute(cache_read_races);read_attribute(reclaim);read_attribute(reclaimed_journal_buckets);read_attribute(flush_write);read_attribute(writeback_keys_done);read_attribute(writeback_keys_failed);read_attribute(io_errors);read_attribute(congested);read_attribute(cutoff_writeback);read_attribute(cutoff_writeback_sync);rw_attribute(congested_read_threshold_us);rw_attribute(congested_write_threshold_us); rw_attribute(sequential_cutoff);rw_attribute(data_csum);rw_attribute(cache_mode);rw_attribute(readahead_cache_policy);rw_attribute(stop_when_cache_set_failed);rw_attribute(writeback_metadata);rw_attribute(writeback_running);rw_attribute(writeback_percent);rw_attribute(writeback_delay);rw_attribute(writeback_rate);rw_attribute(writeback_consider_fragment); rw_attribute(writeback_rate_update_seconds);rw_attribute(writeback_rate_i_term_inverse);rw_attribute(writeback_rate_p_term_inverse);rw_attribute(writeback_rate_fp_term_low);rw_attribute(writeback_rate_fp_term_mid);rw_attribute(writeback_rate_fp_term_high);rw_attribute(writeback_rate_minimum);read_attribute(writeback_rate_debug); read_attribute(stripe_size);read_attribute(partial_stripes_expensive); rw_attribute(synchronous);rw_attribute(journal_delay_ms);rw_attribute(io_disable);rw_attribute(running);rw_attribute(label);rw_attribute(errors);rw_attribute(io_error_limit);rw_attribute(io_error_halflife);rw_attribute(verify);rw_attribute(bypass_torture_test);rw_attribute(key_merging_disabled);rw_attribute(gc_always_rewrite);rw_attribute(expensive_debug_checks);rw_attribute(cache_replacement_policy);rw_attribute(btree_shrinker_disabled);rw_attribute(copy_gc_enabled);rw_attribute(idle_max_writeback_rate);rw_attribute(gc_after_writeback);rw_attribute(size); static ssize_t bch_snprint_string_list(char *buf, size_t size, const char * const list[], size_t selected){ char *out = buf; size_t i; for (i = 0; list[i]; i++) out += scnprintf(out, buf + size - out, i == selected ? "[%s] " : "%s ", list[i]); out[-1] = '\n'; return out - buf;} SHOW(__bch_cached_dev){ struct cached_dev *dc = container_of(kobj, struct cached_dev, disk.kobj); char const *states[] = { "no cache", "clean", "dirty", "inconsistent" }; int wb = dc->writeback_running; #define var(stat) (dc->stat) if (attr == &sysfs_cache_mode) return bch_snprint_string_list(buf, PAGE_SIZE, bch_cache_modes, BDEV_CACHE_MODE(&dc->sb)); if (attr == &sysfs_readahead_cache_policy) return bch_snprint_string_list(buf, PAGE_SIZE, bch_reada_cache_policies, dc->cache_readahead_policy); if (attr == &sysfs_stop_when_cache_set_failed) return bch_snprint_string_list(buf, PAGE_SIZE, bch_stop_on_failure_modes, dc->stop_when_cache_set_failed); sysfs_printf(data_csum, "%i", dc->disk.data_csum); var_printf(verify, "%i"); var_printf(bypass_torture_test, "%i"); var_printf(writeback_metadata, "%i"); var_printf(writeback_running, "%i"); var_printf(writeback_consider_fragment, "%i"); var_print(writeback_delay); var_print(writeback_percent); sysfs_hprint(writeback_rate, wb ? atomic_long_read(&dc->writeback_rate.rate) << 9 : 0); sysfs_printf(io_errors, "%i", atomic_read(&dc->io_errors)); sysfs_printf(io_error_limit, "%i", dc->error_limit); sysfs_printf(io_disable, "%i", dc->io_disable); var_print(writeback_rate_update_seconds); var_print(writeback_rate_i_term_inverse); var_print(writeback_rate_p_term_inverse); var_print(writeback_rate_fp_term_low); var_print(writeback_rate_fp_term_mid); var_print(writeback_rate_fp_term_high); var_print(writeback_rate_minimum); if (attr == &sysfs_writeback_rate_debug) { char rate[20]; char dirty[20]; char target[20]; char proportional[20]; char integral[20]; char change[20]; s64 next_io; /* * Except for dirty and target, other values should * be 0 if writeback is not running. */ bch_hprint(rate, wb ? atomic_long_read(&dc->writeback_rate.rate) << 9 : 0); bch_hprint(dirty, bcache_dev_sectors_dirty(&dc->disk) << 9); bch_hprint(target, dc->writeback_rate_target << 9); bch_hprint(proportional, wb ? dc->writeback_rate_proportional << 9 : 0); bch_hprint(integral, wb ? dc->writeback_rate_integral_scaled << 9 : 0); bch_hprint(change, wb ? dc->writeback_rate_change << 9 : 0); next_io = wb ? div64_s64(dc->writeback_rate.next-local_clock(), NSEC_PER_MSEC) : 0; return sprintf(buf, "rate:\t\t%s/sec\n" "dirty:\t\t%s\n" "target:\t\t%s\n" "proportional:\t%s\n" "integral:\t%s\n" "change:\t\t%s/sec\n" "next io:\t%llims\n", rate, dirty, target, proportional, integral, change, next_io); } sysfs_hprint(dirty_data, bcache_dev_sectors_dirty(&dc->disk) << 9); sysfs_hprint(stripe_size, ((uint64_t)dc->disk.stripe_size) << 9); var_printf(partial_stripes_expensive, "%u"); var_hprint(sequential_cutoff); sysfs_print(running, atomic_read(&dc->running)); sysfs_print(state, states[BDEV_STATE(&dc->sb)]); if (attr == &sysfs_label) { memcpy(buf, dc->sb.label, SB_LABEL_SIZE); buf[SB_LABEL_SIZE + 1] = '\0'; strcat(buf, "\n"); return strlen(buf); } if (attr == &sysfs_backing_dev_name) { snprintf(buf, BDEVNAME_SIZE + 1, "%pg", dc->bdev); strcat(buf, "\n"); return strlen(buf); } if (attr == &sysfs_backing_dev_uuid) { /* convert binary uuid into 36-byte string plus '\0' */ snprintf(buf, 36+1, "%pU", dc->sb.uuid); strcat(buf, "\n"); return strlen(buf); } #undef var return 0;}SHOW_LOCKED(bch_cached_dev) STORE(__cached_dev){ struct cached_dev *dc = container_of(kobj, struct cached_dev, disk.kobj); ssize_t v; struct cache_set *c; struct kobj_uevent_env *env; /* no user space access if system is rebooting */ if (bcache_is_reboot) return -EBUSY; #define d_strtoul(var) sysfs_strtoul(var, dc->var)#define d_strtoul_nonzero(var) sysfs_strtoul_clamp(var, dc->var, 1, INT_MAX)#define d_strtoi_h(var) sysfs_hatoi(var, dc->var) sysfs_strtoul(data_csum, dc->disk.data_csum); d_strtoul(verify); sysfs_strtoul_bool(bypass_torture_test, dc->bypass_torture_test); sysfs_strtoul_bool(writeback_metadata, dc->writeback_metadata); sysfs_strtoul_bool(writeback_running, dc->writeback_running); sysfs_strtoul_bool(writeback_consider_fragment, dc->writeback_consider_fragment); sysfs_strtoul_clamp(writeback_delay, dc->writeback_delay, 0, UINT_MAX); sysfs_strtoul_clamp(writeback_percent, dc->writeback_percent, 0, bch_cutoff_writeback); if (attr == &sysfs_writeback_rate) { ssize_t ret; long int v = atomic_long_read(&dc->writeback_rate.rate); ret = strtoul_safe_clamp(buf, v, 1, INT_MAX); if (!ret) { atomic_long_set(&dc->writeback_rate.rate, v); ret = size; } return ret; } sysfs_strtoul_clamp(writeback_rate_update_seconds, dc->writeback_rate_update_seconds, 1, WRITEBACK_RATE_UPDATE_SECS_MAX); sysfs_strtoul_clamp(writeback_rate_i_term_inverse, dc->writeback_rate_i_term_inverse, 1, UINT_MAX); sysfs_strtoul_clamp(writeback_rate_p_term_inverse, dc->writeback_rate_p_term_inverse, 1, UINT_MAX); sysfs_strtoul_clamp(writeback_rate_fp_term_low, dc->writeback_rate_fp_term_low, 1, dc->writeback_rate_fp_term_mid - 1); sysfs_strtoul_clamp(writeback_rate_fp_term_mid, dc->writeback_rate_fp_term_mid, dc->writeback_rate_fp_term_low + 1, dc->writeback_rate_fp_term_high - 1); sysfs_strtoul_clamp(writeback_rate_fp_term_high, dc->writeback_rate_fp_term_high, dc->writeback_rate_fp_term_mid + 1, UINT_MAX); sysfs_strtoul_clamp(writeback_rate_minimum, dc->writeback_rate_minimum, 1, UINT_MAX); sysfs_strtoul_clamp(io_error_limit, dc->error_limit, 0, INT_MAX); if (attr == &sysfs_io_disable) { int v = strtoul_or_return(buf); dc->io_disable = v ? 1 : 0; } sysfs_strtoul_clamp(sequential_cutoff, dc->sequential_cutoff, 0, UINT_MAX); if (attr == &sysfs_clear_stats) bch_cache_accounting_clear(&dc->accounting); if (attr == &sysfs_running && strtoul_or_return(buf)) { v = bch_cached_dev_run(dc); if (v) return v; } if (attr == &sysfs_cache_mode) { v = __sysfs_match_string(bch_cache_modes, -1, buf); if (v < 0) return v; if ((unsigned int) v != BDEV_CACHE_MODE(&dc->sb)) { SET_BDEV_CACHE_MODE(&dc->sb, v); bch_write_bdev_super(dc, NULL); } } if (attr == &sysfs_readahead_cache_policy) { v = __sysfs_match_string(bch_reada_cache_policies, -1, buf); if (v < 0) return v; if ((unsigned int) v != dc->cache_readahead_policy) dc->cache_readahead_policy = v; } if (attr == &sysfs_stop_when_cache_set_failed) { v = __sysfs_match_string(bch_stop_on_failure_modes, -1, buf); if (v < 0) return v; dc->stop_when_cache_set_failed = v; } if (attr == &sysfs_label) { if (size > SB_LABEL_SIZE) return -EINVAL; memcpy(dc->sb.label, buf, size); if (size < SB_LABEL_SIZE) dc->sb.label[size] = '\0'; if (size && dc->sb.label[size - 1] == '\n') dc->sb.label[size - 1] = '\0'; bch_write_bdev_super(dc, NULL); if (dc->disk.c) { memcpy(dc->disk.c->uuids[dc->disk.id].label, buf, SB_LABEL_SIZE); bch_uuid_write(dc->disk.c); } env = kzalloc_obj(struct kobj_uevent_env); if (!env) return -ENOMEM; add_uevent_var(env, "DRIVER=bcache"); add_uevent_var(env, "CACHED_UUID=%pU", dc->sb.uuid); add_uevent_var(env, "CACHED_LABEL=%s", buf); kobject_uevent_env(&disk_to_dev(dc->disk.disk)->kobj, KOBJ_CHANGE, env->envp); kfree(env); } if (attr == &sysfs_attach) { uint8_t set_uuid[16]; if (bch_parse_uuid(buf, set_uuid) < 16) return -EINVAL; v = -ENOENT; list_for_each_entry(c, &bch_cache_sets, list) { v = bch_cached_dev_attach(dc, c, set_uuid); if (!v) return size; } if (v == -ENOENT) pr_err("Can't attach %s: cache set not found\n", buf); return v; } if (attr == &sysfs_detach && dc->disk.c) bch_cached_dev_detach(dc); if (attr == &sysfs_stop) bcache_device_stop(&dc->disk); return size;} STORE(bch_cached_dev){ struct cached_dev *dc = container_of(kobj, struct cached_dev, disk.kobj); /* no user space access if system is rebooting */ if (bcache_is_reboot) return -EBUSY; mutex_lock(&bch_register_lock); size = __cached_dev_store(kobj, attr, buf, size); if (attr == &sysfs_writeback_running) { /* dc->writeback_running changed in __cached_dev_store() */ if (IS_ERR_OR_NULL(dc->writeback_thread)) { /* * reject setting it to 1 via sysfs if writeback * kthread is not created yet. */ if (dc->writeback_running) { dc->writeback_running = false; pr_err("%s: failed to run non-existent writeback thread\n", dc->disk.disk->disk_name); } } else /* * writeback kthread will check if dc->writeback_running * is true or false. */ bch_writeback_queue(dc); } /* * Only set BCACHE_DEV_WB_RUNNING when cached device attached to * a cache set, otherwise it doesn't make sense. */ if (attr == &sysfs_writeback_percent) if ((dc->disk.c != NULL) && (!test_and_set_bit(BCACHE_DEV_WB_RUNNING, &dc->disk.flags))) schedule_delayed_work(&dc->writeback_rate_update, dc->writeback_rate_update_seconds * HZ); mutex_unlock(&bch_register_lock); return size;}Lines 1–500 of 1183. Load more
// SPDX-License-Identifier: GPL-2.0 /* * bcache sysfs interfaces * * Copyright 2010, 2011 Kent Overstreet <kent.overstreet@gmail.com> * Copyright 2012 Google, Inc. */ #include "bcache.h" #include "sysfs.h" #include "btree.h" #include "request.h" #include "writeback.h" #include "features.h" #include <linux/blkdev.h> #include <linux/sort.h> #include <linux/sched/clock.h> extern bool bcache_is_reboot; /* Default is 0 ("writethrough") */ static const char * const bch_cache_modes[] = { "writethrough", "writeback", "writearound", "none", NULL }; static const char * const bch_reada_cache_policies[] = { "all", "meta-only", NULL }; /* Default is 0 ("auto") */ static const char * const bch_stop_on_failure_modes[] = { "auto", "always", NULL }; static const char * const cache_replacement_policies[] = { "lru", "fifo", "random", NULL }; static const char * const error_actions[] = { "unregister", "panic", NULL }; write_attribute(attach); write_attribute(detach); write_attribute(unregister); write_attribute(stop); write_attribute(clear_stats); write_attribute(trigger_gc); write_attribute(prune_cache); write_attribute(flash_vol_create); read_attribute(bucket_size); read_attribute(block_size); read_attribute(nbuckets); read_attribute(tree_depth); read_attribute(root_usage_percent); read_attribute(priority_stats); read_attribute(btree_cache_size); read_attribute(btree_cache_max_chain); read_attribute(cache_available_percent); read_attribute(written); read_attribute(btree_written); read_attribute(metadata_written); read_attribute(active_journal_entries); read_attribute(backing_dev_name); read_attribute(backing_dev_uuid); sysfs_time_stats_attribute(btree_gc, sec, ms); sysfs_time_stats_attribute(btree_split, sec, us); sysfs_time_stats_attribute(btree_sort, ms, us); sysfs_time_stats_attribute(btree_read, ms, us); read_attribute(btree_nodes); read_attribute(btree_used_percent); read_attribute(average_key_size); read_attribute(dirty_data); read_attribute(bset_tree_stats); read_attribute(feature_compat); read_attribute(feature_ro_compat); read_attribute(feature_incompat); read_attribute(state); read_attribute(cache_read_races); read_attribute(reclaim); read_attribute(reclaimed_journal_buckets); read_attribute(flush_write); read_attribute(writeback_keys_done); read_attribute(writeback_keys_failed); read_attribute(io_errors); read_attribute(congested); read_attribute(cutoff_writeback); read_attribute(cutoff_writeback_sync); rw_attribute(congested_read_threshold_us); rw_attribute(congested_write_threshold_us); rw_attribute(sequential_cutoff); rw_attribute(data_csum); rw_attribute(cache_mode); rw_attribute(readahead_cache_policy); rw_attribute(stop_when_cache_set_failed); rw_attribute(writeback_metadata); rw_attribute(writeback_running); rw_attribute(writeback_percent); rw_attribute(writeback_delay); rw_attribute(writeback_rate); rw_attribute(writeback_consider_fragment); rw_attribute(writeback_rate_update_seconds); rw_attribute(writeback_rate_i_term_inverse); rw_attribute(writeback_rate_p_term_inverse); rw_attribute(writeback_rate_fp_term_low); rw_attribute(writeback_rate_fp_term_mid); rw_attribute(writeback_rate_fp_term_high); rw_attribute(writeback_rate_minimum); read_attribute(writeback_rate_debug); read_attribute(stripe_size); read_attribute(partial_stripes_expensive); rw_attribute(synchronous); rw_attribute(journal_delay_ms); rw_attribute(io_disable); rw_attribute(running); rw_attribute(label); rw_attribute(errors); rw_attribute(io_error_limit); rw_attribute(io_error_halflife); rw_attribute(verify); rw_attribute(bypass_torture_test); rw_attribute(key_merging_disabled); rw_attribute(gc_always_rewrite); rw_attribute(expensive_debug_checks); rw_attribute(cache_replacement_policy); rw_attribute(btree_shrinker_disabled); rw_attribute(copy_gc_enabled); rw_attribute(idle_max_writeback_rate); rw_attribute(gc_after_writeback); rw_attribute(size); static ssize_t bch_snprint_string_list(char *buf, size_t size, const char * const list[], size_t selected) { char *out = buf; size_t i; for (i = 0; list[i]; i++) out += scnprintf(out, buf + size - out, i == selected ? "[%s] " : "%s ", list[i]); out[-1] = '\n'; return out - buf; } SHOW(__bch_cached_dev) { struct cached_dev *dc = container_of(kobj, struct cached_dev, disk.kobj); char const *states[] = { "no cache", "clean", "dirty", "inconsistent" }; int wb = dc->writeback_running; #define var(stat) (dc->stat) if (attr == &sysfs_cache_mode) return bch_snprint_string_list(buf, PAGE_SIZE, bch_cache_modes, BDEV_CACHE_MODE(&dc->sb)); if (attr == &sysfs_readahead_cache_policy) return bch_snprint_string_list(buf, PAGE_SIZE, bch_reada_cache_policies, dc->cache_readahead_policy); if (attr == &sysfs_stop_when_cache_set_failed) return bch_snprint_string_list(buf, PAGE_SIZE, bch_stop_on_failure_modes, dc->stop_when_cache_set_failed); sysfs_printf(data_csum, "%i", dc->disk.data_csum); var_printf(verify, "%i"); var_printf(bypass_torture_test, "%i"); var_printf(writeback_metadata, "%i"); var_printf(writeback_running, "%i"); var_printf(writeback_consider_fragment, "%i"); var_print(writeback_delay); var_print(writeback_percent); sysfs_hprint(writeback_rate, wb ? atomic_long_read(&dc->writeback_rate.rate) << 9 : 0); sysfs_printf(io_errors, "%i", atomic_read(&dc->io_errors)); sysfs_printf(io_error_limit, "%i", dc->error_limit); sysfs_printf(io_disable, "%i", dc->io_disable); var_print(writeback_rate_update_seconds); var_print(writeback_rate_i_term_inverse); var_print(writeback_rate_p_term_inverse); var_print(writeback_rate_fp_term_low); var_print(writeback_rate_fp_term_mid); var_print(writeback_rate_fp_term_high); var_print(writeback_rate_minimum); if (attr == &sysfs_writeback_rate_debug) { char rate[20]; char dirty[20]; char target[20]; char proportional[20]; char integral[20]; char change[20]; s64 next_io; /* * Except for dirty and target, other values should * be 0 if writeback is not running. */ bch_hprint(rate, wb ? atomic_long_read(&dc->writeback_rate.rate) << 9 : 0); bch_hprint(dirty, bcache_dev_sectors_dirty(&dc->disk) << 9); bch_hprint(target, dc->writeback_rate_target << 9); bch_hprint(proportional, wb ? dc->writeback_rate_proportional << 9 : 0); bch_hprint(integral, wb ? dc->writeback_rate_integral_scaled << 9 : 0); bch_hprint(change, wb ? dc->writeback_rate_change << 9 : 0); next_io = wb ? div64_s64(dc->writeback_rate.next-local_clock(), NSEC_PER_MSEC) : 0; return sprintf(buf, "rate:\t\t%s/sec\n" "dirty:\t\t%s\n" "target:\t\t%s\n" "proportional:\t%s\n" "integral:\t%s\n" "change:\t\t%s/sec\n" "next io:\t%llims\n", rate, dirty, target, proportional, integral, change, next_io); } sysfs_hprint(dirty_data, bcache_dev_sectors_dirty(&dc->disk) << 9); sysfs_hprint(stripe_size, ((uint64_t)dc->disk.stripe_size) << 9); var_printf(partial_stripes_expensive, "%u"); var_hprint(sequential_cutoff); sysfs_print(running, atomic_read(&dc->running)); sysfs_print(state, states[BDEV_STATE(&dc->sb)]); if (attr == &sysfs_label) { memcpy(buf, dc->sb.label, SB_LABEL_SIZE); buf[SB_LABEL_SIZE + 1] = '\0'; strcat(buf, "\n"); return strlen(buf); } if (attr == &sysfs_backing_dev_name) { snprintf(buf, BDEVNAME_SIZE + 1, "%pg", dc->bdev); strcat(buf, "\n"); return strlen(buf); } if (attr == &sysfs_backing_dev_uuid) { /* convert binary uuid into 36-byte string plus '\0' */ snprintf(buf, 36+1, "%pU", dc->sb.uuid); strcat(buf, "\n"); return strlen(buf); } #undef var return 0; } SHOW_LOCKED(bch_cached_dev) STORE(__cached_dev) { struct cached_dev *dc = container_of(kobj, struct cached_dev, disk.kobj); ssize_t v; struct cache_set *c; struct kobj_uevent_env *env; /* no user space access if system is rebooting */ if (bcache_is_reboot) return -EBUSY; #define d_strtoul(var) sysfs_strtoul(var, dc->var) #define d_strtoul_nonzero(var) sysfs_strtoul_clamp(var, dc->var, 1, INT_MAX) #define d_strtoi_h(var) sysfs_hatoi(var, dc->var) sysfs_strtoul(data_csum, dc->disk.data_csum); d_strtoul(verify); sysfs_strtoul_bool(bypass_torture_test, dc->bypass_torture_test); sysfs_strtoul_bool(writeback_metadata, dc->writeback_metadata); sysfs_strtoul_bool(writeback_running, dc->writeback_running); sysfs_strtoul_bool(writeback_consider_fragment, dc->writeback_consider_fragment); sysfs_strtoul_clamp(writeback_delay, dc->writeback_delay, 0, UINT_MAX); sysfs_strtoul_clamp(writeback_percent, dc->writeback_percent, 0, bch_cutoff_writeback); if (attr == &sysfs_writeback_rate) { ssize_t ret; long int v = atomic_long_read(&dc->writeback_rate.rate); ret = strtoul_safe_clamp(buf, v, 1, INT_MAX); if (!ret) { atomic_long_set(&dc->writeback_rate.rate, v); ret = size; } return ret; } sysfs_strtoul_clamp(writeback_rate_update_seconds, dc->writeback_rate_update_seconds, 1, WRITEBACK_RATE_UPDATE_SECS_MAX); sysfs_strtoul_clamp(writeback_rate_i_term_inverse, dc->writeback_rate_i_term_inverse, 1, UINT_MAX); sysfs_strtoul_clamp(writeback_rate_p_term_inverse, dc->writeback_rate_p_term_inverse, 1, UINT_MAX); sysfs_strtoul_clamp(writeback_rate_fp_term_low, dc->writeback_rate_fp_term_low, 1, dc->writeback_rate_fp_term_mid - 1); sysfs_strtoul_clamp(writeback_rate_fp_term_mid, dc->writeback_rate_fp_term_mid, dc->writeback_rate_fp_term_low + 1, dc->writeback_rate_fp_term_high - 1); sysfs_strtoul_clamp(writeback_rate_fp_term_high, dc->writeback_rate_fp_term_high, dc->writeback_rate_fp_term_mid + 1, UINT_MAX); sysfs_strtoul_clamp(writeback_rate_minimum, dc->writeback_rate_minimum, 1, UINT_MAX); sysfs_strtoul_clamp(io_error_limit, dc->error_limit, 0, INT_MAX); if (attr == &sysfs_io_disable) { int v = strtoul_or_return(buf); dc->io_disable = v ? 1 : 0; } sysfs_strtoul_clamp(sequential_cutoff, dc->sequential_cutoff, 0, UINT_MAX); if (attr == &sysfs_clear_stats) bch_cache_accounting_clear(&dc->accounting); if (attr == &sysfs_running && strtoul_or_return(buf)) { v = bch_cached_dev_run(dc); if (v) return v; } if (attr == &sysfs_cache_mode) { v = __sysfs_match_string(bch_cache_modes, -1, buf); if (v < 0) return v; if ((unsigned int) v != BDEV_CACHE_MODE(&dc->sb)) { SET_BDEV_CACHE_MODE(&dc->sb, v); bch_write_bdev_super(dc, NULL); } } if (attr == &sysfs_readahead_cache_policy) { v = __sysfs_match_string(bch_reada_cache_policies, -1, buf); if (v < 0) return v; if ((unsigned int) v != dc->cache_readahead_policy) dc->cache_readahead_policy = v; } if (attr == &sysfs_stop_when_cache_set_failed) { v = __sysfs_match_string(bch_stop_on_failure_modes, -1, buf); if (v < 0) return v; dc->stop_when_cache_set_failed = v; } if (attr == &sysfs_label) { if (size > SB_LABEL_SIZE) return -EINVAL; memcpy(dc->sb.label, buf, size); if (size < SB_LABEL_SIZE) dc->sb.label[size] = '\0'; if (size && dc->sb.label[size - 1] == '\n') dc->sb.label[size - 1] = '\0'; bch_write_bdev_super(dc, NULL); if (dc->disk.c) { memcpy(dc->disk.c->uuids[dc->disk.id].label, buf, SB_LABEL_SIZE); bch_uuid_write(dc->disk.c); } env = kzalloc_obj(struct kobj_uevent_env); if (!env) return -ENOMEM; add_uevent_var(env, "DRIVER=bcache"); add_uevent_var(env, "CACHED_UUID=%pU", dc->sb.uuid); add_uevent_var(env, "CACHED_LABEL=%s", buf); kobject_uevent_env(&disk_to_dev(dc->disk.disk)->kobj, KOBJ_CHANGE, env->envp); kfree(env); } if (attr == &sysfs_attach) { uint8_t set_uuid[16]; if (bch_parse_uuid(buf, set_uuid) < 16) return -EINVAL; v = -ENOENT; list_for_each_entry(c, &bch_cache_sets, list) { v = bch_cached_dev_attach(dc, c, set_uuid); if (!v) return size; } if (v == -ENOENT) pr_err("Can't attach %s: cache set not found\n", buf); return v; } if (attr == &sysfs_detach && dc->disk.c) bch_cached_dev_detach(dc); if (attr == &sysfs_stop) bcache_device_stop(&dc->disk); return size; } STORE(bch_cached_dev) { struct cached_dev *dc = container_of(kobj, struct cached_dev, disk.kobj); /* no user space access if system is rebooting */ if (bcache_is_reboot) return -EBUSY; mutex_lock(&bch_register_lock); size = __cached_dev_store(kobj, attr, buf, size); if (attr == &sysfs_writeback_running) { /* dc->writeback_running changed in __cached_dev_store() */ if (IS_ERR_OR_NULL(dc->writeback_thread)) { /* * reject setting it to 1 via sysfs if writeback * kthread is not created yet. */ if (dc->writeback_running) { dc->writeback_running = false; pr_err("%s: failed to run non-existent writeback thread\n", dc->disk.disk->disk_name); } } else /* * writeback kthread will check if dc->writeback_running * is true or false. */ bch_writeback_queue(dc); } /* * Only set BCACHE_DEV_WB_RUNNING when cached device attached to * a cache set, otherwise it doesn't make sense. */ if (attr == &sysfs_writeback_percent) if ((dc->disk.c != NULL) && (!test_and_set_bit(BCACHE_DEV_WB_RUNNING, &dc->disk.flags))) schedule_delayed_work(&dc->writeback_rate_update, dc->writeback_rate_update_seconds * HZ); mutex_unlock(&bch_register_lock); return size; }