[PATCH v1 hot_track 03/18] vfs: add I/O frequency update function
From: Zhi Yong Wu Add some util helpers to update access frequencies for one file or its range. Signed-off-by: Zhi Yong Wu --- fs/hot_tracking.c| 174 ++ fs/hot_tracking.h|5 + include/linux/hot_tracking.h |4 + 3 files changed, 183 insertions(+), 0 deletions(-) diff --git a/fs/hot_tracking.c b/fs/hot_tracking.c index 3118c0b..1142ef1 100644 --- a/fs/hot_tracking.c +++ b/fs/hot_tracking.c @@ -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(>lock); + he = radix_tree_lookup(>hot_inode_tree, ino); + if (he) { + kref_get(>hot_inode.refs); + spin_unlock(>lock); + return he; + } + spin_unlock(>lock); + + he = kmem_cache_zalloc(hot_inode_item_cachep, GFP_NOFS); + if (!he) + return ERR_PTR(-ENOMEM); + + hot_inode_item_init(he, ino, >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(>lock); + ret = radix_tree_insert(>hot_inode_tree, ino, he); + if (ret == -EEXIST) { + kmem_cache_free(hot_inode_item_cachep, he); + spin_unlock(>lock); + radix_tree_preload_end(); + goto again; + } + spin_unlock(>lock); + radix_tree_preload_end(); + + kref_get(>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(>lock); + hr = radix_tree_lookup(>hot_range_tree, start); + if (hr) { + kref_get(>hot_range.refs); + spin_unlock(>lock); + return hr; + } + spin_unlock(>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(>lock); + ret = radix_tree_insert(>hot_range_tree, start, hr); + if (ret == -EEXIST) { + kmem_cache_free(hot_range_item_cachep, hr); + spin_unlock(>lock); + radix_tree_preload_end(); + goto again; + } + spin_unlock(>lock); + radix_tree_preload_end(); + + kref_get(>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(_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, + _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, + _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(>hot_inode.lock); +
[PATCH v1 hot_track 03/18] vfs: add I/O frequency update function
From: Zhi Yong Wu wu...@linux.vnet.ibm.com Add some util helpers to update access frequencies for one file or its range. Signed-off-by: Zhi Yong Wu wu...@linux.vnet.ibm.com --- fs/hot_tracking.c| 174 ++ fs/hot_tracking.h|5 + include/linux/hot_tracking.h |4 + 3 files changed, 183 insertions(+), 0 deletions(-) diff --git a/fs/hot_tracking.c b/fs/hot_tracking.c index 3118c0b..1142ef1 100644 --- a/fs/hot_tracking.c +++ b/fs/hot_tracking.c @@ -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)) { +