Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

1101 lines (1010 sloc) 29.133 kB
/****************************************************************************
* flashcache_conf.c
* FlashCache: Device mapper target for block-level disk caching
*
* Copyright 2010 Facebook, Inc.
* Author: Mohan Srinivasan (mohan@fb.com)
*
* Based on DM-Cache:
* Copyright (C) International Business Machines Corp., 2006
* Author: Ming Zhao (mingzhao@ufl.edu)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; under version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
****************************************************************************/
#include <asm/atomic.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/blkdev.h>
#include <linux/bio.h>
#include <linux/slab.h>
#include <linux/hash.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/pagemap.h>
#include <linux/random.h>
#include <linux/hardirq.h>
#include <linux/sysctl.h>
#include <linux/version.h>
#include <linux/reboot.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
#include "dm.h"
#include "dm-io.h"
#include "dm-bio-list.h"
#include "kcopyd.h"
#else
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,27)
#include "dm.h"
#endif
#include <linux/device-mapper.h>
#include <linux/bio.h>
#include <linux/dm-kcopyd.h>
#endif
#include "flashcache.h"
#include "flashcache_ioctl.h"
static int fallow_clean_speed_min = FALLOW_SPEED_MIN;
static int fallow_clean_speed_max = FALLOW_SPEED_MAX;
extern u_int64_t size_hist[];
static char *flashcache_cons_procfs_cachename(struct cache_c *dmc, char *path_component);
static char *flashcache_cons_sysctl_devname(struct cache_c *dmc);
#define FLASHCACHE_PROC_ROOTDIR_NAME "flashcache"
static int
flashcache_io_latency_init(ctl_table *table, int write,
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)
struct file *file,
#endif
void __user *buffer,
size_t *length, loff_t *ppos)
{
struct cache_c *dmc = (struct cache_c *)table->extra1;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)
proc_dointvec(table, write, file, buffer, length, ppos);
#else
proc_dointvec(table, write, buffer, length, ppos);
#endif
if (write) {
if (dmc->sysctl_io_latency_hist) {
int i;
for (i = 0 ; i < IO_LATENCY_BUCKETS ; i++)
dmc->latency_hist[i] = 0;
dmc->latency_hist_10ms = 0;
}
}
return 0;
}
static int
flashcache_sync_sysctl(ctl_table *table, int write,
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)
struct file *file,
#endif
void __user *buffer,
size_t *length, loff_t *ppos)
{
struct cache_c *dmc = (struct cache_c *)table->extra1;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)
proc_dointvec(table, write, file, buffer, length, ppos);
#else
proc_dointvec(table, write, buffer, length, ppos);
#endif
if (write) {
if (dmc->sysctl_do_sync) {
dmc->sysctl_stop_sync = 0;
cancel_delayed_work(&dmc->delayed_clean);
flush_scheduled_work();
flashcache_sync_all(dmc);
}
}
return 0;
}
static int
flashcache_zerostats_sysctl(ctl_table *table, int write,
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)
struct file *file,
#endif
void __user *buffer,
size_t *length, loff_t *ppos)
{
struct cache_c *dmc = (struct cache_c *)table->extra1;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)
proc_dointvec(table, write, file, buffer, length, ppos);
#else
proc_dointvec(table, write, buffer, length, ppos);
#endif
if (write) {
if (dmc->sysctl_zerostats) {
int i;
memset(&dmc->flashcache_stats, 0, sizeof(struct flashcache_stats));
for (i = 0 ; i < IO_LATENCY_BUCKETS ; i++)
dmc->latency_hist[i] = 0;
dmc->latency_hist_10ms = 0;
}
}
return 0;
}
static int
flashcache_fallow_clean_speed_sysctl(ctl_table *table, int write,
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)
struct file *file,
#endif
void __user *buffer,
size_t *length, loff_t *ppos)
{
struct cache_c *dmc = (struct cache_c *)table->extra1;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)
proc_dointvec(table, write, file, buffer, length, ppos);
#else
proc_dointvec(table, write, buffer, length, ppos);
#endif
if (write) {
if (dmc->sysctl_fallow_clean_speed < fallow_clean_speed_min)
dmc->sysctl_fallow_clean_speed = fallow_clean_speed_min;
if (dmc->sysctl_fallow_clean_speed > fallow_clean_speed_max)
dmc->sysctl_fallow_clean_speed = fallow_clean_speed_max;
}
return 0;
}
static int
flashcache_dirty_thresh_sysctl(ctl_table *table, int write,
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)
struct file *file,
#endif
void __user *buffer,
size_t *length, loff_t *ppos)
{
struct cache_c *dmc = (struct cache_c *)table->extra1;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)
proc_dointvec(table, write, file, buffer, length, ppos);
#else
proc_dointvec(table, write, buffer, length, ppos);
#endif
if (write) {
if (dmc->sysctl_dirty_thresh > DIRTY_THRESH_MAX)
dmc->sysctl_dirty_thresh = DIRTY_THRESH_MAX;
if (dmc->sysctl_dirty_thresh < DIRTY_THRESH_MIN)
dmc->sysctl_dirty_thresh = DIRTY_THRESH_MIN;
dmc->dirty_thresh_set =
(dmc->assoc * dmc->sysctl_dirty_thresh) / 100;
}
return 0;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
#define CTL_UNNUMBERED -2
#endif
/*
* Each ctl_table array needs to be 1 more than the actual number of
* entries - zero padded at the end ! Therefore the NUM_*_SYSCTLS
* is 1 more than then number of sysctls.
*/
#define FLASHCACHE_NUM_WRITEBACK_SYSCTLS 17
static struct flashcache_writeback_sysctl_table {
struct ctl_table_header *sysctl_header;
ctl_table vars[FLASHCACHE_NUM_WRITEBACK_SYSCTLS];
ctl_table dev[2];
ctl_table dir[2];
ctl_table root[2];
} flashcache_writeback_sysctl = {
.vars = {
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.ctl_name = CTL_UNNUMBERED,
#endif
.procname = "io_latency_hist",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &flashcache_io_latency_init,
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.strategy = &sysctl_intvec,
#endif
},
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.ctl_name = CTL_UNNUMBERED,
#endif
.procname = "do_sync",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &flashcache_sync_sysctl,
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.strategy = &sysctl_intvec,
#endif
},
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.ctl_name = CTL_UNNUMBERED,
#endif
.procname = "stop_sync",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec,
},
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.ctl_name = CTL_UNNUMBERED,
#endif
.procname = "dirty_thresh_pct",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &flashcache_dirty_thresh_sysctl,
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.strategy = &sysctl_intvec,
#endif
},
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.ctl_name = CTL_UNNUMBERED,
#endif
.procname = "max_clean_ios_total",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec,
},
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.ctl_name = CTL_UNNUMBERED,
#endif
.procname = "max_clean_ios_set",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec,
},
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.ctl_name = CTL_UNNUMBERED,
#endif
.procname = "do_pid_expiry",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec,
},
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.ctl_name = CTL_UNNUMBERED,
#endif
.procname = "max_pids",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec,
},
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.ctl_name = CTL_UNNUMBERED,
#endif
.procname = "pid_expiry_secs",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec,
},
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.ctl_name = CTL_UNNUMBERED,
#endif
.procname = "reclaim_policy",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec,
},
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.ctl_name = CTL_UNNUMBERED,
#endif
.procname = "zero_stats",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &flashcache_zerostats_sysctl,
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.strategy = &sysctl_intvec,
#endif
},
#ifdef notdef
/*
* Disable this for all except devel builds
* If you enable this, you must bump FLASHCACHE_NUM_WRITEBACK_SYSCTLS
* by 1 !
*/
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.ctl_name = CTL_UNNUMBERED,
#endif
.procname = "error_inject",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec,
},
#endif
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.ctl_name = CTL_UNNUMBERED,
#endif
.procname = "fast_remove",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec,
},
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.ctl_name = CTL_UNNUMBERED,
#endif
.procname = "cache_all",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec,
},
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.ctl_name = CTL_UNNUMBERED,
#endif
.procname = "fallow_clean_speed",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &flashcache_fallow_clean_speed_sysctl,
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.strategy = &sysctl_intvec,
#endif
},
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.ctl_name = CTL_UNNUMBERED,
#endif
.procname = "fallow_delay",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec,
},
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.ctl_name = CTL_UNNUMBERED,
#endif
.procname = "skip_seq_thresh_kb",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec,
},
},
.dev = {
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.ctl_name = CTL_UNNUMBERED,
#endif
.procname = "flashcache-dev",
.maxlen = 0,
.mode = S_IRUGO|S_IXUGO,
.child = flashcache_writeback_sysctl.vars,
},
},
.dir = {
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.ctl_name = CTL_UNNUMBERED,
#endif
.procname = FLASHCACHE_PROC_ROOTDIR_NAME,
.maxlen = 0,
.mode = S_IRUGO|S_IXUGO,
.child = flashcache_writeback_sysctl.dev,
},
},
.root = {
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.ctl_name = CTL_DEV,
#endif
.procname = "dev",
.maxlen = 0,
.mode = 0555,
.child = flashcache_writeback_sysctl.dir,
},
},
};
/*
* Each ctl_table array needs to be 1 more than the actual number of
* entries - zero padded at the end ! Therefore the NUM_*_SYSCTLS
* is 1 more than then number of sysctls.
*/
#define FLASHCACHE_NUM_WRITETHROUGH_SYSCTLS 9
static struct flashcache_writethrough_sysctl_table {
struct ctl_table_header *sysctl_header;
ctl_table vars[FLASHCACHE_NUM_WRITETHROUGH_SYSCTLS];
ctl_table dev[2];
ctl_table dir[2];
ctl_table root[2];
} flashcache_writethrough_sysctl = {
.vars = {
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.ctl_name = CTL_UNNUMBERED,
#endif
.procname = "io_latency_hist",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &flashcache_io_latency_init,
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.strategy = &sysctl_intvec,
#endif
},
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.ctl_name = CTL_UNNUMBERED,
#endif
.procname = "do_pid_expiry",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec,
},
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.ctl_name = CTL_UNNUMBERED,
#endif
.procname = "max_pids",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec,
},
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.ctl_name = CTL_UNNUMBERED,
#endif
.procname = "pid_expiry_secs",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec,
},
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.ctl_name = CTL_UNNUMBERED,
#endif
.procname = "reclaim_policy",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec,
},
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.ctl_name = CTL_UNNUMBERED,
#endif
.procname = "zero_stats",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &flashcache_zerostats_sysctl,
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.strategy = &sysctl_intvec,
#endif
},
#ifdef notdef
/*
* Disable this for all except devel builds
* If you enable this, you must bump FLASHCACHE_NUM_WRITEBACK_SYSCTLS
* by 1 !
*/
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.ctl_name = CTL_UNNUMBERED,
#endif
.procname = "error_inject",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec,
},
#endif
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.ctl_name = CTL_UNNUMBERED,
#endif
.procname = "cache_all",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec,
},
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.ctl_name = CTL_UNNUMBERED,
#endif
.procname = "skip_seq_thresh_kb",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec,
},
},
.dev = {
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.ctl_name = CTL_UNNUMBERED,
#endif
.procname = "flashcache-dev",
.maxlen = 0,
.mode = S_IRUGO|S_IXUGO,
.child = flashcache_writethrough_sysctl.vars,
},
},
.dir = {
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.ctl_name = CTL_UNNUMBERED,
#endif
.procname = FLASHCACHE_PROC_ROOTDIR_NAME,
.maxlen = 0,
.mode = S_IRUGO|S_IXUGO,
.child = flashcache_writethrough_sysctl.dev,
},
},
.root = {
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.ctl_name = CTL_DEV,
#endif
.procname = "dev",
.maxlen = 0,
.mode = 0555,
.child = flashcache_writethrough_sysctl.dir,
},
},
};
int *
flashcache_find_sysctl_data(struct cache_c *dmc, ctl_table *vars)
{
if (strcmp(vars->procname, "io_latency_hist") == 0)
return &dmc->sysctl_io_latency_hist;
else if (strcmp(vars->procname, "do_sync") == 0)
return &dmc->sysctl_do_sync;
else if (strcmp(vars->procname, "stop_sync") == 0)
return &dmc->sysctl_stop_sync;
else if (strcmp(vars->procname, "dirty_thresh_pct") == 0)
return &dmc->sysctl_dirty_thresh;
else if (strcmp(vars->procname, "max_clean_ios_total") == 0)
return &dmc->max_clean_ios_total;
else if (strcmp(vars->procname, "max_clean_ios_set") == 0)
return &dmc->max_clean_ios_set;
else if (strcmp(vars->procname, "do_pid_expiry") == 0)
return &dmc->sysctl_pid_do_expiry;
else if (strcmp(vars->procname, "max_pids") == 0)
return &dmc->sysctl_max_pids;
else if (strcmp(vars->procname, "pid_expiry_secs") == 0)
return &dmc->sysctl_pid_expiry_secs;
else if (strcmp(vars->procname, "reclaim_policy") == 0)
return &dmc->sysctl_reclaim_policy;
else if (strcmp(vars->procname, "zero_stats") == 0)
return &dmc->sysctl_zerostats;
else if (strcmp(vars->procname, "error_inject") == 0)
return &dmc->sysctl_error_inject;
else if (strcmp(vars->procname, "fast_remove") == 0)
return &dmc->sysctl_fast_remove;
else if (strcmp(vars->procname, "cache_all") == 0)
return &dmc->sysctl_cache_all;
else if (strcmp(vars->procname, "fallow_clean_speed") == 0)
return &dmc->sysctl_fallow_clean_speed;
else if (strcmp(vars->procname, "fallow_delay") == 0)
return &dmc->sysctl_fallow_delay;
else if (strcmp(vars->procname, "skip_seq_thresh_kb") == 0)
return &dmc->sysctl_skip_seq_thresh_kb;
VERIFY(0);
return NULL;
}
static void
flashcache_writeback_sysctl_register(struct cache_c *dmc)
{
int i;
struct flashcache_writeback_sysctl_table *t;
t = kmemdup(&flashcache_writeback_sysctl, sizeof(*t), GFP_KERNEL);
if (t == NULL)
return;
for (i = 0 ; i < ARRAY_SIZE(t->vars) - 1 ; i++) {
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
t->vars[i].de = NULL;
#endif
t->vars[i].data = flashcache_find_sysctl_data(dmc, &t->vars[i]);
t->vars[i].extra1 = dmc;
}
t->dev[0].procname = flashcache_cons_sysctl_devname(dmc);
t->dev[0].child = t->vars;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
t->dev[0].de = NULL;
#endif
t->dir[0].child = t->dev;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
t->dir[0].de = NULL;
#endif
t->root[0].child = t->dir;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
t->root[0].de = NULL;
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
t->sysctl_header = register_sysctl_table(t->root, 0);
#else
t->sysctl_header = register_sysctl_table(t->root);
#endif
if (t->sysctl_header == NULL)
goto out;
dmc->sysctl_handle = t;
return;
out:
kfree(t->dev[0].procname);
kfree(t);
}
static void
flashcache_writeback_sysctl_unregister(struct cache_c *dmc)
{
struct flashcache_writeback_sysctl_table *t;
t = dmc->sysctl_handle;
if (t != NULL) {
dmc->sysctl_handle = NULL;
unregister_sysctl_table(t->sysctl_header);
kfree(t->dev[0].procname);
kfree(t);
}
}
static void
flashcache_writethrough_sysctl_register(struct cache_c *dmc)
{
int i;
struct flashcache_writethrough_sysctl_table *t;
t = kmemdup(&flashcache_writethrough_sysctl, sizeof(*t), GFP_KERNEL);
if (t == NULL)
return;
for (i = 0 ; i < ARRAY_SIZE(t->vars) - 1 ; i++) {
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
t->vars[i].de = NULL;
#endif
t->vars[i].data = flashcache_find_sysctl_data(dmc, &t->vars[i]);
t->vars[i].extra1 = dmc;
}
t->dev[0].procname = flashcache_cons_sysctl_devname(dmc);
t->dev[0].child = t->vars;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
t->dev[0].de = NULL;
#endif
t->dir[0].child = t->dev;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
t->dir[0].de = NULL;
#endif
t->root[0].child = t->dir;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
t->root[0].de = NULL;
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
t->sysctl_header = register_sysctl_table(t->root, 0);
#else
t->sysctl_header = register_sysctl_table(t->root);
#endif
if (t->sysctl_header == NULL)
goto out;
dmc->sysctl_handle = t;
return;
out:
kfree(t->dev[0].procname);
kfree(t);
}
static void
flashcache_writethrough_sysctl_unregister(struct cache_c *dmc)
{
struct flashcache_writethrough_sysctl_table *t;
t = dmc->sysctl_handle;
if (t != NULL) {
dmc->sysctl_handle = NULL;
unregister_sysctl_table(t->sysctl_header);
kfree(t->dev[0].procname);
kfree(t);
}
}
static int
flashcache_stats_show(struct seq_file *seq, void *v)
{
struct cache_c *dmc = seq->private;
struct flashcache_stats *stats;
int read_hit_pct, write_hit_pct, dirty_write_hit_pct;
stats = &dmc->flashcache_stats;
if (stats->reads > 0)
read_hit_pct = stats->read_hits * 100 / stats->reads;
else
read_hit_pct = 0;
if (stats->writes > 0) {
write_hit_pct = stats->write_hits * 100 / stats->writes;
dirty_write_hit_pct = stats->dirty_write_hits * 100 / stats->writes;
} else {
write_hit_pct = 0;
dirty_write_hit_pct = 0;
}
seq_printf(seq, "reads=%lu writes=%lu \n",
stats->reads, stats->writes);
seq_printf(seq, "read_hits=%lu read_hit_percent=%d ",
stats->read_hits, read_hit_pct);
if (dmc->cache_mode == FLASHCACHE_WRITE_BACK || dmc->cache_mode == FLASHCACHE_WRITE_THROUGH) {
seq_printf(seq, "write_hits=%lu write_hit_percent=%d ",
stats->write_hits, write_hit_pct);
}
if (dmc->cache_mode == FLASHCACHE_WRITE_BACK) {
seq_printf(seq, "dirty_write_hits=%lu dirty_write_hit_percent=%d ",
stats->dirty_write_hits, dirty_write_hit_pct);
}
if (dmc->cache_mode == FLASHCACHE_WRITE_BACK || dmc->cache_mode == FLASHCACHE_WRITE_THROUGH) {
seq_printf(seq, "replacement=%lu write_replacement=%lu ",
stats->replace, stats->wr_replace);
seq_printf(seq, "write_invalidates=%lu read_invalidates=%lu ",
stats->wr_invalidates, stats->rd_invalidates);
} else { /* WRITE_AROUND */
seq_printf(seq, "replacement=%lu ",
stats->replace);
seq_printf(seq, "read_invalidates=%lu ",
stats->rd_invalidates);
}
#ifdef FLASHCACHE_DO_CHECKSUMS
seq_printf(seq, "checksum_store=%ld checksum_valid=%ld checksum_invalid=%ld ",
stats->checksum_store, stats->checksum_valid, stats->checksum_invalid);
#endif
seq_printf(seq, "pending_enqueues=%lu pending_inval=%lu ",
stats->enqueues, stats->pending_inval);
if (dmc->cache_mode == FLASHCACHE_WRITE_BACK) {
seq_printf(seq, "metadata_dirties=%lu metadata_cleans=%lu ",
stats->md_write_dirty, stats->md_write_clean);
seq_printf(seq, "metadata_batch=%lu metadata_ssd_writes=%lu ",
stats->md_write_batch, stats->md_ssd_writes);
seq_printf(seq, "cleanings=%lu fallow_cleanings=%lu ",
stats->cleanings, stats->fallow_cleanings);
}
seq_printf(seq, "no_room=%lu ",
stats->noroom);
if (dmc->cache_mode == FLASHCACHE_WRITE_BACK) {
seq_printf(seq, "front_merge=%lu back_merge=%lu ",
stats->front_merge, stats->back_merge);
}
seq_printf(seq, "disk_reads=%lu disk_writes=%lu ssd_reads=%lu ssd_writes=%lu ",
stats->disk_reads, stats->disk_writes, stats->ssd_reads, stats->ssd_writes);
seq_printf(seq, "uncached_reads=%lu uncached_writes=%lu uncached_IO_requeue=%lu ",
stats->uncached_reads, stats->uncached_writes, stats->uncached_io_requeue);
seq_printf(seq, "uncached_sequential_reads=%lu uncached_sequential_writes=%lu ",
stats->uncached_sequential_reads, stats->uncached_sequential_writes);
seq_printf(seq, "pid_adds=%lu pid_dels=%lu pid_drops=%lu pid_expiry=%lu\n",
stats->pid_adds, stats->pid_dels, stats->pid_drops, stats->expiry);
return 0;
}
static int
flashcache_stats_open(struct inode *inode, struct file *file)
{
return single_open(file, &flashcache_stats_show, PDE(inode)->data);
}
static struct file_operations flashcache_stats_operations = {
.open = flashcache_stats_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int
flashcache_errors_show(struct seq_file *seq, void *v)
{
struct cache_c *dmc = seq->private;
seq_printf(seq, "disk_read_errors=%d disk_write_errors=%d ",
dmc->flashcache_errors.disk_read_errors,
dmc->flashcache_errors.disk_write_errors);
seq_printf(seq, "ssd_read_errors=%d ssd_write_errors=%d ",
dmc->flashcache_errors.ssd_read_errors,
dmc->flashcache_errors.ssd_write_errors);
seq_printf(seq, "memory_alloc_errors=%d\n",
dmc->flashcache_errors.memory_alloc_errors);
return 0;
}
static int
flashcache_errors_open(struct inode *inode, struct file *file)
{
return single_open(file, &flashcache_errors_show, PDE(inode)->data);
}
static struct file_operations flashcache_errors_operations = {
.open = flashcache_errors_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int
flashcache_iosize_hist_show(struct seq_file *seq, void *v)
{
int i;
for (i = 1 ; i <= 32 ; i++) {
seq_printf(seq, "%d:%llu ", i*512, size_hist[i]);
}
seq_printf(seq, "\n");
return 0;
}
static int
flashcache_iosize_hist_open(struct inode *inode, struct file *file)
{
return single_open(file, &flashcache_iosize_hist_show, PDE(inode)->data);
}
static struct file_operations flashcache_iosize_hist_operations = {
.open = flashcache_iosize_hist_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int
flashcache_pidlists_show(struct seq_file *seq, void *v)
{
struct cache_c *dmc = seq->private;
struct flashcache_cachectl_pid *pid_list;
unsigned long flags;
spin_lock_irqsave(&dmc->cache_spin_lock, flags);
seq_printf(seq, "Blacklist: ");
pid_list = dmc->blacklist_head;
while (pid_list != NULL) {
seq_printf(seq, "%u ", pid_list->pid);
pid_list = pid_list->next;
}
seq_printf(seq, "\n");
seq_printf(seq, "Whitelist: ");
pid_list = dmc->whitelist_head;
while (pid_list != NULL) {
seq_printf(seq, "%u ", pid_list->pid);
pid_list = pid_list->next;
}
seq_printf(seq, "\n");
spin_unlock_irqrestore(&dmc->cache_spin_lock, flags);
return 0;
}
static int
flashcache_pidlists_open(struct inode *inode, struct file *file)
{
return single_open(file, &flashcache_pidlists_show, PDE(inode)->data);
}
static struct file_operations flashcache_pidlists_operations = {
.open = flashcache_pidlists_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
extern char *flashcache_sw_version;
static int
flashcache_version_show(struct seq_file *seq, void *v)
{
seq_printf(seq, "Flashcache Version : %s\n", flashcache_sw_version);
#ifdef COMMIT_REV
seq_printf(seq, "git commit: %s\n", COMMIT_REV);
#endif
return 0;
}
static int
flashcache_version_open(struct inode *inode, struct file *file)
{
return single_open(file, &flashcache_version_show, PDE(inode)->data);
}
static struct file_operations flashcache_version_operations = {
.open = flashcache_version_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
void
flashcache_module_procfs_init(void)
{
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *entry;
if (proc_mkdir("flashcache", NULL)) {
entry = create_proc_entry("flashcache/flashcache_version", 0, NULL);
if (entry)
entry->proc_fops = &flashcache_version_operations;
}
#endif /* CONFIG_PROC_FS */
}
void
flashcache_module_procfs_releae(void)
{
#ifdef CONFIG_PROC_FS
(void)remove_proc_entry("flashcache/flashcache_version", NULL);
(void)remove_proc_entry("flashcache", NULL);
#endif /* CONFIG_PROC_FS */
}
static char *
flashcache_cons_sysctl_devname(struct cache_c *dmc)
{
char *pathname;
pathname = kzalloc(strlen(dmc->cache_devname) + strlen(dmc->disk_devname) + 2,
GFP_KERNEL);
strcpy(pathname, strrchr(dmc->cache_devname, '/') + 1);
strcat(pathname, "+");
strcat(pathname, strrchr(dmc->disk_devname, '/') + 1);
return pathname;
}
static char *
flashcache_cons_procfs_cachename(struct cache_c *dmc, char *path_component)
{
char *pathname;
char *s;
pathname = kzalloc(strlen(dmc->cache_devname) + strlen(dmc->disk_devname) + 4 +
strlen(FLASHCACHE_PROC_ROOTDIR_NAME) +
strlen(path_component),
GFP_KERNEL);
strcpy(pathname, FLASHCACHE_PROC_ROOTDIR_NAME);
strcat(pathname, "/");
s = strrchr(dmc->cache_devname, '/');
if (s)
s++;
else
s = dmc->cache_devname;
strcat(pathname, s);
strcat(pathname, "+");
s = strrchr(dmc->disk_devname, '/');
if (s)
s++;
else
s = dmc->disk_devname;
strcat(pathname, s);
if (strcmp(path_component, "") != 0) {
strcat(pathname, "/");
strcat(pathname, path_component);
}
return pathname;
}
void
flashcache_ctr_procfs(struct cache_c *dmc)
{
char *s;
struct proc_dir_entry *entry;
s = flashcache_cons_procfs_cachename(dmc, "");
entry = proc_mkdir(s, NULL);
kfree(s);
if (entry == NULL)
return;
s = flashcache_cons_procfs_cachename(dmc, "flashcache_stats");
entry = create_proc_entry(s, 0, NULL);
if (entry) {
entry->proc_fops = &flashcache_stats_operations;
entry->data = dmc;
}
kfree(s);
s = flashcache_cons_procfs_cachename(dmc, "flashcache_errors");
entry = create_proc_entry(s, 0, NULL);
if (entry) {
entry->proc_fops = &flashcache_errors_operations;
entry->data = dmc;
}
kfree(s);
s = flashcache_cons_procfs_cachename(dmc, "flashcache_iosize_hist");
entry = create_proc_entry(s, 0, NULL);
if (entry) {
entry->proc_fops = &flashcache_iosize_hist_operations;
entry->data = dmc;
}
kfree(s);
s = flashcache_cons_procfs_cachename(dmc, "flashcache_pidlists");
entry = create_proc_entry(s, 0, NULL);
if (entry) {
entry->proc_fops = &flashcache_pidlists_operations;
entry->data = dmc;
}
kfree(s);
if (dmc->cache_mode == FLASHCACHE_WRITE_BACK)
flashcache_writeback_sysctl_register(dmc);
else
flashcache_writethrough_sysctl_register(dmc);
}
void
flashcache_dtr_procfs(struct cache_c *dmc)
{
char *s;
s = flashcache_cons_procfs_cachename(dmc, "flashcache_stats");
remove_proc_entry(s, NULL);
kfree(s);
s = flashcache_cons_procfs_cachename(dmc, "flashcache_errors");
remove_proc_entry(s, NULL);
kfree(s);
s = flashcache_cons_procfs_cachename(dmc, "flashcache_iosize_hist");
remove_proc_entry(s, NULL);
kfree(s);
s = flashcache_cons_procfs_cachename(dmc, "flashcache_pidlists");
remove_proc_entry(s, NULL);
kfree(s);
s = flashcache_cons_procfs_cachename(dmc, "");
remove_proc_entry(s, NULL);
kfree(s);
if (dmc->cache_mode == FLASHCACHE_WRITE_BACK)
flashcache_writeback_sysctl_unregister(dmc);
else
flashcache_writethrough_sysctl_unregister(dmc);
}
Jump to Line
Something went wrong with that request. Please try again.