vfs: hot data tracking
Subsystems (9)
cover vfs: hot data tracking
From: Zhi Yong Wu <[email protected]>
HI, guys,
Any comments or ideas are appreciated, thanks.
NOTE:
The patchset can be obtained via my kernel dev git on github:
[email protected]:wuzhy/kernel.git hot_tracking
If you're interested, you can also review them via
https://github.com/wuzhy/kernel/commits/hot_tracking
For more info, please check hot_tracking.txt in Documentation
TODO List:
1.) Need to do scalability or performance tests. - Required 2.) Need one simpler but efficient temp calculation function 3.) How to save the file temperature among the umount to be able to
preserve the file tempreture after reboot - Optional
Changelog:
- Embed struct hot_type in struct file_system_type [Darrick J. Wong]
- Cleanup Some issues [David Sterba]
- Use a static hot debugfs root [Greg KH]
- Rewritten debugfs support based on seq_file operation. [Dave Chinner]
- Refactored workqueue support. [Dave Chinner]
- Turn some Micro into be tunable [Zhiyong, Zheng Liu]
TIME_TO_KICK, and HEAT_UPDATE_DELAY
- Introduce hot func registering framework [Zhiyong]
- Remove global variable for hot tracking [Zhiyong]
- Add xfs hot tracking support [Dave Chinner]
- Add ext4 hot tracking support [Zheng Liu]
- Cleanedup a lot of other issues [Dave Chinner]
- Converted to Radix trees, not RB-tree [Zhiyong, Dave Chinner]
- Added memory shrinker [Dave Chinner]
- Converted to one workqueue to update map info periodically [Dave Chinner]
- Cleanedup a lot of other issues [Dave Chinner]
- Reduce new files and put all in fs/hot_tracking.[ch] [Dave Chinner]
- Add btrfs hot tracking support [Zhiyong]
- The first three patches can probably just be flattened into one. [Marco Stornelli , Dave Chinner]
Dave Chinner (1):
xfs: add hot tracking support
Zheng Liu (1):
ext4: add hot tracking support
Zhi Yong Wu (16):
vfs: introduce some data structures
vfs: add init and cleanup functions
vfs: add I/O frequency update function
vfs: add two map info arrays
vfs: add hooks to enable hot tracking
vfs: add temp calculation function
vfs: add map info update function
vfs: add aging function
vfs: add one work queue
vfs: add FS hot type support
vfs: register one shrinker
vfs: add one ioctl interface
vfs: add debugfs support
procfs: add two hot_track proc files
btrfs: add hot tracking support
vfs: add documentation
Documentation/filesystems/00-INDEX | 2 + Documentation/filesystems/hot_tracking.txt | 263 ++++++ fs/Makefile | 2 +- fs/btrfs/ctree.h | 1 + fs/btrfs/super.c | 22 +- fs/compat_ioctl.c | 5 + fs/dcache.c | 2 + fs/direct-io.c | 6 + fs/ext4/ext4.h | 3 + fs/ext4/super.c | 13 +- fs/hot_tracking.c | 1321 ++++++++++++++++++++++++++++ fs/hot_tracking.h | 53 ++ fs/ioctl.c | 74 ++ fs/xfs/xfs_mount.h | 1 + fs/xfs/xfs_super.c | 16 + include/linux/fs.h | 5 + include/linux/hot_tracking.h | 146 +++ kernel/sysctl.c | 14 + mm/filemap.c | 6 + mm/page-writeback.c | 12 + mm/readahead.c | 7 + 21 files changed, 1971 insertions(+), 3 deletions(-) create mode 100644 Documentation/filesystems/hot_tracking.txt create mode 100644 fs/hot_tracking.c create mode 100644 fs/hot_tracking.h create mode 100644 include/linux/hot_tracking.h
Signature · 1 line
1.7.6.5
PATCH 1/18 vfs: introduce some data structures
From: Zhi Yong Wu <[email protected]>
One root structure hot_info is defined, is hooked
up in super_block, and will be used to hold radix tree root, hash list root and some other information, etc.
Adds hot_inode_tree struct to keep track of
frequently accessed files, and be keyed by {inode, offset}. Trees contain hot_inode_items representing those files and ranges.
Having these trees means that vfs can quickly determine the
temperature of some data by doing some calculations on the hot_freq_data struct that hangs off of the tree item.
Define two items hot_inode_item and hot_range_item,
one of them represents one tracked file
to keep track of its access frequency and the tree of ranges in this file, while the latter represents
a file range of one inode.
Each of the two structures contains a hot_freq_data
struct with its frequency of access metrics (number of {reads, writes}, last {read,write} time, frequency of {reads,writes}).
Also, each hot_inode_item contains one hot_range_tree
struct which is keyed by {inode, offset, length} and used to keep track of all the ranges in this file.
--- fs/Makefile | 2 +- fs/dcache.c | 2 + fs/hot_tracking.c | 109 ++++++++++++++++++++++++++++++++++++++++++ fs/hot_tracking.h | 23 +++++++++ include/linux/hot_tracking.h | 73 ++++++++++++++++++++++++++++ 5 files changed, 208 insertions(+), 1 deletions(-) create mode 100644 fs/hot_tracking.c create mode 100644 fs/hot_tracking.h create mode 100644 include/linux/hot_tracking.h
fs/Makefile +1 -1
@@ -11,7 +11,7 @@ obj-y := open.o read_write.o file_table.o super.o \ attr.o bad_inode.o file.o filesystems.o namespace.o \ seq_file.o xattr.o libfs.o fs-writeback.o \ pnode.o drop_caches.o splice.o sync.o utimes.o \ - stack.o fs_struct.o statfs.o + stack.o fs_struct.o statfs.o hot_tracking.o ifeq ($(CONFIG_BLOCK),y) obj-y += buffer.o bio.o block_dev.o direct-io.o mpage.o ioprio.o
fs/dcache.c +2 -0
@@ -37,6 +37,7 @@#include <linux/rculist_bl.h> #include <linux/prefetch.h> #include <linux/ratelimit.h> +#include <linux/hot_tracking.h> #include "internal.h" #include "mount.h"
@@ -3172,4 +3173,5 @@ void __init vfs_caches_init(unsigned long mempages)mnt_init ();bdev_cache_init ();chrdev_init (); +hot_cache_init (); }
fs/hot_tracking.c new file +109 -0
109 lines not on this page · raw message
fs/hot_tracking.h new file +23 -0
@@ -0,0 +1,23 @@ +/* +* fs/hot_tracking.h +* +* Copyright (C) 2012 IBM Corp. All rights reserved. +* Written by Zhi Yong Wu <[email protected]> +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public +* License v2 as published by the Free Software Foundation. +*/ + +#ifndef __HOT_TRACKING__ +#define __HOT_TRACKING__ + +#include <linux/radix-tree.h> +#include <linux/workqueue.h> +#include <linux/hot_tracking.h> + +/* values for hot_freq_data flags */ +#define FREQ_DATA_TYPE_INODE (1 << 0) +#define FREQ_DATA_TYPE_RANGE (1 << 1) + +#endif /* __HOT_TRACKING__ */
include/linux/hot_tracking.h new file +73 -0
73 lines not on this page · raw message
Signature · 1 line
1.7.6.5
PATCH 2/18 vfs: add init and cleanup functions
In reply to [email protected]From: Zhi Yong Wu <[email protected]>
Add initialization function to create some
key data structures when hot tracking is enabled;
Clean up them when hot tracking is disabled
--- fs/hot_tracking.c | 124 ++++++++++++++++++++++++++++++++++++++++++ include/linux/fs.h | 4 ++ include/linux/hot_tracking.h | 3 + 3 files changed, 131 insertions(+), 0 deletions(-)
fs/hot_tracking.c +124 -0
@@ -76,12 +76,103 @@ static void hot_inode_item_init(struct hot_inode_item *he, he->hot_inode_tree = hot_inode_tree;kref_init (&he->hot_inode.refs);spin_lock_init (&he->hot_inode.lock); +INIT_LIST_HEAD (&he->hot_inode.n_list); he->hot_inode.hot_freq_data.avg_delta_reads = (u64) -1 ; he->hot_inode.hot_freq_data.avg_delta_writes = (u64) -1 ; he->hot_inode.hot_freq_data.flags =FREQ_DATA_TYPE_INODE ;hot_range_tree_init (he); } +static void hot_range_item_free (struct kref *kref) +{ +struct hot_comm_item *comm_item =container_of (kref, + struct hot_comm_item, refs); +struct hot_range_item *hr =container_of (comm_item, + struct hot_range_item, hot_range); + +radix_tree_delete (&hr->hot_inode->hot_range_tree, hr->start); +kmem_cache_free (hot_range_item_cachep, hr); +} + +/* +* Drops the reference out on hot_range_item by one +* and free the structure if the reference count hits zero +*/ +static void hot_range_item_put (struct hot_range_item *hr) +{ +kref_put (&hr->hot_range.refs, hot_range_item_free); +} + +/* Frees the entire hot_range_tree. */ +static void hot_range_tree_free (struct hot_inode_item *he) +{ +struct hot_range_item *hr_nodes[8 ]; +loff_t start =0 ; +int i, n; + +while (1 ) { +spin_lock (&he->lock); + n =radix_tree_gang_lookup (&he->hot_range_tree, + (void **)hr_nodes, start, +ARRAY_SIZE (hr_nodes)); +if (!n) { +spin_unlock (&he->lock); +break ; + } + + start = hr_nodes[n -1 ]->start +1 ; +for (i =0 ; i < n; i++) +hot_range_item_put (hr_nodes[i]); +spin_unlock (&he->lock); + } +} + +static void hot_inode_item_free (struct kref *kref) +{ +struct hot_comm_item *comm_item =container_of (kref, + struct hot_comm_item, refs); +struct hot_inode_item *he =container_of (comm_item, + struct hot_inode_item, hot_inode); + +hot_range_tree_free (he); +radix_tree_delete (he->hot_inode_tree, he->i_ino); +kmem_cache_free (hot_inode_item_cachep, he); +} + +/* +* Drops the reference out on hot_inode_item by one +* and free the structure if the reference count hits zero +*/ +void hot_inode_item_put (struct hot_inode_item *he) +{ +kref_put (&he->hot_inode.refs, hot_inode_item_free); +} +EXPORT_SYMBOL_GPL (hot_inode_item_put); + +/* Frees the entire hot_inode_tree. */ +static void hot_inode_tree_exit (struct hot_info *root) +{ +struct hot_inode_item *hi_nodes[8 ]; +unsigned long ino =0 ; +int i, n; + +while (1 ) { +spin_lock (&root->lock); + n =radix_tree_gang_lookup (&root->hot_inode_tree, + (void **)hi_nodes, ino, +ARRAY_SIZE (hi_nodes)); +if (!n) { +spin_unlock (&root->lock); +break ; + } + + ino = hi_nodes[n -1 ]->i_ino +1 ; +for (i =0 ; i < n; i++) +hot_inode_item_put (hi_nodes[i]); +spin_unlock (&root->lock); + } +} +/* * Initialize kmem cache for hot_inode_item and hot_range_item. */
@@ -107,3 +198,36 @@ err:kmem_cache_destroy (hot_inode_item_cachep); }EXPORT_SYMBOL_GPL (hot_cache_init); + +/* +* Initialize the data structures for hot data tracking. +*/ +int hot_track_init (struct super_block *sb) +{ +struct hot_info *root; +int ret = -ENOMEM ; + + root =kzalloc (sizeof (struct hot_info ),GFP_NOFS ); +if (!root) { +printk (KERN_ERR "%s: Failed to malloc memory for " +"hot_info\n" , __func__); +return ret; + } + + sb->s_hot_root = root; +hot_inode_tree_init (root); + +printk (KERN_INFO "VFS: Turning on hot data tracking\n" ); + +return 0 ; +} +EXPORT_SYMBOL_GPL (hot_track_init); + +void hot_track_exit (struct super_block *sb) +{ +struct hot_info *root = sb->s_hot_root; + +hot_inode_tree_exit (root); +kfree (root); +} +EXPORT_SYMBOL_GPL (hot_track_exit);
include/linux/fs.h +4 -0
@@ -27,6 +27,7 @@#include <linux/lockdep.h> #include <linux/percpu-rwsem.h> #include <linux/blk_types.h> +#include <linux/hot_tracking.h> #include <asm/byteorder.h> #include <uapi/linux/fs.h>
@@ -1321,6 +1322,9 @@ struct super_block {/* Being remounted read-only */ int s_readonly_remount; + +/* Hot data tracking*/ +struct hot_info *s_hot_root; };/* superblock cache pruning functions */
include/linux/hot_tracking.h +3 -0
@@ -69,5 +69,8 @@ struct hot_info { };extern void __init hot_cache_init (void ); +extern int hot_track_init (struct super_block *sb); +extern void hot_track_exit (struct super_block *sb); +extern void hot_inode_item_put (struct hot_inode_item *he);#endif /* _LINUX_HOTTRACK_H */
Signature · 1 line
1.7.6.5
PATCH 3/18 vfs: add I/O frequency update function
In reply to [email protected]From: Zhi Yong Wu <[email protected]>
Add some util helpers to update access frequencies
for one file or its range.
--- fs/hot_tracking.c | 174 ++++++++++++++++++++++++++++++++++++++++++ fs/hot_tracking.h | 5 + include/linux/hot_tracking.h | 4 + 3 files changed, 183 insertions(+), 0 deletions(-)
fs/hot_tracking.c +174 -0
@@ -173,6 +173,131 @@ static void hot_inode_tree_exit(struct hot_info *root) } } +struct hot_inode_item +*hot_inode_item_lookup (struct hot_info *root,unsigned long ino) +{ +struct hot_inode_item *he; +int ret; + +again: +spin_lock (&root->lock); + he =radix_tree_lookup (&root->hot_inode_tree, ino); +if (he) { +kref_get (&he->hot_inode.refs); +spin_unlock (&root->lock); +return he; + } +spin_unlock (&root->lock); + + he =kmem_cache_zalloc (hot_inode_item_cachep,GFP_NOFS ); +if (!he) +return ERR_PTR (-ENOMEM ); + +hot_inode_item_init (he, ino, &root->hot_inode_tree); + + ret =radix_tree_preload (GFP_NOFS & ~__GFP_HIGHMEM); +if (ret) { +kmem_cache_free (hot_inode_item_cachep, he); +return ERR_PTR (ret); + } + +spin_lock (&root->lock); + ret =radix_tree_insert (&root->hot_inode_tree, ino, he); +if (ret == -EEXIST ) { +kmem_cache_free (hot_inode_item_cachep, he); +spin_unlock (&root->lock); +radix_tree_preload_end (); + goto again; + } +spin_unlock (&root->lock); +radix_tree_preload_end (); + +kref_get (&he->hot_inode.refs); +return he; +} +EXPORT_SYMBOL_GPL (hot_inode_item_lookup); + +static struct hot_range_item +*hot_range_item_lookup (struct hot_inode_item *he, +loff_t start) +{ +struct hot_range_item *hr; +int ret; + +again: +spin_lock (&he->lock); + hr =radix_tree_lookup (&he->hot_range_tree, start); +if (hr) { +kref_get (&hr->hot_range.refs); +spin_unlock (&he->lock); +return hr; + } +spin_unlock (&he->lock); + + hr =kmem_cache_zalloc (hot_range_item_cachep,GFP_NOFS ); +if (!hr) +return ERR_PTR (-ENOMEM ); + +hot_range_item_init (hr, start, he); + + ret =radix_tree_preload (GFP_NOFS & ~__GFP_HIGHMEM); +if (ret) { +kmem_cache_free (hot_range_item_cachep, hr); +return ERR_PTR (ret); + } + +spin_lock (&he->lock); + ret =radix_tree_insert (&he->hot_range_tree, start, hr); +if (ret == -EEXIST ) { +kmem_cache_free (hot_range_item_cachep, hr); +spin_unlock (&he->lock); +radix_tree_preload_end (); + goto again; + } +spin_unlock (&he->lock); +radix_tree_preload_end (); + +kref_get (&hr->hot_range.refs); +return hr; +} + +/* +* This function does the actual work of updating +* the frequency numbers, whatever they turn out to be. +*/ +static void hot_rw_freq_calc (struct timespec old_atime, +struct timespec cur_time,u64 *avg) +{ +struct timespec delta_ts; +u64 new_delta; + + delta_ts =timespec_sub (cur_time, old_atime); + new_delta =timespec_to_ns (&delta_ts) >>FREQ_POWER ; + + *avg = (*avg <<FREQ_POWER ) - *avg + new_delta; + *avg = *avg >>FREQ_POWER ; +} + +static void hot_freq_data_update (struct hot_freq_data *freq_data,bool write) +{ +struct timespec cur_time =current_kernel_time (); + +if (write) { + freq_data->nr_writes +=1 ; +hot_rw_freq_calc (freq_data->last_write_time, + cur_time, + &freq_data->avg_delta_writes); + freq_data->last_write_time = cur_time; + }else { + freq_data->nr_reads +=1 ; +hot_rw_freq_calc (freq_data->last_read_time, + freq_data->last_read_time, + cur_time, + &freq_data->avg_delta_reads); + freq_data->last_read_time = cur_time; + } +} +/* * Initialize kmem cache for hot_inode_item and hot_range_item. */
@@ -200,6 +325,55 @@ err:EXPORT_SYMBOL_GPL (hot_cache_init);/* +* Main function to update access frequency from read/writepage(s) hooks +*/ +void hot_update_freqs (struct inode *inode,loff_t start, +size_t len,int rw) +{ +struct hot_info *root = inode->i_sb->s_hot_root; +struct hot_inode_item *he; +struct hot_range_item *hr; +loff_t cur, end; + +if (!root || (len ==0 )) +return ; + + he =hot_inode_item_lookup (root, inode->i_ino); +if (IS_ERR (he)) { +WARN_ON (1 ); +return ; + } + +spin_lock (&he->hot_inode.lock); +hot_freq_data_update (&he->hot_inode.hot_freq_data, rw); +spin_unlock (&he->hot_inode.lock); + +/* +* Align ranges on RANGE_SIZE boundary +* to prevent proliferation of range structs +*/ + end = (start + len +RANGE_SIZE -1 ) >>RANGE_BITS ; +for (cur = (start >>RANGE_BITS ); cur < end; cur++) { + hr =hot_range_item_lookup (he, cur); +if (IS_ERR (hr)) { +WARN (1 ,"hot_range_item_lookup returns %ld\n" , +PTR_ERR (hr)); +hot_inode_item_put (he); +return ; + } + +spin_lock (&hr->hot_range.lock); +hot_freq_data_update (&hr->hot_range.hot_freq_data, rw); +spin_unlock (&hr->hot_range.lock); + +hot_range_item_put (hr); + } + +hot_inode_item_put (he); +} +EXPORT_SYMBOL_GPL (hot_update_freqs); + +/* * Initialize the data structures for hot data tracking. */ int hot_track_init (struct super_block *sb)
fs/hot_tracking.h +5 -0
@@ -20,4 +20,9 @@#define FREQ_DATA_TYPE_INODE (1 << 0)#define FREQ_DATA_TYPE_RANGE (1 << 1) +/* size of sub-file ranges */ +#define RANGE_BITS 20 +#define RANGE_SIZE (1 << RANGE_BITS) +#define FREQ_POWER 4 +#endif /* __HOT_TRACKING__ */
include/linux/hot_tracking.h +4 -0
@@ -72,5 +72,9 @@ extern void __init hot_cache_init(void);extern int hot_track_init (struct super_block *sb);extern void hot_track_exit (struct super_block *sb);extern void hot_inode_item_put (struct hot_inode_item *he); +extern void hot_update_freqs (struct inode *inode,loff_t start, +size_t len,int rw); +extern struct hot_inode_item *hot_inode_item_lookup (struct hot_info *root, +unsigned long ino);#endif /* _LINUX_HOTTRACK_H */
Signature · 1 line
1.7.6.5
PATCH 4/18 vfs: add two map info arrays
In reply to [email protected]From: Zhi Yong Wu <[email protected]>
Adds two map arrays which contains
a lot of list and is used to efficiently
look up the data temperature of a file or its
ranges.
In each list of map arrays, the array node
will keep track of temperature info.
--- fs/hot_tracking.c | 60 ++++++++++++++++++++++++++++++++++++++++++ include/linux/hot_tracking.h | 16 +++++++++++ 2 files changed, 76 insertions(+), 0 deletions(-)
fs/hot_tracking.c +60 -0
@@ -58,6 +58,7 @@ static void hot_range_item_init(struct hot_range_item *hr, loff_t start, hr->hot_inode = he;kref_init (&hr->hot_range.refs);spin_lock_init (&hr->hot_range.lock); +INIT_LIST_HEAD (&hr->hot_range.n_list); hr->hot_range.hot_freq_data.avg_delta_reads = (u64) -1 ; hr->hot_range.hot_freq_data.avg_delta_writes = (u64) -1 ; hr->hot_range.hot_freq_data.flags =FREQ_DATA_TYPE_RANGE ;
@@ -89,6 +90,16 @@ static void hot_range_item_free(struct kref *kref) struct hot_comm_item, refs);struct hot_range_item *hr =container_of (comm_item, struct hot_range_item, hot_range); +struct hot_info *root =container_of ( + hr->hot_inode->hot_inode_tree, + struct hot_info, hot_inode_tree); + +spin_lock (&hr->hot_range.lock); +if (!list_empty (&hr->hot_range.n_list)) { +list_del_init (&hr->hot_range.n_list); + root->hot_map_nr--; + } +spin_unlock (&hr->hot_range.lock);radix_tree_delete (&hr->hot_inode->hot_range_tree, hr->start);kmem_cache_free (hot_range_item_cachep, hr);
@@ -133,6 +144,15 @@ static void hot_inode_item_free(struct kref *kref) struct hot_comm_item, refs);struct hot_inode_item *he =container_of (comm_item, struct hot_inode_item, hot_inode); +struct hot_info *root =container_of (he->hot_inode_tree, + struct hot_info, hot_inode_tree); + +spin_lock (&he->hot_inode.lock); +if (!list_empty (&he->hot_inode.n_list)) { +list_del_init (&he->hot_inode.n_list); + root->hot_map_nr--; + } +spin_unlock (&he->hot_inode.lock);hot_range_tree_free (he);radix_tree_delete (he->hot_inode_tree, he->i_ino);
@@ -299,6 +319,44 @@ static void hot_freq_data_update(struct hot_freq_data *freq_data, bool write) }/* +* Initialize inode and range map info. +*/ +static void hot_map_init (struct hot_info *root) +{ +int i; +for (i =0 ; i <HEAT_MAP_SIZE ; i++) { +INIT_LIST_HEAD (&root->heat_inode_map[i].node_list); +INIT_LIST_HEAD (&root->heat_range_map[i].node_list); + root->heat_inode_map[i].temp = i; + root->heat_range_map[i].temp = i; + } +} + +static void hot_map_list_free (struct list_head *node_list, +struct hot_info *root) +{ +struct list_head *pos, *next; +struct hot_comm_item *node; + +list_for_each_safe (pos, next, node_list) { + node = list_entry(pos, struct hot_comm_item, n_list); +list_del_init (&node->n_list); + root->hot_map_nr--; + } + +} + +/* Free inode and range map info */ +static void hot_map_exit (struct hot_info *root) +{ +int i; +for (i =0 ; i <HEAT_MAP_SIZE ; i++) { +hot_map_list_free (&root->heat_inode_map[i].node_list, root); +hot_map_list_free (&root->heat_range_map[i].node_list, root); + } +} + +/* * Initialize kmem cache for hot_inode_item and hot_range_item. */ void __init hot_cache_init (void )
@@ -390,6 +448,7 @@ int hot_track_init(struct super_block *sb) sb->s_hot_root = root;hot_inode_tree_init (root); +hot_map_init (root);printk (KERN_INFO "VFS: Turning on hot data tracking\n" );
@@ -401,6 +460,7 @@ void hot_track_exit(struct super_block *sb) {struct hot_info *root = sb->s_hot_root; +hot_map_exit (root);hot_inode_tree_exit (root);kfree (root); }
include/linux/hot_tracking.h +16 -0
@@ -20,6 +20,9 @@#include <linux/kref.h> #include <linux/fs.h> +#define HEAT_MAP_BITS 8 +#define HEAT_MAP_SIZE (1 << HEAT_MAP_BITS) +/* * A frequency data struct holds values that are used to * determine temperature of files and file ranges. These structs
@@ -36,11 +39,18 @@ struct hot_freq_data {u32 last_temp; }; +/* List heads in hot map array */ +struct hot_map_head { +struct list_head node_list; +u8 temp; +}; +/* The common info for both following structures */ struct hot_comm_item {struct hot_freq_data hot_freq_data;/* frequency data */ spinlock_t lock;/* protects object data */ struct kref refs;/* prevents kfree */ +struct list_head n_list;/* list node index */ };/* An item representing an inode and its access frequency */
@@ -66,6 +76,12 @@ struct hot_range_item {struct hot_info {struct radix_tree_root hot_inode_tree;spinlock_t lock;/*protect inode tree */ + +/* map of inode temperature */ +struct hot_map_head heat_inode_map[HEAT_MAP_SIZE ]; +/* map of range temperature */ +struct hot_map_head heat_range_map[HEAT_MAP_SIZE ]; +unsigned int hot_map_nr; };extern void __init hot_cache_init (void );
Signature · 1 line
1.7.6.5
PATCH 5/18 vfs: add hooks to enable hot tracking
In reply to [email protected]From: Zhi Yong Wu <[email protected]>
Miscellaneous features that implement hot data tracking
and generally make the hot data functions a bit more friendly.
--- fs/direct-io.c | 6 ++++++ mm/filemap.c | 6 ++++++ mm/page-writeback.c | 12 ++++++++++++ mm/readahead.c | 7 +++++++ 4 files changed, 31 insertions(+), 0 deletions(-)
fs/direct-io.c +6 -0
@@ -37,6 +37,7 @@#include <linux/uio.h> #include <linux/atomic.h> #include <linux/prefetch.h> +#include "hot_tracking.h" /* * How many user pages to map in one call to get_user_pages(). This determines
@@ -1297,6 +1298,11 @@ __blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,prefetch (bdev->bd_queue);prefetch ((char *)bdev->bd_queue +SMP_CACHE_BYTES ); +/* Hot data tracking */ +hot_update_freqs (inode, offset, +iov_length (iov, nr_segs), + rw &WRITE ); +return do_blockdev_direct_IO (rw, iocb, inode, bdev, iov, offset, nr_segs, get_block, end_io, submit_io, flags);
mm/filemap.c +6 -0
@@ -33,6 +33,7 @@#include <linux/hardirq.h> /* for BUG_ON(!in_atomic()) only */ #include <linux/memcontrol.h> #include <linux/cleancache.h> +#include <linux/hot_tracking.h> #include "internal.h" /*
@@ -1224,6 +1225,11 @@ readpage:* PG_error will be set again if readpage fails. */ ClearPageError (page); + +/* Hot data tracking */ +hot_update_freqs (inode, page->index <<PAGE_CACHE_SHIFT , +PAGE_CACHE_SIZE ,0 ); +/* Start the actual read. The read will unlock the page. */ error = mapping->a_ops->readpage (filp, page);
mm/page-writeback.c +12 -0
@@ -35,6 +35,7 @@#include <linux/buffer_head.h> /* __set_page_dirty_buffers */ #include <linux/pagevec.h> #include <linux/timer.h> +#include <linux/hot_tracking.h> #include <trace/events/writeback.h> /*
@@ -1903,13 +1904,24 @@ EXPORT_SYMBOL(generic_writepages);int do_writepages (struct address_space *mapping,struct writeback_control *wbc) {int ret; +pgoff_t start =0 ; +size_t count =0 ;if (wbc->nr_to_write <=0 )return 0 ; + + start = mapping->writeback_index <<PAGE_CACHE_SHIFT ; + count = wbc->nr_to_write; +if (mapping->a_ops->writepages) ret = mapping->a_ops->writepages (mapping, wbc);else ret =generic_writepages (mapping, wbc); + +/* Hot data tracking */ +hot_update_freqs (mapping->host, (loff_t )start, + (count - (u64 )wbc->nr_to_write) *PAGE_CACHE_SIZE ,1 ); +return ret; }
mm/readahead.c +7 -0
@@ -19,6 +19,7 @@#include <linux/pagemap.h> #include <linux/syscalls.h> #include <linux/file.h> +#include <linux/hot_tracking.h> /* * Initialise a struct file's readahead state. Assumes that the caller has
@@ -138,6 +139,12 @@ static int read_pages(struct address_space *mapping, struct file *filp, out:blk_finish_plug (&plug); +/* Hot data tracking */ +hot_update_freqs (mapping->host, + (list_entry(pages->prev, struct page, lru)->index) + <<PAGE_CACHE_SHIFT , + nr_pages *PAGE_CACHE_SIZE ,0 ); +return ret; }
Signature · 1 line
1.7.6.5
PATCH 6/18 vfs: add temp calculation function
In reply to [email protected]From: Zhi Yong Wu <[email protected]>
--- fs/hot_tracking.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 74 insertions(+), 0 deletions(-)
fs/hot_tracking.c +74 -0
@@ -25,6 +25,14 @@static struct kmem_cache *hot_inode_item_cachep__read_mostly ;static struct kmem_cache *hot_range_item_cachep__read_mostly ; +static u64 hot_raw_shift (u64 counter,u32 bits,bool dir) +{ +if (dir) +return counter << bits; +else +return counter >> bits; +} +/* * Initialize the inode tree. Should be called for each new inode * access or other user of the hot_inode interface.
@@ -319,6 +327,72 @@ static void hot_freq_data_update(struct hot_freq_data *freq_data, bool write) }/* +* hot_temp_calc() is responsible for distilling the six heat +* criteria down into a single temperature value for the data, +* which is an integer between 0 and HEAT_MAX_VALUE. +*/ +static u32 hot_temp_calc (struct hot_freq_data *freq_data) +{ +u32 result =0 ; + +struct timespec ckt =current_kernel_time (); +u64 cur_time =timespec_to_ns (&ckt); + +u32 nrr_heat = (u32 )hot_raw_shift ((u64 )freq_data->nr_reads, +NRR_MULTIPLIER_POWER , true); +u32 nrw_heat = (u32 )hot_raw_shift ((u64 )freq_data->nr_writes, +NRW_MULTIPLIER_POWER , true); + +u64 ltr_heat = +hot_raw_shift ((cur_time -timespec_to_ns (&freq_data->last_read_time)), +LTR_DIVIDER_POWER , false); +u64 ltw_heat = +hot_raw_shift ((cur_time -timespec_to_ns (&freq_data->last_write_time)), +LTW_DIVIDER_POWER , false); + +u64 avr_heat = +hot_raw_shift ((((u64) -1 ) - freq_data->avg_delta_reads), +AVR_DIVIDER_POWER , false); +u64 avw_heat = +hot_raw_shift ((((u64) -1 ) - freq_data->avg_delta_writes), +AVW_DIVIDER_POWER , false); + +/* ltr_heat is now guaranteed to be u32 safe */ +if (ltr_heat >=hot_raw_shift ((u64 )1 ,32 , true)) + ltr_heat =0 ; +else + ltr_heat =hot_raw_shift ((u64 )1 ,32 , true) - ltr_heat; + +/* ltw_heat is now guaranteed to be u32 safe */ +if (ltw_heat >=hot_raw_shift ((u64 )1 ,32 , true)) + ltw_heat =0 ; +else + ltw_heat =hot_raw_shift ((u64 )1 ,32 , true) - ltw_heat; + +/* avr_heat is now guaranteed to be u32 safe */ +if (avr_heat >=hot_raw_shift ((u64 )1 ,32 , true)) + avr_heat = (u32) -1 ; + +/* avw_heat is now guaranteed to be u32 safe */ +if (avw_heat >=hot_raw_shift ((u64 )1 ,32 , true)) + avw_heat = (u32) -1 ; + + nrr_heat = (u32 )hot_raw_shift ((u64 )nrr_heat, + (3 -NRR_COEFF_POWER ), false); + nrw_heat = (u32 )hot_raw_shift ((u64 )nrw_heat, + (3 -NRW_COEFF_POWER ), false); + ltr_heat =hot_raw_shift (ltr_heat, (3 -LTR_COEFF_POWER ), false); + ltw_heat =hot_raw_shift (ltw_heat, (3 -LTW_COEFF_POWER ), false); + avr_heat =hot_raw_shift (avr_heat, (3 -AVR_COEFF_POWER ), false); + avw_heat =hot_raw_shift (avw_heat, (3 -AVW_COEFF_POWER ), false); + + result = nrr_heat + nrw_heat + (u32 ) ltr_heat + + (u32 ) ltw_heat + (u32 ) avr_heat + (u32 ) avw_heat; + +return result; +} + +/* * Initialize inode and range map info. */ static void hot_map_init (struct hot_info *root)
Signature · 1 line
1.7.6.5
PATCH 7/18 vfs: add map info update function
In reply to [email protected]From: Zhi Yong Wu <[email protected]>
--- fs/hot_tracking.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/hot_tracking.h | 21 ++++++++++++++++ 2 files changed, 88 insertions(+), 0 deletions(-)
fs/hot_tracking.c +67 -0
@@ -393,6 +393,73 @@ static u32 hot_temp_calc(struct hot_freq_data *freq_data) }/* +* Calculate a new temperature and, if necessary, +* move the list_head corresponding to this inode or range +* to the proper list with the new temperature +*/ +static void hot_map_update (struct hot_freq_data *freq_data, +struct hot_info *root) +{ +struct hot_map_head *buckets, *cur_bucket; +struct hot_comm_item *comm_item; +struct hot_inode_item *he; +struct hot_range_item *hr; +u32 temp =hot_temp_calc (freq_data); +u8 a_temp = (u8 )hot_raw_shift ((u64 )temp, (32 -HEAT_MAP_BITS ), false); +u8 b_temp = (u8 )hot_raw_shift ((u64 )freq_data->last_temp, + (32 -HEAT_MAP_BITS ), false); + + comm_item =container_of (freq_data, + struct hot_comm_item, hot_freq_data); + +if (freq_data->flags &FREQ_DATA_TYPE_INODE ) { + he =container_of (comm_item, + struct hot_inode_item, hot_inode); + buckets = root->heat_inode_map; + +if (he ==NULL ) +return ; + +spin_lock (&he->hot_inode.lock); +if (list_empty (&he->hot_inode.n_list) || (a_temp != b_temp)) { +if (!list_empty (&he->hot_inode.n_list)) { +list_del_init (&he->hot_inode.n_list); + root->hot_map_nr--; + } + + cur_bucket = buckets + a_temp; +list_add_tail (&he->hot_inode.n_list, + &cur_bucket->node_list); + root->hot_map_nr++; + freq_data->last_temp = temp; + } +spin_unlock (&he->hot_inode.lock); + }else if (freq_data->flags &FREQ_DATA_TYPE_RANGE ) { + hr =container_of (comm_item, + struct hot_range_item, hot_range); + buckets = root->heat_range_map; + +if (hr ==NULL ) +return ; + +spin_lock (&hr->hot_range.lock); +if (list_empty (&hr->hot_range.n_list) || (a_temp != b_temp)) { +if (!list_empty (&hr->hot_range.n_list)) { +list_del_init (&hr->hot_range.n_list); + root->hot_map_nr--; + } + + cur_bucket = buckets + a_temp; +list_add_tail (&hr->hot_range.n_list, + &cur_bucket->node_list); + root->hot_map_nr++; + freq_data->last_temp = temp; + } +spin_unlock (&hr->hot_range.lock); + } +} + +/* * Initialize inode and range map info. */ static void hot_map_init (struct hot_info *root)
fs/hot_tracking.h +21 -0
@@ -25,4 +25,25 @@#define RANGE_SIZE (1 << RANGE_BITS)#define FREQ_POWER 4 +/* NRR/NRW heat unit = 2^X accesses */ +#define NRR_MULTIPLIER_POWER 20/* NRR - number of reads since mount */ +#define NRR_COEFF_POWER 0 +#define NRW_MULTIPLIER_POWER 20/* NRW - number of writes since mount */ +#define NRW_COEFF_POWER 0 + +/* LTR/LTW heat unit = 2^X ns of age */ +#define LTR_DIVIDER_POWER 30/* LTR - time elapsed since last read(ns) */ +#define LTR_COEFF_POWER 1 +#define LTW_DIVIDER_POWER 30/* LTW - time elapsed since last write(ns) */ +#define LTW_COEFF_POWER 1 + +/* +* AVR/AVW cold unit = 2^X ns of average delta +* AVR/AVW heat unit = HEAT_MAX_VALUE - cold unit +*/ +#define AVR_DIVIDER_POWER 40/* AVR - average delta between recent reads(ns) */ +#define AVR_COEFF_POWER 0 +#define AVW_DIVIDER_POWER 40/* AVW - average delta between recent writes(ns) */ +#define AVW_COEFF_POWER 0 +#endif /* __HOT_TRACKING__ */
Signature · 1 line
1.7.6.5
PATCH 8/18 vfs: add aging function
In reply to [email protected]From: Zhi Yong Wu <[email protected]>
--- fs/hot_tracking.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/hot_tracking.h | 6 +++++ 2 files changed, 62 insertions(+), 0 deletions(-)
fs/hot_tracking.c +56 -0
@@ -392,6 +392,24 @@ static u32 hot_temp_calc(struct hot_freq_data *freq_data)return result; } +static bool hot_is_obsolete (struct hot_freq_data *freq_data) +{ +int ret =0 ; +struct timespec ckt =current_kernel_time (); + +u64 cur_time =timespec_to_ns (&ckt); +u64 last_read_ns = + (cur_time -timespec_to_ns (&freq_data->last_read_time)); +u64 last_write_ns = + (cur_time -timespec_to_ns (&freq_data->last_write_time)); +u64 kick_ns =TIME_TO_KICK *NSEC_PER_SEC ; + +if ((last_read_ns > kick_ns) && (last_write_ns > kick_ns)) + ret =1 ; + +return ret; +} +/* * Calculate a new temperature and, if necessary, * move the list_head corresponding to this inode or range
@@ -459,6 +477,44 @@ static void hot_map_update(struct hot_freq_data *freq_data, } } +/* Update temperatures for each range item for aging purposes */ +static void hot_range_update (struct hot_inode_item *he, +struct hot_info *root) +{ +struct hot_range_item *hr_nodes[8 ]; +loff_t start =0 ; +bool obsolete; +int i, n; + +while (1 ) { +spin_lock (&he->lock); + n =radix_tree_gang_lookup (&he->hot_range_tree, + (void **)hr_nodes, start, +ARRAY_SIZE (hr_nodes)); +if (!n) { +spin_unlock (&he->lock); +break ; + } +spin_unlock (&he->lock); + + start = hr_nodes[n -1 ]->start +1 ; +for (i =0 ; i < n; i++) { +kref_get (&hr_nodes[i]->hot_range.refs); +hot_map_update ( + &hr_nodes[i]->hot_range.hot_freq_data, root); + +spin_lock (&hr_nodes[i]->hot_range.lock); + obsolete =hot_is_obsolete ( + &hr_nodes[i]->hot_range.hot_freq_data); +spin_unlock (&hr_nodes[i]->hot_range.lock); + +hot_range_item_put (hr_nodes[i]); +if (obsolete) +hot_range_item_put (hr_nodes[i]); + } + } +} +/* * Initialize inode and range map info. */
fs/hot_tracking.h +6 -0
@@ -25,6 +25,12 @@#define RANGE_SIZE (1 << RANGE_BITS)#define FREQ_POWER 4 +/* +* time to quit keeping track of +* tracking data (seconds) +*/ +#define TIME_TO_KICK 300 +/* NRR/NRW heat unit = 2^X accesses */ #define NRR_MULTIPLIER_POWER 20/* NRR - number of reads since mount */ #define NRR_COEFF_POWER 0
Signature · 1 line
1.7.6.5
PATCH 9/18 vfs: add one work queue
In reply to [email protected]From: Zhi Yong Wu <[email protected]>
Add a per-superblock workqueue and a delayed_work
to run periodic work to update map info on each superblock.
--- fs/hot_tracking.c | 85 ++++++++++++++++++++++++++++++++++++++++++ fs/hot_tracking.h | 3 + include/linux/hot_tracking.h | 3 + 3 files changed, 91 insertions(+), 0 deletions(-)
fs/hot_tracking.c +85 -0
@@ -15,9 +15,12 @@#include <linux/module.h> #include <linux/spinlock.h> #include <linux/hardirq.h> +#include <linux/kthread.h> +#include <linux/freezer.h> #include <linux/fs.h> #include <linux/blkdev.h> #include <linux/types.h> +#include <linux/list_sort.h> #include <linux/limits.h> #include "hot_tracking.h"
@@ -553,6 +556,67 @@ static void hot_map_exit(struct hot_info *root) } } +/* Temperature compare function*/ +static int hot_temp_cmp (void *priv,struct list_head *a, +struct list_head *b) +{ +struct hot_comm_item *ap = + container_of(a, struct hot_comm_item, n_list); +struct hot_comm_item *bp = + container_of(b, struct hot_comm_item, n_list); + +int diff = ap->hot_freq_data.last_temp + - bp->hot_freq_data.last_temp; +if (diff >0 ) +return -1 ; +if (diff <0 ) +return 1 ; +return 0 ; +} + +/* +* Every sync period we update temperatures for +* each hot inode item and hot range item for aging +* purposes. +*/ +static void hot_update_worker (struct work_struct *work) +{ +struct hot_info *root =container_of (to_delayed_work (work), + struct hot_info, update_work); +struct hot_inode_item *hi_nodes[8 ]; +unsigned long ino =0 ; +int i, n; + +while (1 ) { + n =radix_tree_gang_lookup (&root->hot_inode_tree, + (void **)hi_nodes, ino, +ARRAY_SIZE (hi_nodes)); +if (!n) +break ; + + ino = hi_nodes[n -1 ]->i_ino +1 ; +for (i =0 ; i < n; i++) { +kref_get (&hi_nodes[i]->hot_inode.refs); +hot_map_update ( + &hi_nodes[i]->hot_inode.hot_freq_data, root); +hot_range_update (hi_nodes[i], root); +hot_inode_item_put (hi_nodes[i]); + } + } + +/* Sort temperature map info */ +for (i =0 ; i <HEAT_MAP_SIZE ; i++) { +list_sort (NULL , &root->heat_inode_map[i].node_list, + hot_temp_cmp); +list_sort (NULL , &root->heat_range_map[i].node_list, + hot_temp_cmp); + } + +/* Instert next delayed work */ +queue_delayed_work (root->update_wq, &root->update_work, +msecs_to_jiffies (HEAT_UPDATE_DELAY *MSEC_PER_SEC )); +} +/* * Initialize kmem cache for hot_inode_item and hot_range_item. */
@@ -647,9 +711,28 @@ int hot_track_init(struct super_block *sb)hot_inode_tree_init (root);hot_map_init (root); + root->update_wq =alloc_workqueue ( +"hot_update_wq" ,WQ_NON_REENTRANT ,0 ); +if (!root->update_wq) { +printk (KERN_ERR "%s: Failed to create " +"hot update workqueue\n" , __func__); + goto failed_wq; + } + +/* Initialize hot tracking wq and arm one delayed work */ +INIT_DELAYED_WORK (&root->update_work, hot_update_worker); +queue_delayed_work (root->update_wq, &root->update_work, +msecs_to_jiffies (HEAT_UPDATE_DELAY *MSEC_PER_SEC )); +printk (KERN_INFO "VFS: Turning on hot data tracking\n" );return 0 ; + +failed_wq: +hot_map_exit (root); +hot_inode_tree_exit (root); +kfree (root); +return ret; }EXPORT_SYMBOL_GPL (hot_track_init);
@@ -657,6 +740,8 @@ void hot_track_exit(struct super_block *sb) {struct hot_info *root = sb->s_hot_root; +cancel_delayed_work_sync (&root->update_work); +destroy_workqueue (root->update_wq);hot_map_exit (root);hot_inode_tree_exit (root);kfree (root);
fs/hot_tracking.h +3 -0
@@ -31,6 +31,9 @@*/ #define TIME_TO_KICK 300 +/* set how often to update temperatures (seconds) */ +#define HEAT_UPDATE_DELAY 300 +/* NRR/NRW heat unit = 2^X accesses */ #define NRR_MULTIPLIER_POWER 20/* NRR - number of reads since mount */ #define NRR_COEFF_POWER 0
include/linux/hot_tracking.h +3 -0
@@ -82,6 +82,9 @@ struct hot_info {/* map of range temperature */ struct hot_map_head heat_range_map[HEAT_MAP_SIZE ];unsigned int hot_map_nr; + +struct workqueue_struct *update_wq; +struct delayed_work update_work; };extern void __init hot_cache_init (void );
Signature · 1 line
1.7.6.5
PATCH 10/18 vfs: add FS hot type support
In reply to [email protected]From: Zhi Yong Wu <[email protected]>
Introduce one way to enable that specific FS
can inject its own hot tracking type.
--- fs/hot_tracking.c | 43 +++++++++++++++++++++++++++++++---------- fs/hot_tracking.h | 1 - include/linux/fs.h | 1 + include/linux/hot_tracking.h | 19 ++++++++++++++++++ 4 files changed, 52 insertions(+), 12 deletions(-)
fs/hot_tracking.c +32 -11
@@ -64,8 +64,11 @@ void hot_range_tree_init(struct hot_inode_item *he)static void hot_range_item_init (struct hot_range_item *hr,loff_t start,struct hot_inode_item *he) { +struct hot_info *root =container_of (he->hot_inode_tree, + struct hot_info, hot_inode_tree); + hr->start = start; - hr->len =RANGE_SIZE ; + hr->len =hot_raw_shift (1 , root->hot_type->range_bits, true); hr->hot_inode = he;kref_init (&hr->hot_range.refs);spin_lock_init (&hr->hot_range.lock);
@@ -309,19 +312,21 @@ static void hot_rw_freq_calc(struct timespec old_atime, *avg = *avg >>FREQ_POWER ; } -static void hot_freq_data_update (struct hot_freq_data *freq_data,bool write) +static void hot_freq_data_update (struct hot_info *root, +struct hot_freq_data *freq_data,bool write) {struct timespec cur_time =current_kernel_time ();if (write) { freq_data->nr_writes +=1 ; -hot_rw_freq_calc (freq_data->last_write_time, + root->hot_type->ops.hot_rw_freq_calc_fn ( + freq_data->last_write_time, cur_time, &freq_data->avg_delta_writes); freq_data->last_write_time = cur_time; }else { freq_data->nr_reads +=1 ; -hot_rw_freq_calc (freq_data->last_read_time, + root->hot_type->ops.hot_rw_freq_calc_fn ( freq_data->last_read_time, cur_time, &freq_data->avg_delta_reads);
@@ -425,7 +430,7 @@ static void hot_map_update(struct hot_freq_data *freq_data,struct hot_comm_item *comm_item;struct hot_inode_item *he;struct hot_range_item *hr; -u32 temp =hot_temp_calc (freq_data); +u32 temp = root->hot_type->ops.hot_temp_calc_fn (freq_data);u8 a_temp = (u8 )hot_raw_shift ((u64 )temp, (32 -HEAT_MAP_BITS ), false);u8 b_temp = (u8 )hot_raw_shift ((u64 )freq_data->last_temp, (32 -HEAT_MAP_BITS ), false);
@@ -507,7 +512,7 @@ static void hot_range_update(struct hot_inode_item *he, &hr_nodes[i]->hot_range.hot_freq_data, root);spin_lock (&hr_nodes[i]->hot_range.lock); - obsolete =hot_is_obsolete ( + obsolete = root->hot_type->ops.hot_is_obsolete_fn ( &hr_nodes[i]->hot_range.hot_freq_data);spin_unlock (&hr_nodes[i]->hot_range.lock);
@@ -652,6 +657,7 @@ void hot_update_freqs(struct inode *inode, loff_t start,struct hot_info *root = inode->i_sb->s_hot_root;struct hot_inode_item *he;struct hot_range_item *hr; +u64 range_size;loff_t cur, end;if (!root || (len ==0 ))
@@ -664,15 +670,19 @@ void hot_update_freqs(struct inode *inode, loff_t start, }spin_lock (&he->hot_inode.lock); -hot_freq_data_update (&he->hot_inode.hot_freq_data, rw); +hot_freq_data_update (root, &he->hot_inode.hot_freq_data, rw);spin_unlock (&he->hot_inode.lock);/* -* Align ranges on RANGE_SIZE boundary +* Align ranges on range size boundary * to prevent proliferation of range structs */ - end = (start + len +RANGE_SIZE -1 ) >>RANGE_BITS ; -for (cur = (start >>RANGE_BITS ); cur < end; cur++) { + range_size =hot_raw_shift (1 , + root->hot_type->range_bits, true); + end =hot_raw_shift ((start + len + range_size -1 ), + root->hot_type->range_bits, false); + cur =hot_raw_shift (start, root->hot_type->range_bits, false); +for (; cur < end; cur++) { hr =hot_range_item_lookup (he, cur);if (IS_ERR (hr)) { WARN(1, "hot_range_item_lookup returns %ld\n",
@@ -682,7 +692,7 @@ void hot_update_freqs(struct inode *inode, loff_t start, }spin_lock (&hr->hot_range.lock); -hot_freq_data_update (&hr->hot_range.hot_freq_data, rw); +hot_freq_data_update (root, &hr->hot_range.hot_freq_data, rw);spin_unlock (&hr->hot_range.lock);hot_range_item_put (hr);
@@ -711,6 +721,17 @@ int hot_track_init(struct super_block *sb)hot_inode_tree_init (root);hot_map_init (root); +/* Get hot type for specific FS */ + root->hot_type = &sb->s_type->hot_type; +if (!root->hot_type->ops.hot_rw_freq_calc_fn) + root->hot_type->ops.hot_rw_freq_calc_fn = hot_rw_freq_calc; +if (!root->hot_type->ops.hot_temp_calc_fn) + root->hot_type->ops.hot_temp_calc_fn = hot_temp_calc; +if (!root->hot_type->ops.hot_is_obsolete_fn) + root->hot_type->ops.hot_is_obsolete_fn = hot_is_obsolete; +if (root->hot_type->range_bits ==0 ) + root->hot_type->range_bits =RANGE_BITS ; + root->update_wq =alloc_workqueue ("hot_update_wq" ,WQ_NON_REENTRANT ,0 );if (!root->update_wq) {
fs/hot_tracking.h +0 -1
@@ -22,7 +22,6 @@/* size of sub-file ranges */ #define RANGE_BITS 20 -#define RANGE_SIZE (1 << RANGE_BITS)#define FREQ_POWER 4/*
include/linux/fs.h +1 -0
@@ -1821,6 +1821,7 @@ struct file_system_type {struct dentry *(*mount) (struct file_system_type *,int ,const char *,void *);void (*kill_sb) (struct super_block *); +struct hot_type hot_type;struct module *owner;struct file_system_type * next;struct hlist_head fs_supers;
include/linux/hot_tracking.h +19 -0
@@ -73,6 +73,24 @@ struct hot_range_item {size_t len;/* length in bytes */ }; +typedef void (hot_rw_freq_calc_fn ) (struct timespec old_atime, +struct timespec cur_time,u64 *avg); +typedef u32 (hot_temp_calc_fn ) (struct hot_freq_data *freq_data); +typedef bool (hot_is_obsolete_fn ) (struct hot_freq_data *freq_data); + +struct hot_func_ops { +hot_rw_freq_calc_fn *hot_rw_freq_calc_fn; +hot_temp_calc_fn *hot_temp_calc_fn; +hot_is_obsolete_fn *hot_is_obsolete_fn; +}; + +/* identifies an hot type */ +struct hot_type { +u64 range_bits; +/* fields provided by specific FS */ +struct hot_func_ops ops; +}; +struct hot_info {struct radix_tree_root hot_inode_tree;spinlock_t lock;/*protect inode tree */
@@ -85,6 +103,7 @@ struct hot_info {struct workqueue_struct *update_wq;struct delayed_work update_work; +struct hot_type *hot_type; };extern void __init hot_cache_init (void );
Signature · 1 line
1.7.6.5
PATCH 11/18 vfs: register one shrinker
In reply to [email protected]From: Zhi Yong Wu <[email protected]>
Register a shrinker to control the amount of
memory that is used in tracking hot regions - if we are throwing inodes out of memory due to memory pressure, we most definitely are going to need to reduce the amount of memory the tracking code is using, even if it means losing useful information (i.e. the shrinker accelerates the aging process).
--- fs/hot_tracking.c | 61 ++++++++++++++++++++++++++++++++++++++++++ include/linux/hot_tracking.h | 1 + 2 files changed, 62 insertions(+), 0 deletions(-)
fs/hot_tracking.c +61 -0
@@ -648,6 +648,61 @@ err: }EXPORT_SYMBOL_GPL (hot_cache_init); +static int hot_track_prune_map (struct hot_map_head *map_head, +bool type,int nr) +{ +struct hot_comm_item *node; +int i; + +for (i =0 ; i <HEAT_MAP_SIZE ; i++) { +while (!list_empty (&(map_head + i)->node_list)) { +if (nr-- <=0 ) +break ; + + node =list_first_entry (&(map_head + i)->node_list, + struct hot_comm_item, n_list); +if (type) { +struct hot_inode_item *hot_inode = +container_of (node, + struct hot_inode_item, hot_inode); +hot_inode_item_put (hot_inode); + }else { +struct hot_range_item *hot_range = +container_of (node, + struct hot_range_item, hot_range); +hot_range_item_put (hot_range); + } + } + } + +return nr; +} + +/* The shrinker callback function */ +static int hot_track_prune (struct shrinker *shrink, +struct shrink_control *sc) +{ +struct hot_info *root = + container_of(shrink, struct hot_info, hot_shrink); +int ret; + +if (sc->nr_to_scan ==0 ) +return root->hot_map_nr; + +if (!(sc->gfp_mask & __GFP_FS)) +return -1 ; + + ret =hot_track_prune_map (root->heat_range_map, + false, sc->nr_to_scan); +if (ret >0 ) + ret =hot_track_prune_map (root->heat_inode_map, + true, ret); +if (ret >0 ) + root->hot_map_nr -= (sc->nr_to_scan - ret); + +return root->hot_map_nr; +} +/* * Main function to update access frequency from read/writepage(s) hooks */
@@ -745,6 +800,11 @@ int hot_track_init(struct super_block *sb)queue_delayed_work (root->update_wq, &root->update_work,msecs_to_jiffies (HEAT_UPDATE_DELAY *MSEC_PER_SEC )); +/* Register a shrinker callback */ + root->hot_shrink.shrink = hot_track_prune; + root->hot_shrink.seeks =DEFAULT_SEEKS ; +register_shrinker (&root->hot_shrink); +printk (KERN_INFO "VFS: Turning on hot data tracking\n" );return 0 ;
@@ -761,6 +821,7 @@ void hot_track_exit(struct super_block *sb) {struct hot_info *root = sb->s_hot_root; +unregister_shrinker (&root->hot_shrink);cancel_delayed_work_sync (&root->update_work);destroy_workqueue (root->update_wq);hot_map_exit (root);
include/linux/hot_tracking.h +1 -0
@@ -104,6 +104,7 @@ struct hot_info {struct workqueue_struct *update_wq;struct delayed_work update_work;struct hot_type *hot_type; +struct shrinker hot_shrink; };extern void __init hot_cache_init (void );
Signature · 1 line
1.7.6.5
PATCH 12/18 vfs: add one ioctl interface
In reply to [email protected]From: Zhi Yong Wu <[email protected]>
FS_IOC_GET_HEAT_INFO: return a struct containing the various
metrics collected in hot_freq_data structs, and also return a calculated data temperature based on those metrics. Optionally, retrieve the temperature from the hot data hash list instead of recalculating it.
--- fs/compat_ioctl.c | 5 +++ fs/ioctl.c | 74 ++++++++++++++++++++++++++++++++++++++++++ include/linux/hot_tracking.h | 19 +++++++++++ 3 files changed, 98 insertions(+), 0 deletions(-)
fs/compat_ioctl.c +5 -0
@@ -57,6 +57,7 @@#include <linux/i2c-dev.h> #include <linux/atalk.h> #include <linux/gfp.h> +#include <linux/hot_tracking.h> #include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci.h>
@@ -1400,6 +1401,9 @@ COMPATIBLE_IOCTL(TIOCSTART) COMPATIBLE_IOCTL(TIOCSTOP)#endif +/*Hot data tracking*/ +COMPATIBLE_IOCTL (FS_IOC_GET_HEAT_INFO ) +/* fat 'r' ioctls. These are handled by fat with ->compat_ioctl, but we don't want warnings on other file systems. So declare them as compatible here. */
@@ -1579,6 +1583,7 @@ asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd,case FIBMAP :case FIGETBSZ :case FIONREAD : +case FS_IOC_GET_HEAT_INFO :if (S_ISREG (f.file->f_path.dentry->d_inode->i_mode))break ;/*FALL THROUGH*/
fs/ioctl.c +74 -0
92 lines not on this page · raw message
include/linux/hot_tracking.h +19 -0
@@ -39,6 +39,17 @@ struct hot_freq_data {u32 last_temp; }; +struct hot_heat_info { +__u64 avg_delta_reads; +__u64 avg_delta_writes; +__u64 last_read_time; +__u64 last_write_time; +__u32 num_reads; +__u32 num_writes; +__u32 temp; +__u8 live; +}; +/* List heads in hot map array */ struct hot_map_head {struct list_head node_list;
@@ -107,6 +118,14 @@ struct hot_info {struct shrinker hot_shrink; }; +/* +* Hot data tracking ioctls: +* +* HOT_INFO - retrieve info on frequency of access +*/ +#define FS_IOC_GET_HEAT_INFO _IOR('f', 17, \ + struct hot_heat_info) +extern void __init hot_cache_init (void );extern int hot_track_init (struct super_block *sb);extern void hot_track_exit (struct super_block *sb);
Signature · 1 line
1.7.6.5
PATCH 13/18 vfs: add debugfs support
In reply to [email protected]From: Zhi Yong Wu <[email protected]>
Add a /sys/kernel/debug/hot_track/<device_name>/ directory for each
volume that contains two files. The first, inode_stats', contains the heat information for inodes that have been brought into the hot data map structures. The second, range_stats', contains similar information for subfile ranges.
--- fs/hot_tracking.c | 484 ++++++++++++++++++++++++++++++++++++++++++ fs/hot_tracking.h | 5 + include/linux/hot_tracking.h | 1 + 3 files changed, 490 insertions(+), 0 deletions(-)
fs/hot_tracking.c +484 -0
@@ -21,9 +21,12 @@#include <linux/blkdev.h> #include <linux/types.h> #include <linux/list_sort.h> +#include <linux/debugfs.h> #include <linux/limits.h> #include "hot_tracking.h" +static struct dentry *hot_debugfs_root; +/* kmem_cache pointers for slab caches */ static struct kmem_cache *hot_inode_item_cachep__read_mostly ;static struct kmem_cache *hot_range_item_cachep__read_mostly ;
@@ -623,6 +626,475 @@ static void hot_update_worker(struct work_struct *work) }/* +* take the inode, find ranges associated with inode +* and print each range data struct +*/ +static struct hot_range_item +*hot_range_tree_walk (struct hot_inode_item *he, +loff_t *pos,loff_t start,bool flag) +{ +struct hot_range_item *hr_nodes[8 ]; +loff_t l = *pos; +int i, n; + +/* Walk the hot_range_tree for inode */ +while (1 ) { +spin_lock (&he->lock); + n =radix_tree_gang_lookup (&he->hot_range_tree, + (void **)hr_nodes, start, +ARRAY_SIZE (hr_nodes)); +if (!n) { +spin_unlock (&he->lock); +break ; + } +spin_unlock (&he->lock); + + start = hr_nodes[n -1 ]->start +1 ; +for (i =0 ; i < n; i++) { +if ((!flag && !l--) || (flag)) { +if (flag) + (*pos)++; +kref_get (&hr_nodes[i]->hot_range.refs); +return hr_nodes[i]; + } + } + } + +return NULL ; +} + +static void +*hot_inode_tree_walk (struct seq_file *seq,loff_t *pos, +unsigned long ino,bool type,bool flag) +{ +struct hot_info *root = seq->private; +struct hot_inode_item *hi_nodes[8 ]; +struct hot_range_item *hr; +loff_t l = *pos; +int i, n; + +while (1 ) { +spin_lock (&root->lock); + n =radix_tree_gang_lookup (&root->hot_inode_tree, + (void **)hi_nodes, ino, +ARRAY_SIZE (hi_nodes)); +if (!n) { +spin_unlock (&root->lock); +break ; + } +spin_unlock (&root->lock); + + ino = hi_nodes[n -1 ]->i_ino +1 ; +for (i =0 ; i < n; i++) { +if (!type) { + hr =hot_range_tree_walk (hi_nodes[i], + pos,0 , flag); +if (hr) +return hr; + }else { +if ((!flag && !l--) || (flag)) { +if (flag) + (*pos)++; +kref_get (&hi_nodes[i]->hot_inode.refs); +return hi_nodes[i]; + } + } + } + } + +return NULL ; +} + +static void *hot_range_seq_start (struct seq_file *seq,loff_t *pos) +{ +return hot_inode_tree_walk (seq, pos,0 , false, false); +} + +static void *hot_range_seq_next (struct seq_file *seq, +void *v,loff_t *pos) +{ +struct hot_range_item *hr_next, *hr = v; +loff_t start = hr->start +1 ; + +/* Walk the hot_range_tree for inode */ + hr_next =hot_range_tree_walk (hr->hot_inode, pos, start, true); +if (hr_next) +return hr_next; + +return hot_inode_tree_walk (seq, pos, + hr->hot_inode->i_ino +1 , false, true); +} + +static void hot_range_seq_stop (struct seq_file *seq,void *v) +{ +struct hot_range_item *hr = v; + +if (hr) +hot_range_item_put (hr); +} + +static int hot_range_seq_show (struct seq_file *seq,void *v) +{ +struct hot_range_item *hr = v; +struct hot_inode_item *he = hr->hot_inode; +struct hot_freq_data *freq_data = &hr->hot_range.hot_freq_data; +struct hot_info *root =container_of (he->hot_inode_tree, + struct hot_info, hot_inode_tree); +loff_t start = hr->start *hot_raw_shift (1 , + root->hot_type->range_bits, true); + +/* Always lock hot_inode_item first */ +spin_lock (&he->hot_inode.lock); +spin_lock (&hr->hot_range.lock); +seq_printf (seq,"inode %lu, range start " \ +"%llu (range len %llu) reads %u, writes %u, " +"avg read time %llu, avg write time %llu, temp %u\n" , + he->i_ino, (unsigned long long )start, + (unsigned long long )hr->len, + freq_data->nr_reads, + freq_data->nr_writes, + freq_data->avg_delta_reads /NSEC_PER_MSEC , + freq_data->avg_delta_writes /NSEC_PER_MSEC , + (u8 )hot_raw_shift ((u64 )freq_data->last_temp, + (32 -HEAT_MAP_BITS ), false)); +spin_unlock (&hr->hot_range.lock); +spin_unlock (&he->hot_inode.lock); + +return 0 ; +} + +static void *hot_inode_seq_start (struct seq_file *seq,loff_t *pos) +{ +return hot_inode_tree_walk (seq, pos,0 , true, false); +} + +static void *hot_inode_seq_next (struct seq_file *seq,void *v,loff_t *pos) +{ +struct hot_inode_item *he = v; +unsigned long ino = he->i_ino +1 ; + +hot_inode_item_put (he); + +return hot_inode_tree_walk (seq, pos, ino, true, true); +} + +static void hot_inode_seq_stop (struct seq_file *seq,void *v) +{ +struct hot_inode_item *he = v; + +if (he) +hot_inode_item_put (he); +} + +static int hot_inode_seq_show (struct seq_file *seq,void *v) +{ +struct hot_inode_item *he = v; +struct hot_freq_data *freq_data = &he->hot_inode.hot_freq_data; + +spin_lock (&he->hot_inode.lock); +seq_printf (seq,"inode %lu, reads %u, writes %u, " \ +"avg read time %llu, avg write time %llu, temp %d\n" , + he->i_ino, + freq_data->nr_reads, + freq_data->nr_writes, + freq_data->avg_delta_reads /NSEC_PER_MSEC , + freq_data->avg_delta_writes /NSEC_PER_MSEC , + (u8 )hot_raw_shift ((u64 )freq_data->last_temp, + (32 -HEAT_MAP_BITS ), false)); +spin_unlock (&he->hot_inode.lock); + +return 0 ; +} + +static void *hot_spot_range_seq_start (struct seq_file *seq,loff_t *pos) +{ +struct hot_info *root = seq->private; +struct hot_range_item *hr; +struct hot_comm_item *comm_item; +struct list_head *n_list; +int i; + +for (i =HEAT_MAP_SIZE -1 ; i >=0 ; i--) { + n_list =seq_list_start ( + &root->heat_range_map[i].node_list, *pos); +if (n_list) { + comm_item =container_of (n_list, + struct hot_comm_item, n_list); + hr =container_of (comm_item, + struct hot_range_item, hot_range); +kref_get (&hr->hot_range.refs); +return hr; + } + } + +return NULL ; +} + +static void *hot_spot_range_seq_next (struct seq_file *seq,void *v,loff_t *pos) +{ +struct hot_info *root = seq->private; +struct hot_range_item *hr_next, *hr = v; +struct hot_comm_item *comm_item; +struct list_head *n_list; +int i = (int )hot_raw_shift (hr->hot_range.hot_freq_data.last_temp, + (32 -HEAT_MAP_BITS ), false); + + n_list =seq_list_next (&hr->hot_range.n_list, + &root->heat_range_map[i].node_list, pos); +hot_range_item_put (hr); +next: +if (n_list) { + comm_item =container_of (n_list, + struct hot_comm_item, n_list); + hr_next =container_of (comm_item, + struct hot_range_item, hot_range); +kref_get (&hr_next->hot_range.refs); +return hr_next; + }else if (--i >=0 ) { + n_list =seq_list_next (&root->heat_range_map[i].node_list, + &root->heat_range_map[i].node_list, pos); + goto next; + } + +return NULL ; +} + +static void *hot_spot_inode_seq_start (struct seq_file *seq,loff_t *pos) +{ +struct hot_info *root = seq->private; +struct hot_inode_item *he; +struct hot_comm_item *comm_item; +struct list_head *n_list; +int i; + +for (i =HEAT_MAP_SIZE -1 ; i >=0 ; i--) { + n_list =seq_list_start ( + &root->heat_inode_map[i].node_list, *pos); +if (n_list) { + comm_item =container_of (n_list, + struct hot_comm_item, n_list); + he =container_of (comm_item, + struct hot_inode_item, hot_inode); +kref_get (&he->hot_inode.refs); +return he; + } + } + +return NULL ; +} + +static void *hot_spot_inode_seq_next (struct seq_file *seq,void *v,loff_t *pos) +{ +struct hot_info *root = seq->private; +struct hot_inode_item *he_next, *he = v; +struct hot_comm_item *comm_item; +struct list_head *n_list; +int i = (int )hot_raw_shift (he->hot_inode.hot_freq_data.last_temp, + (32 -HEAT_MAP_BITS ), false); + + n_list =seq_list_next (&he->hot_inode.n_list, + &root->heat_inode_map[i].node_list, pos); +hot_inode_item_put (he); +next: +if (n_list) { + comm_item =container_of (n_list, + struct hot_comm_item, n_list); + he_next =container_of (comm_item, + struct hot_inode_item, hot_inode); +kref_get (&he_next->hot_inode.refs); +return he_next; + }else if (--i >=0 ) { + n_list =seq_list_next (&root->heat_inode_map[i].node_list, + &root->heat_inode_map[i].node_list, pos); + goto next; + } + +return NULL ; +} + +static const struct seq_operations hot_range_seq_ops = { + .start = hot_range_seq_start, + .next = hot_range_seq_next, + .stop = hot_range_seq_stop, + .show = hot_range_seq_show +}; + +static const struct seq_operations hot_inode_seq_ops = { + .start = hot_inode_seq_start, + .next = hot_inode_seq_next, + .stop = hot_inode_seq_stop, + .show = hot_inode_seq_show +}; + +static const struct seq_operations hot_spot_range_seq_ops = { + .start = hot_spot_range_seq_start, + .next = hot_spot_range_seq_next, + .stop = hot_range_seq_stop, + .show = hot_range_seq_show +}; + +static const struct seq_operations hot_spot_inode_seq_ops = { + .start = hot_spot_inode_seq_start, + .next = hot_spot_inode_seq_next, + .stop = hot_inode_seq_stop, + .show = hot_inode_seq_show +}; + +static int hot_range_seq_open (struct inode *inode,struct file *file) +{ +int ret =seq_open_private (file, &hot_range_seq_ops,0 ); +if (ret ==0 ) { +struct seq_file *seq = file->private_data; + seq->private = inode->i_private; + } +return ret; +} + +static int hot_inode_seq_open (struct inode *inode,struct file *file) +{ +int ret =seq_open_private (file, &hot_inode_seq_ops,0 ); +if (ret ==0 ) { +struct seq_file *seq = file->private_data; + seq->private = inode->i_private; + } +return ret; +} + +static int hot_spot_range_seq_open (struct inode *inode,struct file *file) +{ +int ret =seq_open_private (file, &hot_spot_range_seq_ops,0 ); +if (ret ==0 ) { +struct seq_file *seq = file->private_data; + seq->private = inode->i_private; + } +return ret; +} + +static int hot_spot_inode_seq_open (struct inode *inode,struct file *file) +{ +int ret =seq_open_private (file, &hot_spot_inode_seq_ops,0 ); +if (ret ==0 ) { +struct seq_file *seq = file->private_data; + seq->private = inode->i_private; + } +return ret; +} + +/* fops to override for printing range data */ +static const struct file_operations hot_debugfs_range_fops = { + .open = hot_range_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +/* fops to override for printing inode data */ +static const struct file_operations hot_debugfs_inode_fops = { + .open = hot_inode_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +/* fops to override for printing temperature data */ +static const struct file_operations hot_debugfs_spot_range_fops = { + .open = hot_spot_range_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static const struct file_operations hot_debugfs_spot_inode_fops = { + .open = hot_spot_inode_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static const struct hot_debugfs hot_debugfs[] = { + { + .name ="rt_stats_range" , + .fops = &hot_debugfs_range_fops, + }, + { + .name ="rt_stats_inode" , + .fops = &hot_debugfs_inode_fops, + }, + { + .name ="hot_spots_range" , + .fops = &hot_debugfs_spot_range_fops, + }, + { + .name ="hot_spots_inode" , + .fops = &hot_debugfs_spot_inode_fops, + }, +}; + +/* initialize debugfs */ +static int hot_debugfs_init (struct super_block *sb) +{ +static const char hot_name[] ="hot_track" ; +struct dentry *dentry; +int i, ret =0 ; + +/* Determine if hot debufs root has existed */ +if (!hot_debugfs_root) { + hot_debugfs_root =debugfs_create_dir (hot_name,NULL ); +if (IS_ERR (hot_debugfs_root)) { + ret =PTR_ERR (hot_debugfs_root); +return ret; + } + } + +if (!S_ISDIR (hot_debugfs_root->d_inode->i_mode)) +return -ENOTDIR ; + +/* create debugfs folder for this volume by mounted dev name */ + sb->s_hot_root->vol_dentry = +debugfs_create_dir (sb->s_id, hot_debugfs_root); +if (IS_ERR (sb->s_hot_root->vol_dentry)) { + ret =PTR_ERR (sb->s_hot_root->vol_dentry); + goto err; + } + +/* create debugfs hot data files */ +for (i =0 ; i <ARRAY_SIZE (hot_debugfs); i++) { + dentry =debugfs_create_file (hot_debugfs[i].name, +S_IFREG |S_IRUSR |S_IWUSR , + sb->s_hot_root->vol_dentry, + sb->s_hot_root, + hot_debugfs[i].fops); +if (IS_ERR (dentry)) { + ret =PTR_ERR (dentry); + goto err; + } + } + +return 0 ; + +err: +debugfs_remove_recursive (sb->s_hot_root->vol_dentry); + +if (list_empty (&hot_debugfs_root->d_subdirs)) +debugfs_remove (hot_debugfs_root); + +return ret; +} + +/* remove dentries for debugsfs */ +static void hot_debugfs_exit (struct super_block *sb) +{ +/* remove all debugfs entries recursively from the volume root */ +if (sb->s_hot_root->vol_dentry) +debugfs_remove_recursive (sb->s_hot_root->vol_dentry); +else +BUG (); + +if (list_empty (&hot_debugfs_root->d_subdirs)) +debugfs_remove (hot_debugfs_root); +} + +/* * Initialize kmem cache for hot_inode_item and hot_range_item. */ void __init hot_cache_init (void )
@@ -805,10 +1277,21 @@ int hot_track_init(struct super_block *sb) root->hot_shrink.seeks =DEFAULT_SEEKS ;register_shrinker (&root->hot_shrink); + ret =hot_debugfs_init (sb); +if (ret) { +printk (KERN_ERR "%s: hot_debugfs_init error: %d\n" , + __func__, ret); + goto failed_debugfs; + } +printk (KERN_INFO "VFS: Turning on hot data tracking\n" );return 0 ; +failed_debugfs: +unregister_shrinker (&root->hot_shrink); +cancel_delayed_work_sync (&root->update_work); +destroy_workqueue (root->update_wq); failed_wq:hot_map_exit (root);hot_inode_tree_exit (root);
@@ -826,6 +1309,7 @@ void hot_track_exit(struct super_block *sb)destroy_workqueue (root->update_wq);hot_map_exit (root);hot_inode_tree_exit (root); +hot_debugfs_exit (sb);kfree (root); }EXPORT_SYMBOL_GPL (hot_track_exit);
fs/hot_tracking.h +5 -0
@@ -54,4 +54,9 @@#define AVW_DIVIDER_POWER 40/* AVW - average delta between recent writes(ns) */ #define AVW_COEFF_POWER 0 +struct hot_debugfs { +const char *name; +const struct file_operations *fops; +}; +#endif /* __HOT_TRACKING__ */
include/linux/hot_tracking.h +1 -0
@@ -116,6 +116,7 @@ struct hot_info {struct delayed_work update_work;struct hot_type *hot_type;struct shrinker hot_shrink; +struct dentry *vol_dentry; };/*
Signature · 1 line
1.7.6.5
PATCH 14/18 procfs: add two hot_track proc files
In reply to [email protected]From: Zhi Yong Wu <[email protected]>
Add two proc files hot-kick-time and hot-update-delay
under the dir /proc/sys/fs/ in order to turn TIME_TO_KICK and HEAT_UPDATE_DELAY into be tunable.
--- fs/hot_tracking.c | 12 +++++++++--- fs/hot_tracking.h | 9 --------- include/linux/hot_tracking.h | 7 +++++++ kernel/sysctl.c | 14 ++++++++++++++ 4 files changed, 30 insertions(+), 12 deletions(-)
fs/hot_tracking.c +9 -3
@@ -27,6 +27,12 @@static struct dentry *hot_debugfs_root; +int sysctl_hot_kick_time__read_mostly =300 ; +EXPORT_SYMBOL_GPL (sysctl_hot_kick_time); + +int sysctl_hot_update_delay__read_mostly =300 ; +EXPORT_SYMBOL_GPL (sysctl_hot_update_delay); +/* kmem_cache pointers for slab caches */ static struct kmem_cache *hot_inode_item_cachep__read_mostly ;static struct kmem_cache *hot_range_item_cachep__read_mostly ;
@@ -413,7 +419,7 @@ static bool hot_is_obsolete(struct hot_freq_data *freq_data) (cur_time -timespec_to_ns (&freq_data->last_read_time));u64 last_write_ns = (cur_time -timespec_to_ns (&freq_data->last_write_time)); -u64 kick_ns =TIME_TO_KICK *NSEC_PER_SEC ; +u64 kick_ns = sysctl_hot_kick_time *NSEC_PER_SEC ;if ((last_read_ns > kick_ns) && (last_write_ns > kick_ns)) ret =1 ;
@@ -622,7 +628,7 @@ static void hot_update_worker(struct work_struct *work)/* Instert next delayed work */ queue_delayed_work (root->update_wq, &root->update_work, -msecs_to_jiffies (HEAT_UPDATE_DELAY *MSEC_PER_SEC )); +msecs_to_jiffies (sysctl_hot_update_delay *MSEC_PER_SEC )); }/*
@@ -1270,7 +1276,7 @@ int hot_track_init(struct super_block *sb)/* Initialize hot tracking wq and arm one delayed work */ INIT_DELAYED_WORK (&root->update_work, hot_update_worker);queue_delayed_work (root->update_wq, &root->update_work, -msecs_to_jiffies (HEAT_UPDATE_DELAY *MSEC_PER_SEC )); +msecs_to_jiffies (sysctl_hot_update_delay *MSEC_PER_SEC ));/* Register a shrinker callback */ root->hot_shrink.shrink = hot_track_prune;
fs/hot_tracking.h +0 -9
@@ -24,15 +24,6 @@#define RANGE_BITS 20#define FREQ_POWER 4 -/* -* time to quit keeping track of -* tracking data (seconds) -*/ -#define TIME_TO_KICK 300 - -/* set how often to update temperatures (seconds) */ -#define HEAT_UPDATE_DELAY 300 -/* NRR/NRW heat unit = 2^X accesses */ #define NRR_MULTIPLIER_POWER 20/* NRR - number of reads since mount */ #define NRR_COEFF_POWER 0
include/linux/hot_tracking.h +7 -0
@@ -120,6 +120,13 @@ struct hot_info { };/* +* Two variables have meanings as below: +* 1. time to quit keeping track of tracking data (seconds) +* 2. set how often to update temperatures (seconds) +*/ +extern int sysctl_hot_kick_time, sysctl_hot_update_delay; + +/* * Hot data tracking ioctls: * * HOT_INFO - retrieve info on frequency of access
kernel/sysctl.c +14 -0
@@ -1545,6 +1545,20 @@ static struct ctl_table fs_table[] = { .proc_handler = &pipe_proc_fn, .extra1 = &pipe_min_size, }, + { + .procname = "hot-kick-time", + .data = &sysctl_hot_kick_time, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, + { + .procname = "hot-update-delay", + .data = &sysctl_hot_update_delay, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, { } };
Signature · 1 line
1.7.6.5
PATCH 15/18 btrfs: add hot tracking support
In reply to [email protected]From: Zhi Yong Wu <[email protected]>
Introduce one new mount option '-o hot_track',
and add its parsing support.
Its usage looks like:
mount -o hot_track
mount -o nouser,hot_track
mount -o nouser,hot_track,loop
mount -o hot_track,nouser
--- fs/btrfs/ctree.h | 1 + fs/btrfs/super.c | 22 +++++++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletions(-)
fs/btrfs/ctree.h +1 -0
@@ -1756,6 +1756,7 @@ struct btrfs_ioctl_defrag_range_args {#define BTRFS_MOUNT_CHECK_INTEGRITY (1 << 20)#define BTRFS_MOUNT_CHECK_INTEGRITY_INCLUDING_EXTENT_DATA (1 << 21)#define BTRFS_MOUNT_PANIC_ON_FATAL_ERROR (1 << 22) +#define BTRFS_MOUNT_HOT_TRACK (1 << 23)#define btrfs_clear_opt (o, opt) ((o) &= ~BTRFS_MOUNT_##opt)#define btrfs_set_opt (o, opt) ((o) |= BTRFS_MOUNT_##opt)
fs/btrfs/super.c +21 -1
@@ -41,6 +41,7 @@#include <linux/slab.h> #include <linux/cleancache.h> #include <linux/ratelimit.h> +#include <linux/hot_tracking.h> #include "compat.h" #include "delayed-inode.h" #include "ctree.h"
@@ -299,6 +300,10 @@ static void btrfs_put_super(struct super_block *sb)* last process that kept it busy. Or segfault in the aforementioned * process... Whom would you report that to? */ + +/* Hot data tracking */ +if (btrfs_test_opt (btrfs_sb (sb)->tree_root,HOT_TRACK )) +hot_track_exit (sb); }enum {
@@ -311,7 +316,7 @@ enum { Opt_enospc_debug, Opt_subvolrootid, Opt_defrag, Opt_inode_cache, Opt_no_space_cache, Opt_recovery, Opt_skip_balance, Opt_check_integrity, Opt_check_integrity_including_extent_data, - Opt_check_integrity_print_mask, Opt_fatal_errors, + Opt_check_integrity_print_mask, Opt_fatal_errors, Opt_hot_track, Opt_err, };
@@ -352,6 +357,7 @@ static match_table_t tokens = { {Opt_check_integrity_including_extent_data, "check_int_data"}, {Opt_check_integrity_print_mask, "check_int_print_mask=%d"}, {Opt_fatal_errors, "fatal_errors=%s"}, + {Opt_hot_track, "hot_track"}, {Opt_err, NULL}, };
@@ -614,6 +620,9 @@ int btrfs_parse_options(struct btrfs_root *root, char *options) goto out; }break ; +case Opt_hot_track: +btrfs_set_opt (info->mount_opt,HOT_TRACK ); +break ;case Opt_err:printk (KERN_INFO "btrfs: unrecognized mount option " "'%s'\n" , p);
@@ -841,11 +850,20 @@ static int btrfs_fill_super(struct super_block *sb, goto fail_close; } +if (btrfs_test_opt (fs_info->tree_root,HOT_TRACK )) { + err =hot_track_init (sb); +if (err) + goto fail_hot; + } +save_mount_options (sb, data);cleancache_init_fs (sb); sb->s_flags |=MS_ACTIVE ;return 0 ; +fail_hot: +dput (sb->s_root); + sb->s_root =NULL ; fail_close:close_ctree (fs_info->tree_root);return err;
@@ -941,6 +959,8 @@ static int btrfs_show_options(struct seq_file *seq, struct dentry *dentry)seq_puts (seq,",skip_balance" );if (btrfs_test_opt (root,PANIC_ON_FATAL_ERROR ))seq_puts (seq,",fatal_errors=panic" ); +if (btrfs_test_opt (root,HOT_TRACK )) +seq_puts (seq,",hot_track" );return 0 ; }
Signature · 1 line
1.7.6.5
PATCH 16/18 xfs: add hot tracking support
In reply to [email protected]From: Dave Chinner <[email protected]>
Connect up the VFS hot tracking support
so XFS filesystems can make use of it.
--- fs/xfs/xfs_mount.h | 1 + fs/xfs/xfs_super.c | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 0 deletions(-)
fs/xfs/xfs_mount.h +1 -0
@@ -217,6 +217,7 @@ typedef struct xfs_mount {#define XFS_MOUNT_WSYNC (1ULL << 0)/* for nfs - all metadata ops must be synchronous except for space allocations */ +#define XFS_MOUNT_HOTTRACK (1ULL << 1)/* hot inode tracking */ #define XFS_MOUNT_WAS_CLEAN (1ULL << 3)#define XFS_MOUNT_FS_SHUTDOWN (1ULL << 4)/* atomic stop of all filesystem operations, typically for
fs/xfs/xfs_super.c +16 -0
@@ -61,6 +61,7 @@#include <linux/kthread.h> #include <linux/freezer.h> #include <linux/parser.h> +#include <linux/hot_tracking.h> static const struct super_operations xfs_super_operations;static kmem_zone_t *xfs_ioend_zone;
@@ -114,6 +115,7 @@ mempool_t *xfs_ioend_pool;#define MNTOPT_NODELAYLOG "nodelaylog"/* Delayed logging disabled */ #define MNTOPT_DISCARD "discard"/* Discard unused blocks */ #define MNTOPT_NODISCARD "nodiscard"/* Do not discard unused blocks */ +#define MNTOPT_HOTTRACK "hot_track"/* hot inode tracking */ /* * Table driven mount option parser.
@@ -371,6 +373,8 @@ xfs_parseargs( mp->m_flags |=XFS_MOUNT_DISCARD ; }else if (!strcmp (this_char,MNTOPT_NODISCARD )) { mp->m_flags &= ~XFS_MOUNT_DISCARD ; + }else if (!strcmp (this_char,MNTOPT_HOTTRACK )) { + mp->m_flags |=XFS_MOUNT_HOTTRACK ; }else if (!strcmp (this_char,"ihashsize" )) {xfs_warn (mp,"ihashsize no longer used, option is deprecated." );
@@ -1005,6 +1009,9 @@ xfs_fs_put_super( {struct xfs_mount *mp =XFS_M (sb); +if (mp->m_flags &XFS_MOUNT_HOTTRACK ) +hot_track_exit (sb); +xfs_filestream_unmount (mp);cancel_delayed_work_sync (&mp->m_sync_work);xfs_unmountfs (mp);
@@ -1407,7 +1414,16 @@ xfs_fs_fill_super( goto out_unmount; } +if (mp->m_flags &XFS_MOUNT_HOTTRACK ) { + error =hot_track_init (sb); +if (error) + goto out_free_root; + } +return 0 ; + out_free_root: +dput (sb->s_root); + sb->s_root =NULL ; out_syncd_stop:xfs_syncd_stop (mp); out_filestream_unmount:
Signature · 1 line
1.7.6.5
PATCH 17/18 ext4: add hot tracking support
In reply to [email protected]From: Zheng Liu <[email protected]>
Define a new mount option to add VFS hot
tracking support in order to use it in ext4.
--- fs/ext4/ext4.h | 3 +++ fs/ext4/super.c | 13 ++++++++++++- 2 files changed, 15 insertions(+), 1 deletions(-)
fs/ext4/ext4.h +3 -0
@@ -1298,6 +1298,9 @@ struct ext4_sb_info {/* Precomputed FS UUID checksum for seeding other checksums */ __u32 s_csum_seed; + +/* Enable hot tracking or not */ +int s_hottrack_enable; };static inline struct ext4_sb_info *EXT4_SB (struct super_block *sb)
fs/ext4/super.c +12 -1
@@ -864,6 +864,8 @@ static void ext4_put_super(struct super_block *sb)ext4_ext_release (sb);ext4_xattr_put_super (sb); +if (sbi->s_hottrack_enable) +hot_track_exit (sb);if (!(sb->s_flags &MS_RDONLY )) {EXT4_CLEAR_INCOMPAT_FEATURE (sb,EXT4_FEATURE_INCOMPAT_RECOVER ); es->s_state =cpu_to_le16 (sbi->s_mount_state);
@@ -1222,7 +1224,7 @@ enum { Opt_inode_readahead_blks, Opt_journal_ioprio, Opt_dioread_nolock, Opt_dioread_lock, Opt_discard, Opt_nodiscard, Opt_init_itable, Opt_noinit_itable, - Opt_max_dir_size_kb, + Opt_max_dir_size_kb, Opt_hottrack, }; static const match_table_t tokens = {
@@ -1297,6 +1299,7 @@ static const match_table_t tokens = { {Opt_init_itable, "init_itable"}, {Opt_noinit_itable, "noinit_itable"}, {Opt_max_dir_size_kb, "max_dir_size_kb=%u"}, + {Opt_hottrack, "hot_track"}, {Opt_removed, "check=none"}, /* mount option from ext2/3 */ {Opt_removed, "nocheck"}, /* mount option from ext2/3 */ {Opt_removed, "reservation"}, /* mount option from ext2/3 */
@@ -1595,6 +1598,14 @@ static int handle_mount_opt(struct super_block *sb, char *opt, int token, sbi->s_li_wait_mult = arg; }else if (token == Opt_max_dir_size_kb) { sbi->s_max_dir_size_kb = arg; + }else if (token == Opt_hottrack) { +if (hot_track_init (sb)) { +ext4_msg (sb,KERN_ERR , +"EXT4-fs: hot tracking initialization" +" failed" ); +return -1 ; + } + sbi->s_hottrack_enable =1 ; }else if (token == Opt_stripe) { sbi->s_stripe = arg; }else if (m->flags &MOPT_DATAJ ) {
Signature · 1 line
1.7.6.5
PATCH 18/18 vfs: add documentation
In reply to [email protected]From: Zhi Yong Wu <[email protected]>
Add one doc for VFS hot tracking feature
--- Documentation/filesystems/00-INDEX | 2 + Documentation/filesystems/hot_tracking.txt | 263 ++++++++++++++++++++++++++++ 2 files changed, 265 insertions(+), 0 deletions(-) create mode 100644 Documentation/filesystems/hot_tracking.txt
Documentation/filesystems/00-INDEX +2 -0
@@ -118,3 +118,5 @@ xfs.txt - info and mount options for the XFS filesystem. xip.txt - info on execute-in-place for file mappings. +hot_tracking.txt + - info on hot data tracking in VFS layer
Documentation/filesystems/hot_tracking.txt new file +263 -0
263 lines not on this page · raw message
Signature · 1 line
1.7.6.5