1183 lines · 29.8 KB · C
RawDownload
// 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.