X-Git-Url: https://git.whamcloud.com/?p=fs%2Flustre-release.git;a=blobdiff_plain;f=lustre%2Fllite%2Flproc_llite.c;h=a0aea001118d960a03021440d53331f5126cf22c;hp=18f140664954ad517813db05b3d9da8c203bd319;hb=e49995acfd026f3ca85d05dc1b91d97a8743fe72;hpb=dff46e780827cf723c90bd349bc22951fb46e0ae diff --git a/lustre/llite/lproc_llite.c b/lustre/llite/lproc_llite.c index 18f1406..a0aea00 100644 --- a/lustre/llite/lproc_llite.c +++ b/lustre/llite/lproc_llite.c @@ -27,7 +27,7 @@ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. * Use is subject to license terms. * - * Copyright (c) 2012, 2013, Intel Corporation. + * Copyright (c) 2012, 2014, Intel Corporation. */ /* * This file is part of Lustre, http://www.lustre.org/ @@ -36,21 +36,20 @@ #define DEBUG_SUBSYSTEM S_LLITE #include -#include #include #include #include #include "llite_internal.h" +#include "vvp_internal.h" struct proc_dir_entry *proc_lustre_fs_root; #ifdef LPROCFS /* /proc/lustre/llite mount point registration */ -extern struct file_operations vvp_dump_pgcache_file_ops; -struct file_operations ll_rw_extents_stats_fops; -struct file_operations ll_rw_extents_stats_pp_fops; -struct file_operations ll_rw_offset_stats_fops; +static const struct file_operations ll_rw_extents_stats_fops; +static const struct file_operations ll_rw_extents_stats_pp_fops; +static const struct file_operations ll_rw_offset_stats_fops; static int ll_blksize_seq_show(struct seq_file *m, void *v) { @@ -209,7 +208,8 @@ static int ll_xattr_cache_seq_show(struct seq_file *m, void *v) return seq_printf(m, "%u\n", sbi->ll_xattr_cache_enabled); } -static ssize_t ll_xattr_cache_seq_write(struct file *file, const char *buffer, +static ssize_t ll_xattr_cache_seq_write(struct file *file, + const char __user *buffer, size_t count, loff_t *off) { struct seq_file *m = file->private_data; @@ -260,18 +260,25 @@ static int ll_max_readahead_mb_seq_show(struct seq_file *m, void *v) } static ssize_t -ll_max_readahead_mb_seq_write(struct file *file, const char *buffer, +ll_max_readahead_mb_seq_write(struct file *file, const char __user *buffer, size_t count, loff_t *off) { struct seq_file *m = file->private_data; struct ll_sb_info *sbi = ll_s2sbi((struct super_block *)m->private); - int mult, rc, pages_number; + __u64 val; + long pages_number; + int mult; + int rc; mult = 1 << (20 - PAGE_CACHE_SHIFT); - rc = lprocfs_write_frac_helper(buffer, count, &pages_number, mult); + rc = lprocfs_write_frac_u64_helper(buffer, count, &val, mult); if (rc) return rc; + if (val > LONG_MAX) + return -ERANGE; + pages_number = (long)val; + if (pages_number < 0 || pages_number > totalram_pages / 2) { /* 1/2 of RAM */ CERROR("can't set file readahead more than %lu MB\n", @@ -302,7 +309,8 @@ static int ll_max_readahead_per_file_mb_seq_show(struct seq_file *m, void *v) } static ssize_t -ll_max_readahead_per_file_mb_seq_write(struct file *file, const char *buffer, +ll_max_readahead_per_file_mb_seq_write(struct file *file, + const char __user *buffer, size_t count, loff_t *off) { struct seq_file *m = file->private_data; @@ -345,7 +353,8 @@ static int ll_max_read_ahead_whole_mb_seq_show(struct seq_file *m, void *v) } static ssize_t -ll_max_read_ahead_whole_mb_seq_write(struct file *file, const char *buffer, +ll_max_read_ahead_whole_mb_seq_write(struct file *file, + const char __user *buffer, size_t count, loff_t *off) { struct seq_file *m = file->private_data; @@ -381,18 +390,18 @@ static int ll_max_cached_mb_seq_show(struct seq_file *m, void *v) struct ll_sb_info *sbi = ll_s2sbi(sb); struct cl_client_cache *cache = &sbi->ll_cache; int shift = 20 - PAGE_CACHE_SHIFT; - int max_cached_mb; - int unused_mb; + long max_cached_mb; + long unused_mb; max_cached_mb = cache->ccc_lru_max >> shift; - unused_mb = cfs_atomic_read(&cache->ccc_lru_left) >> shift; + unused_mb = atomic_long_read(&cache->ccc_lru_left) >> shift; return seq_printf(m, "users: %d\n" - "max_cached_mb: %d\n" - "used_mb: %d\n" - "unused_mb: %d\n" + "max_cached_mb: %ld\n" + "used_mb: %ld\n" + "unused_mb: %ld\n" "reclaim_count: %u\n", - cfs_atomic_read(&cache->ccc_users), + atomic_read(&cache->ccc_users), max_cached_mb, max_cached_mb - unused_mb, unused_mb, @@ -400,7 +409,7 @@ static int ll_max_cached_mb_seq_show(struct seq_file *m, void *v) } static ssize_t -ll_max_cached_mb_seq_write(struct file *file, const char *buffer, +ll_max_cached_mb_seq_write(struct file *file, const char __user *buffer, size_t count, loff_t *off) { struct seq_file *m = file->private_data; @@ -408,27 +417,42 @@ ll_max_cached_mb_seq_write(struct file *file, const char *buffer, struct ll_sb_info *sbi = ll_s2sbi(sb); struct cl_client_cache *cache = &sbi->ll_cache; struct lu_env *env; + __u64 val; + long diff = 0; + long nrpages = 0; + long pages_number; int refcheck; - int mult, rc, pages_number; - int diff = 0; - int nrpages = 0; + int mult; + long rc; + char kernbuf[128]; ENTRY; + if (count >= sizeof(kernbuf)) + RETURN(-EINVAL); + + if (copy_from_user(kernbuf, buffer, count)) + RETURN(-EFAULT); + kernbuf[count] = 0; + mult = 1 << (20 - PAGE_CACHE_SHIFT); - buffer = lprocfs_find_named_value(buffer, "max_cached_mb:", &count); - rc = lprocfs_write_frac_helper(buffer, count, &pages_number, mult); + buffer += lprocfs_find_named_value(kernbuf, "max_cached_mb:", &count) - + kernbuf; + rc = lprocfs_write_frac_u64_helper(buffer, count, &val, mult); if (rc) RETURN(rc); + if (val > LONG_MAX) + return -ERANGE; + pages_number = (long)val; + if (pages_number < 0 || pages_number > totalram_pages) { CERROR("%s: can't set max cache more than %lu MB\n", ll_get_fsname(sb, NULL, 0), totalram_pages >> (20 - PAGE_CACHE_SHIFT)); RETURN(-ERANGE); } - - if (sbi->ll_dt_exp == NULL) /* being initialized */ - GOTO(out, rc = 0); + /* Allow enough cache so clients can make well-formed RPCs */ + pages_number = max_t(long, pages_number, PTLRPC_MAX_BRW_PAGES); spin_lock(&sbi->ll_lock); diff = pages_number - cache->ccc_lru_max; @@ -436,7 +460,7 @@ ll_max_cached_mb_seq_write(struct file *file, const char *buffer, /* easy - add more LRU slots. */ if (diff >= 0) { - cfs_atomic_add(diff, &cache->ccc_lru_left); + atomic_long_add(diff, &cache->ccc_lru_left); GOTO(out, rc = 0); } @@ -446,18 +470,18 @@ ll_max_cached_mb_seq_write(struct file *file, const char *buffer, diff = -diff; while (diff > 0) { - int tmp; + long tmp; /* reduce LRU budget from free slots. */ do { - int ov, nv; + long ov, nv; - ov = cfs_atomic_read(&cache->ccc_lru_left); + ov = atomic_long_read(&cache->ccc_lru_left); if (ov == 0) break; nv = ov > diff ? ov - diff : 0; - rc = cfs_atomic_cmpxchg(&cache->ccc_lru_left, ov, nv); + rc = atomic_long_cmpxchg(&cache->ccc_lru_left, ov, nv); if (likely(ov == rc)) { diff -= ov - nv; nrpages += ov - nv; @@ -468,6 +492,11 @@ ll_max_cached_mb_seq_write(struct file *file, const char *buffer, if (diff <= 0) break; + if (sbi->ll_dt_exp == NULL) { /* being initialized */ + rc = -ENODEV; + break; + } + /* difficult - have to ask OSCs to drop LRU slots. */ tmp = diff << 1; rc = obd_set_info_async(env, sbi->ll_dt_exp, @@ -486,7 +515,7 @@ out: spin_unlock(&sbi->ll_lock); rc = count; } else { - cfs_atomic_add(nrpages, &cache->ccc_lru_left); + atomic_long_add(nrpages, &cache->ccc_lru_left); } return rc; } @@ -500,7 +529,8 @@ static int ll_checksum_seq_show(struct seq_file *m, void *v) return seq_printf(m, "%u\n", (sbi->ll_flags & LL_SBI_CHECKSUM) ? 1 : 0); } -static ssize_t ll_checksum_seq_write(struct file *file, const char *buffer, +static ssize_t ll_checksum_seq_write(struct file *file, + const char __user *buffer, size_t count, loff_t *off) { struct seq_file *m = file->private_data; @@ -535,7 +565,8 @@ static int ll_max_rw_chunk_seq_show(struct seq_file *m, void *v) return seq_printf(m, "%lu\n", ll_s2sbi(sb)->ll_max_rw_chunk); } -static ssize_t ll_max_rw_chunk_seq_write(struct file *file, const char *buffer, +static ssize_t ll_max_rw_chunk_seq_write(struct file *file, + const char __user *buffer, size_t count, loff_t *off) { struct seq_file *m = file->private_data; @@ -564,8 +595,8 @@ static int ll_rd_track_id(struct seq_file *m, enum stats_track_type type) } } -static int ll_wr_track_id(const char *buffer, unsigned long count, void *data, - enum stats_track_type type) +static int ll_wr_track_id(const char __user *buffer, unsigned long count, + void *data, enum stats_track_type type) { struct super_block *sb = data; int rc, pid; @@ -587,7 +618,8 @@ static int ll_track_pid_seq_show(struct seq_file *m, void *v) return ll_rd_track_id(m, STATS_TRACK_PID); } -static ssize_t ll_track_pid_seq_write(struct file *file, const char *buffer, +static ssize_t ll_track_pid_seq_write(struct file *file, + const char __user *buffer, size_t count, loff_t *off) { struct seq_file *seq = file->private_data; @@ -600,7 +632,8 @@ static int ll_track_ppid_seq_show(struct seq_file *m, void *v) return ll_rd_track_id(m, STATS_TRACK_PPID); } -static ssize_t ll_track_ppid_seq_write(struct file *file, const char *buffer, +static ssize_t ll_track_ppid_seq_write(struct file *file, + const char __user *buffer, size_t count, loff_t *off) { struct seq_file *seq = file->private_data; @@ -613,7 +646,8 @@ static int ll_track_gid_seq_show(struct seq_file *m, void *v) return ll_rd_track_id(m, STATS_TRACK_GID); } -static ssize_t ll_track_gid_seq_write(struct file *file, const char *buffer, +static ssize_t ll_track_gid_seq_write(struct file *file, + const char __user *buffer, size_t count, loff_t *off) { struct seq_file *seq = file->private_data; @@ -629,7 +663,8 @@ static int ll_statahead_max_seq_show(struct seq_file *m, void *v) return seq_printf(m, "%u\n", sbi->ll_sa_max); } -static ssize_t ll_statahead_max_seq_write(struct file *file, const char *buffer, +static ssize_t ll_statahead_max_seq_write(struct file *file, + const char __user *buffer, size_t count, loff_t *off) { struct seq_file *m = file->private_data; @@ -659,7 +694,8 @@ static int ll_statahead_agl_seq_show(struct seq_file *m, void *v) sbi->ll_flags & LL_SBI_AGL_ENABLED ? 1 : 0); } -static ssize_t ll_statahead_agl_seq_write(struct file *file, const char *buffer, +static ssize_t ll_statahead_agl_seq_write(struct file *file, + const char __user *buffer, size_t count, loff_t *off) { struct seq_file *m = file->private_data; @@ -703,7 +739,8 @@ static int ll_lazystatfs_seq_show(struct seq_file *m, void *v) (sbi->ll_flags & LL_SBI_LAZYSTATFS) ? 1 : 0); } -static ssize_t ll_lazystatfs_seq_write(struct file *file, const char *buffer, +static ssize_t ll_lazystatfs_seq_write(struct file *file, + const char __user *buffer, size_t count, loff_t *off) { struct seq_file *m = file->private_data; @@ -723,20 +760,65 @@ static ssize_t ll_lazystatfs_seq_write(struct file *file, const char *buffer, } LPROC_SEQ_FOPS(ll_lazystatfs); -static int ll_maxea_size_seq_show(struct seq_file *m, void *v) +static int ll_max_easize_seq_show(struct seq_file *m, void *v) { struct super_block *sb = m->private; struct ll_sb_info *sbi = ll_s2sbi(sb); unsigned int ealen; int rc; - rc = ll_get_max_mdsize(sbi, &ealen); - if (rc) - return rc; + rc = ll_get_max_mdsize(sbi, &ealen); + if (rc) + return rc; + + return seq_printf(m, "%u\n", ealen); +} +LPROC_SEQ_FOPS_RO(ll_max_easize); + +static int ll_defult_easize_seq_show(struct seq_file *m, void *v) +{ + struct super_block *sb = m->private; + struct ll_sb_info *sbi = ll_s2sbi(sb); + unsigned int ealen; + int rc; + + rc = ll_get_default_mdsize(sbi, &ealen); + if (rc) + return rc; return seq_printf(m, "%u\n", ealen); } -LPROC_SEQ_FOPS_RO(ll_maxea_size); +LPROC_SEQ_FOPS_RO(ll_defult_easize); + +static int ll_max_cookiesize_seq_show(struct seq_file *m, void *v) +{ + struct super_block *sb = m->private; + struct ll_sb_info *sbi = ll_s2sbi(sb); + unsigned int cookielen; + int rc; + + rc = ll_get_max_cookiesize(sbi, &cookielen); + if (rc) + return rc; + + return seq_printf(m, "%u\n", cookielen); +} +LPROC_SEQ_FOPS_RO(ll_max_cookiesize); + +static int ll_defult_cookiesize_seq_show(struct seq_file *m, void *v) +{ + struct super_block *sb = m->private; + struct ll_sb_info *sbi = ll_s2sbi(sb); + unsigned int cookielen; + int rc; + + rc = ll_get_default_cookiesize(sbi, &cookielen); + if (rc) + return rc; + + return seq_printf(m, "%u\n", cookielen); +} +LPROC_SEQ_FOPS_RO(ll_defult_cookiesize); static int ll_sbi_flags_seq_show(struct seq_file *m, void *v) { @@ -767,15 +849,115 @@ static int ll_unstable_stats_seq_show(struct seq_file *m, void *v) struct super_block *sb = m->private; struct ll_sb_info *sbi = ll_s2sbi(sb); struct cl_client_cache *cache = &sbi->ll_cache; - int pages, mb; + long pages; + int mb; - pages = cfs_atomic_read(&cache->ccc_unstable_nr); + pages = atomic_long_read(&cache->ccc_unstable_nr); mb = (pages * PAGE_CACHE_SIZE) >> 20; - return seq_printf(m, "unstable_pages: %8d\n" - "unstable_mb: %8d\n", pages, mb); + return seq_printf(m, "unstable_check: %8d\n" + "unstable_pages: %12ld\n" + "unstable_mb: %8d\n", + cache->ccc_unstable_check, pages, mb); +} + +static ssize_t ll_unstable_stats_seq_write(struct file *file, + const char __user *buffer, + size_t count, loff_t *unused) +{ + struct seq_file *seq = file->private_data; + struct ll_sb_info *sbi = ll_s2sbi((struct super_block *)seq->private); + char kernbuf[128]; + int val, rc; + + if (count == 0) + return 0; + if (count >= sizeof(kernbuf)) + return -EINVAL; + + if (copy_from_user(kernbuf, buffer, count)) + return -EFAULT; + kernbuf[count] = 0; + + buffer += lprocfs_find_named_value(kernbuf, "unstable_check:", &count) - + kernbuf; + rc = lprocfs_write_helper(buffer, count, &val); + if (rc < 0) + return rc; + + /* borrow lru lock to set the value */ + spin_lock(&sbi->ll_cache.ccc_lru_lock); + sbi->ll_cache.ccc_unstable_check = !!val; + spin_unlock(&sbi->ll_cache.ccc_lru_lock); + + return count; } -LPROC_SEQ_FOPS_RO(ll_unstable_stats); +LPROC_SEQ_FOPS(ll_unstable_stats); + +static int ll_root_squash_seq_show(struct seq_file *m, void *v) +{ + struct super_block *sb = m->private; + struct ll_sb_info *sbi = ll_s2sbi(sb); + struct root_squash_info *squash = &sbi->ll_squash; + + return seq_printf(m, "%u:%u\n", squash->rsi_uid, squash->rsi_gid); +} + +static ssize_t ll_root_squash_seq_write(struct file *file, + const char __user *buffer, + size_t count, loff_t *off) +{ + struct seq_file *m = file->private_data; + struct super_block *sb = m->private; + struct ll_sb_info *sbi = ll_s2sbi(sb); + struct root_squash_info *squash = &sbi->ll_squash; + + return lprocfs_wr_root_squash(buffer, count, squash, + ll_get_fsname(sb, NULL, 0)); +} +LPROC_SEQ_FOPS(ll_root_squash); + +static int ll_nosquash_nids_seq_show(struct seq_file *m, void *v) +{ + struct super_block *sb = m->private; + struct ll_sb_info *sbi = ll_s2sbi(sb); + struct root_squash_info *squash = &sbi->ll_squash; + int len, rc; + + down_read(&squash->rsi_sem); + if (!list_empty(&squash->rsi_nosquash_nids)) { + len = cfs_print_nidlist(m->buf + m->count, m->size - m->count, + &squash->rsi_nosquash_nids); + m->count += len; + rc = seq_printf(m, "\n"); + } else { + rc = seq_printf(m, "NONE\n"); + } + up_read(&squash->rsi_sem); + + return rc; +} + +static ssize_t ll_nosquash_nids_seq_write(struct file *file, + const char __user *buffer, + size_t count, loff_t *off) +{ + struct seq_file *m = file->private_data; + struct super_block *sb = m->private; + struct ll_sb_info *sbi = ll_s2sbi(sb); + struct root_squash_info *squash = &sbi->ll_squash; + int rc; + + rc = lprocfs_wr_nosquash_nids(buffer, count, squash, + ll_get_fsname(sb, NULL, 0)); + if (rc < 0) + return rc; + + ll_compute_rootsquash_state(sbi); + + return rc; +} +LPROC_SEQ_FOPS(ll_nosquash_nids); struct lprocfs_seq_vars lprocfs_llite_obd_vars[] = { { .name = "uuid", @@ -825,19 +1007,29 @@ struct lprocfs_seq_vars lprocfs_llite_obd_vars[] = { { .name = "lazystatfs", .fops = &ll_lazystatfs_fops }, { .name = "max_easize", - .fops = &ll_maxea_size_fops }, + .fops = &ll_max_easize_fops }, + { .name = "default_easize", + .fops = &ll_defult_easize_fops }, + { .name = "max_cookiesize", + .fops = &ll_max_cookiesize_fops }, + { .name = "default_cookiesize", + .fops = &ll_defult_cookiesize_fops }, { .name = "sbi_flags", .fops = &ll_sbi_flags_fops }, { .name = "xattr_cache", .fops = &ll_xattr_cache_fops }, { .name = "unstable_stats", .fops = &ll_unstable_stats_fops }, - { 0 } + { .name = "root_squash", + .fops = &ll_root_squash_fops }, + { .name = "nosquash_nids", + .fops = &ll_nosquash_nids_fops }, + { NULL } }; #define MAX_STRING_SIZE 128 -struct llite_file_opcode { +static const struct llite_file_opcode { __u32 opcode; __u32 type; const char *opname; @@ -902,7 +1094,8 @@ void ll_stats_ops_tally(struct ll_sb_info *sbi, int op, int count) sbi->ll_stats_track_id == current->parent->pid) lprocfs_counter_add(sbi->ll_stats, op, count); else if (sbi->ll_stats_track_type == STATS_TRACK_GID && - sbi->ll_stats_track_id == current_gid()) + sbi->ll_stats_track_id == + from_kgid(&init_user_ns, current_gid())) lprocfs_counter_add(sbi->ll_stats, op, count); } EXPORT_SYMBOL(ll_stats_ops_tally); @@ -1164,24 +1357,39 @@ static int ll_rw_extents_stats_pp_seq_show(struct seq_file *seq, void *v) } static ssize_t ll_rw_extents_stats_pp_seq_write(struct file *file, - const char *buf, size_t len, - loff_t *off) + const char __user *buf, + size_t len, + loff_t *off) { - struct seq_file *seq = file->private_data; - struct ll_sb_info *sbi = seq->private; - struct ll_rw_extents_info *io_extents = &sbi->ll_rw_extents_info; - int i; - int value = 1, rc = 0; + struct seq_file *seq = file->private_data; + struct ll_sb_info *sbi = seq->private; + struct ll_rw_extents_info *io_extents = &sbi->ll_rw_extents_info; + int i; + int value = 1, rc = 0; - rc = lprocfs_write_helper(buf, len, &value); - if (rc < 0 && (strcmp(buf, "disabled") == 0 || - strcmp(buf, "Disabled") == 0)) - value = 0; + if (len == 0) + return -EINVAL; - if (value == 0) - sbi->ll_rw_stats_on = 0; - else - sbi->ll_rw_stats_on = 1; + rc = lprocfs_write_helper(buf, len, &value); + if (rc < 0 && len < 16) { + char kernbuf[16]; + + if (copy_from_user(kernbuf, buf, len)) + return -EFAULT; + kernbuf[len] = 0; + + if (kernbuf[len - 1] == '\n') + kernbuf[len - 1] = 0; + + if (strcmp(kernbuf, "disabled") == 0 || + strcmp(kernbuf, "Disabled") == 0) + value = 0; + } + + if (value == 0) + sbi->ll_rw_stats_on = 0; + else + sbi->ll_rw_stats_on = 1; spin_lock(&sbi->ll_pp_extent_lock); for (i = 0; i < LL_PROCESS_HIST_MAX; i++) { @@ -1223,24 +1431,40 @@ static int ll_rw_extents_stats_seq_show(struct seq_file *seq, void *v) return 0; } -static ssize_t ll_rw_extents_stats_seq_write(struct file *file, const char *buf, - size_t len, loff_t *off) +static ssize_t ll_rw_extents_stats_seq_write(struct file *file, + const char __user *buf, + size_t len, loff_t *off) { - struct seq_file *seq = file->private_data; - struct ll_sb_info *sbi = seq->private; - struct ll_rw_extents_info *io_extents = &sbi->ll_rw_extents_info; - int i; - int value = 1, rc = 0; + struct seq_file *seq = file->private_data; + struct ll_sb_info *sbi = seq->private; + struct ll_rw_extents_info *io_extents = &sbi->ll_rw_extents_info; + int i; + int value = 1, rc = 0; - rc = lprocfs_write_helper(buf, len, &value); - if (rc < 0 && (strcmp(buf, "disabled") == 0 || - strcmp(buf, "Disabled") == 0)) - value = 0; + if (len == 0) + return -EINVAL; + + rc = lprocfs_write_helper(buf, len, &value); + if (rc < 0 && len < 16) { + char kernbuf[16]; + + if (copy_from_user(kernbuf, buf, len)) + return -EFAULT; + kernbuf[len] = 0; + + if (kernbuf[len - 1] == '\n') + kernbuf[len - 1] = 0; + + if (strcmp(kernbuf, "disabled") == 0 || + strcmp(kernbuf, "Disabled") == 0) + value = 0; + } + + if (value == 0) + sbi->ll_rw_stats_on = 0; + else + sbi->ll_rw_stats_on = 1; - if (value == 0) - sbi->ll_rw_stats_on = 0; - else - sbi->ll_rw_stats_on = 1; spin_lock(&sbi->ll_pp_extent_lock); for (i = 0; i <= LL_PROCESS_HIST_MAX; i++) { io_extents->pp_extents[i].pid = 0; @@ -1251,7 +1475,6 @@ static ssize_t ll_rw_extents_stats_seq_write(struct file *file, const char *buf, return len; } - LPROC_SEQ_FOPS(ll_rw_extents_stats); void ll_rw_stats_tally(struct ll_sb_info *sbi, pid_t pid, @@ -1413,25 +1636,40 @@ static int ll_rw_offset_stats_seq_show(struct seq_file *seq, void *v) return 0; } -static ssize_t ll_rw_offset_stats_seq_write(struct file *file, const char *buf, - size_t len, loff_t *off) +static ssize_t ll_rw_offset_stats_seq_write(struct file *file, + const char __user *buf, + size_t len, loff_t *off) { - struct seq_file *seq = file->private_data; - struct ll_sb_info *sbi = seq->private; - struct ll_rw_process_info *process_info = sbi->ll_rw_process_info; - struct ll_rw_process_info *offset_info = sbi->ll_rw_offset_info; - int value = 1, rc = 0; + struct seq_file *seq = file->private_data; + struct ll_sb_info *sbi = seq->private; + struct ll_rw_process_info *process_info = sbi->ll_rw_process_info; + struct ll_rw_process_info *offset_info = sbi->ll_rw_offset_info; + int value = 1, rc = 0; - rc = lprocfs_write_helper(buf, len, &value); + if (len == 0) + return -EINVAL; - if (rc < 0 && (strcmp(buf, "disabled") == 0 || - strcmp(buf, "Disabled") == 0)) - value = 0; + rc = lprocfs_write_helper(buf, len, &value); - if (value == 0) - sbi->ll_rw_stats_on = 0; - else - sbi->ll_rw_stats_on = 1; + if (rc < 0 && len < 16) { + char kernbuf[16]; + + if (copy_from_user(kernbuf, buf, len)) + return -EFAULT; + kernbuf[len] = 0; + + if (kernbuf[len - 1] == '\n') + kernbuf[len - 1] = 0; + + if (strcmp(kernbuf, "disabled") == 0 || + strcmp(kernbuf, "Disabled") == 0) + value = 0; + } + + if (value == 0) + sbi->ll_rw_stats_on = 0; + else + sbi->ll_rw_stats_on = 1; spin_lock(&sbi->ll_process_lock); sbi->ll_offset_process_count = 0;