mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-12 13:55:32 +00:00
![Zheng Liu](/assets/img/avatar_default.png)
Although extent status is loaded on-demand, we also need to reclaim extent from the tree when we are under a heavy memory pressure because in some cases fragmented extent tree causes status tree costs too much memory. Here we maintain a lru list in super_block. When the extent status of an inode is accessed and changed, this inode will be move to the tail of the list. The inode will be dropped from this list when it is cleared. In the inode, a counter is added to count the number of cached objects in extent status tree. Here only written/unwritten/hole extent is counted because delayed extent doesn't be reclaimed due to fiemap, bigalloc and seek_data/hole need it. The counter will be increased as a new extent is allocated, and it will be decreased as a extent is freed. In this commit we use normal shrinker framework to reclaim memory from the status tree. ext4_es_reclaim_extents_count() traverses the lru list to count the number of reclaimable extents. ext4_es_shrink() tries to reclaim written/unwritten/hole extents from extent status tree. The inode that has been shrunk is moved to the tail of lru list. Signed-off-by: Zheng Liu <wenqing.lz@taobao.com> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu> Cc: Jan kara <jack@suse.cz>
114 lines
3.1 KiB
C
114 lines
3.1 KiB
C
/*
|
|
* fs/ext4/extents_status.h
|
|
*
|
|
* Written by Yongqiang Yang <xiaoqiangnk@gmail.com>
|
|
* Modified by
|
|
* Allison Henderson <achender@linux.vnet.ibm.com>
|
|
* Zheng Liu <wenqing.lz@taobao.com>
|
|
*
|
|
*/
|
|
|
|
#ifndef _EXT4_EXTENTS_STATUS_H
|
|
#define _EXT4_EXTENTS_STATUS_H
|
|
|
|
/*
|
|
* Turn on ES_DEBUG__ to get lots of info about extent status operations.
|
|
*/
|
|
#ifdef ES_DEBUG__
|
|
#define es_debug(fmt, ...) printk(fmt, ##__VA_ARGS__)
|
|
#else
|
|
#define es_debug(fmt, ...) no_printk(fmt, ##__VA_ARGS__)
|
|
#endif
|
|
|
|
#define EXTENT_STATUS_WRITTEN 0x80000000 /* written extent */
|
|
#define EXTENT_STATUS_UNWRITTEN 0x40000000 /* unwritten extent */
|
|
#define EXTENT_STATUS_DELAYED 0x20000000 /* delayed extent */
|
|
#define EXTENT_STATUS_HOLE 0x10000000 /* hole */
|
|
|
|
#define EXTENT_STATUS_FLAGS (EXTENT_STATUS_WRITTEN | \
|
|
EXTENT_STATUS_UNWRITTEN | \
|
|
EXTENT_STATUS_DELAYED | \
|
|
EXTENT_STATUS_HOLE)
|
|
|
|
struct extent_status {
|
|
struct rb_node rb_node;
|
|
ext4_lblk_t es_lblk; /* first logical block extent covers */
|
|
ext4_lblk_t es_len; /* length of extent in block */
|
|
ext4_fsblk_t es_pblk; /* first physical block */
|
|
};
|
|
|
|
struct ext4_es_tree {
|
|
struct rb_root root;
|
|
struct extent_status *cache_es; /* recently accessed extent */
|
|
};
|
|
|
|
extern int __init ext4_init_es(void);
|
|
extern void ext4_exit_es(void);
|
|
extern void ext4_es_init_tree(struct ext4_es_tree *tree);
|
|
|
|
extern int ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk,
|
|
ext4_lblk_t len, ext4_fsblk_t pblk,
|
|
unsigned long long status);
|
|
extern int ext4_es_remove_extent(struct inode *inode, ext4_lblk_t lblk,
|
|
ext4_lblk_t len);
|
|
extern void ext4_es_find_delayed_extent(struct inode *inode, ext4_lblk_t lblk,
|
|
struct extent_status *es);
|
|
extern int ext4_es_lookup_extent(struct inode *inode, ext4_lblk_t lblk,
|
|
struct extent_status *es);
|
|
|
|
static inline int ext4_es_is_written(struct extent_status *es)
|
|
{
|
|
return (es->es_pblk & EXTENT_STATUS_WRITTEN);
|
|
}
|
|
|
|
static inline int ext4_es_is_unwritten(struct extent_status *es)
|
|
{
|
|
return (es->es_pblk & EXTENT_STATUS_UNWRITTEN);
|
|
}
|
|
|
|
static inline int ext4_es_is_delayed(struct extent_status *es)
|
|
{
|
|
return (es->es_pblk & EXTENT_STATUS_DELAYED);
|
|
}
|
|
|
|
static inline int ext4_es_is_hole(struct extent_status *es)
|
|
{
|
|
return (es->es_pblk & EXTENT_STATUS_HOLE);
|
|
}
|
|
|
|
static inline ext4_fsblk_t ext4_es_status(struct extent_status *es)
|
|
{
|
|
return (es->es_pblk & EXTENT_STATUS_FLAGS);
|
|
}
|
|
|
|
static inline ext4_fsblk_t ext4_es_pblock(struct extent_status *es)
|
|
{
|
|
return (es->es_pblk & ~EXTENT_STATUS_FLAGS);
|
|
}
|
|
|
|
static inline void ext4_es_store_pblock(struct extent_status *es,
|
|
ext4_fsblk_t pb)
|
|
{
|
|
ext4_fsblk_t block;
|
|
|
|
block = (pb & ~EXTENT_STATUS_FLAGS) |
|
|
(es->es_pblk & EXTENT_STATUS_FLAGS);
|
|
es->es_pblk = block;
|
|
}
|
|
|
|
static inline void ext4_es_store_status(struct extent_status *es,
|
|
unsigned long long status)
|
|
{
|
|
ext4_fsblk_t block;
|
|
|
|
block = (status & EXTENT_STATUS_FLAGS) |
|
|
(es->es_pblk & ~EXTENT_STATUS_FLAGS);
|
|
es->es_pblk = block;
|
|
}
|
|
|
|
extern void ext4_es_register_shrinker(struct super_block *sb);
|
|
extern void ext4_es_unregister_shrinker(struct super_block *sb);
|
|
extern void ext4_es_lru_add(struct inode *inode);
|
|
extern void ext4_es_lru_del(struct inode *inode);
|
|
|
|
#endif /* _EXT4_EXTENTS_STATUS_H */
|