Alloc inodes from pool from server

Inode allocation was always modifying the in-memory super block.  This
doesn't work when the server is solely responsible for modifying the
super blocks.  We add network messages to have mounts send a message to
the server to request inodes that they can use to satisfy allocation.

Signed-off-by: Zach Brown <zab@versity.com>
This commit is contained in:
Zach Brown
2017-04-18 13:51:10 -07:00
parent 453715a78d
commit b50de90196
7 changed files with 221 additions and 14 deletions
+118 -10
View File
@@ -17,6 +17,7 @@
#include <linux/xattr.h>
#include <linux/mm.h>
#include <linux/pagemap.h>
#include <linux/sched.h>
#include "format.h"
#include "super.h"
@@ -30,6 +31,7 @@
#include "msg.h"
#include "kvec.h"
#include "item.h"
#include "net.h"
/*
* XXX
@@ -37,6 +39,14 @@
* - use inode item value lengths for forward/back compat
*/
struct free_ino_pool {
wait_queue_head_t waitq;
spinlock_t lock;
u64 ino;
u64 nr;
bool in_flight;
};
static struct kmem_cache *scoutfs_inode_cachep;
static void scoutfs_inode_ctor(void *obj)
@@ -359,24 +369,98 @@ u64 scoutfs_last_ino(struct super_block *sb)
return last;
}
/*
* Network replies refill the pool, providing ino = ~0ULL nr = 0 when
* there's no more inodes (which should never happen in practice.)
*/
void scoutfs_inode_fill_pool(struct super_block *sb, u64 ino, u64 nr)
{
struct free_ino_pool *pool = SCOUTFS_SB(sb)->free_ino_pool;
trace_printk("filling ino %llu nr %llu\n", ino, nr);
spin_lock(&pool->lock);
pool->ino = ino;
pool->nr = nr;
pool->in_flight = false;
spin_unlock(&pool->lock);
wake_up(&pool->waitq);
}
static bool pool_in_flight(struct free_ino_pool *pool)
{
bool in_flight;
spin_lock(&pool->lock);
in_flight = pool->in_flight;
spin_unlock(&pool->lock);
return in_flight;
}
/*
* We have a pool of free inodes given to us by the server. If it
* empties we only ever have one request for new inodes in flight. The
* net layer calls us when it gets a reply. If there's no more inodes
* we'll get ino == ~0 and nr == 0.
*/
static int alloc_ino(struct super_block *sb, u64 *ino)
{
struct scoutfs_sb_info *sbi = SCOUTFS_SB(sb);
struct scoutfs_super_block *super = &sbi->super;
struct free_ino_pool *pool = SCOUTFS_SB(sb)->free_ino_pool;
bool request;
int ret;
spin_lock(&sbi->next_ino_lock);
*ino = 0;
if (super->next_ino == 0) {
ret = -ENOSPC;
} else {
*ino = le64_to_cpu(super->next_ino);
le64_add_cpu(&super->next_ino, 1);
ret = 0;
spin_lock(&pool->lock);
while (pool->nr == 0 && pool->ino != ~0ULL) {
if (pool->in_flight) {
request = false;
} else {
pool->in_flight = true;
request = true;
}
spin_unlock(&pool->lock);
if (request) {
ret = scoutfs_net_alloc_inodes(sb);
if (ret) {
spin_lock(&pool->lock);
pool->in_flight = false;
spin_unlock(&pool->lock);
wake_up(&pool->waitq);
goto out;
}
}
ret = wait_event_interruptible(pool->waitq,
!pool_in_flight(pool));
if (ret)
goto out;
spin_lock(&pool->lock);
}
spin_unlock(&sbi->next_ino_lock);
if (pool->nr == 0) {
*ino = 0;
ret = -ENOSPC;
} else {
*ino = pool->ino++;
pool->nr--;
ret = 0;
}
spin_unlock(&pool->lock);
out:
trace_printk("ret %d ino %llu pool ino %llu nr %llu req %u (racey)\n",
ret, *ino, pool->ino, pool->nr, pool->in_flight);
return ret;
}
@@ -633,6 +717,30 @@ int scoutfs_orphan_inode(struct inode *inode)
return ret;
}
int scoutfs_inode_setup(struct super_block *sb)
{
struct scoutfs_sb_info *sbi = SCOUTFS_SB(sb);
struct free_ino_pool *pool;
pool = kzalloc(sizeof(struct free_ino_pool), GFP_KERNEL);
if (!pool)
return -ENOMEM;
init_waitqueue_head(&pool->waitq);
spin_lock_init(&pool->lock);
sbi->free_ino_pool = pool;
return 0;
}
void scoutfs_inode_destroy(struct super_block *sb)
{
struct free_ino_pool *pool = SCOUTFS_SB(sb)->free_ino_pool;
kfree(pool);
}
void scoutfs_inode_exit(void)
{
if (scoutfs_inode_cachep) {