mirror of
https://github.com/versity/scoutfs.git
synced 2026-04-20 05:20:30 +00:00
Add lock coverage for inode index items. Sadly, this isn't trivial. We have to predict the value of the indexed fields before the operation to lock those items. One value in particular we can't reliably predict: the sequence of the transaction we enter after locking. Also operations can create an absolute ton of index item updates -- rename can modify nr_inodes * items_per_inode * 2 items, so maybe 24 today. And these items can be arbitrarily positioned in the key space. So to handle all this we add functions to gather predicted item values we'll need to lock sort and lock them all, then pass appropriate locks down to the item functions during inode updates. The trickiest bit of the index locking code is having to retry if the sequence number changes. Preparing locks has to guess the sequence number of its upcoming trans and then makes item update decisions based on that. If we enter and have a different sequence number then we need to back off and retry with the correct sequence number (we may find that we'll need to update the indexed meta seq and need to have it locked). The use of the functions is straight forward. Sites figure out the predicted sizes, lock, pass the locks to inode updates, and unlock. While we're at it we replace the individual item field tracking variables in the inode info with an array of indexed values. The code ends up a bit nicer. It also gets rid of the indexed time fields that were left behind and were unused. It's worth noting that we're getting exclusive locks on the index updates. Locking the meta/data seq updates results in complete global serialization of all changes. We'll need concurrent writer locks to get concurrency back. Signed-off-by: Zach Brown <zab@versity.com>
111 lines
3.5 KiB
C
111 lines
3.5 KiB
C
#ifndef _SCOUTFS_INODE_H_
|
|
#define _SCOUTFS_INODE_H_
|
|
|
|
#include "key.h"
|
|
#include "lock.h"
|
|
#include "per_task.h"
|
|
#include "count.h"
|
|
|
|
struct scoutfs_lock;
|
|
|
|
struct scoutfs_inode_info {
|
|
/* read or initialized for each inode instance */
|
|
u64 ino;
|
|
u64 next_readdir_pos;
|
|
u64 meta_seq;
|
|
u64 data_seq;
|
|
u64 data_version;
|
|
|
|
/*
|
|
* The in-memory item info caches the current index item values
|
|
* so that we can decide to update them with comparisons instead
|
|
* of by maintaining state that tracks the inode differing from
|
|
* the item. The "item_" prefix is a bit clumsy :/.
|
|
*/
|
|
struct mutex item_mutex;
|
|
bool have_item;
|
|
u64 item_majors[SCOUTFS_INODE_INDEX_NR];
|
|
u32 item_minors[SCOUTFS_INODE_INDEX_NR];
|
|
|
|
/* updated at on each new lock acquisition */
|
|
atomic64_t last_refreshed;
|
|
|
|
/* initialized once for slab object */
|
|
seqcount_t seqcount;
|
|
bool staging; /* holder of i_mutex is staging */
|
|
struct scoutfs_per_task pt_data_lock;
|
|
struct rw_semaphore xattr_rwsem;
|
|
struct rb_node writeback_node;
|
|
|
|
struct inode inode;
|
|
};
|
|
|
|
static inline struct scoutfs_inode_info *SCOUTFS_I(struct inode *inode)
|
|
{
|
|
return container_of(inode, struct scoutfs_inode_info, inode);
|
|
}
|
|
|
|
static inline u64 scoutfs_ino(struct inode *inode)
|
|
{
|
|
return SCOUTFS_I(inode)->ino;
|
|
}
|
|
|
|
void scoutfs_inode_init_key(struct scoutfs_key_buf *key,
|
|
struct scoutfs_inode_key *ikey, u64 ino);
|
|
|
|
struct inode *scoutfs_alloc_inode(struct super_block *sb);
|
|
void scoutfs_destroy_inode(struct inode *inode);
|
|
int scoutfs_drop_inode(struct inode *inode);
|
|
void scoutfs_evict_inode(struct inode *inode);
|
|
int scoutfs_orphan_inode(struct inode *inode);
|
|
|
|
struct inode *scoutfs_iget(struct super_block *sb, u64 ino);
|
|
struct inode *scoutfs_ilookup(struct super_block *sb, u64 ino);
|
|
|
|
int scoutfs_inode_index_start(struct super_block *sb, u64 *seq);
|
|
int scoutfs_inode_index_prepare(struct super_block *sb, struct list_head *list,
|
|
struct inode *inode, u64 new_size,
|
|
bool set_data_seq);
|
|
int scoutfs_inode_index_prepare_ino(struct super_block *sb,
|
|
struct list_head *list, u64 ino,
|
|
umode_t mode, u64 new_size);
|
|
int scoutfs_inode_index_lock_hold(struct super_block *sb,
|
|
struct list_head *list, u64 seq,
|
|
const struct scoutfs_item_count cnt);
|
|
void scoutfs_inode_index_unlock(struct super_block *sb, struct list_head *list);
|
|
|
|
int scoutfs_dirty_inode_item(struct inode *inode, struct scoutfs_lock *lock);
|
|
void scoutfs_update_inode_item(struct inode *inode, struct scoutfs_lock *lock,
|
|
struct list_head *ind_locks);
|
|
|
|
void scoutfs_inode_fill_pool(struct super_block *sb, u64 ino, u64 nr);
|
|
int scoutfs_alloc_ino(struct super_block *sb, u64 *ino);
|
|
struct inode *scoutfs_new_inode(struct super_block *sb, struct inode *dir,
|
|
umode_t mode, dev_t rdev, u64 ino,
|
|
struct scoutfs_lock *lock);
|
|
|
|
void scoutfs_inode_set_meta_seq(struct inode *inode);
|
|
void scoutfs_inode_set_data_seq(struct inode *inode);
|
|
void scoutfs_inode_inc_data_version(struct inode *inode);
|
|
u64 scoutfs_inode_meta_seq(struct inode *inode);
|
|
u64 scoutfs_inode_data_seq(struct inode *inode);
|
|
u64 scoutfs_inode_data_version(struct inode *inode);
|
|
|
|
int scoutfs_inode_refresh(struct inode *inode, struct scoutfs_lock *lock,
|
|
int flags);
|
|
|
|
int scoutfs_scan_orphans(struct super_block *sb);
|
|
|
|
void scoutfs_inode_queue_writeback(struct inode *inode);
|
|
int scoutfs_inode_walk_writeback(struct super_block *sb, bool write);
|
|
|
|
u64 scoutfs_last_ino(struct super_block *sb);
|
|
|
|
void scoutfs_inode_exit(void);
|
|
int scoutfs_inode_init(void);
|
|
|
|
int scoutfs_inode_setup(struct super_block *sb);
|
|
void scoutfs_inode_destroy(struct super_block *sb);
|
|
|
|
#endif
|