Highlights:
- Server-to-server copy code from Olga. To use it, client and both servers must have support, the target server must be able to access the source server over NFSv4.2, and the target server must have the inter_copy_offload_enable module parameter set. - Improvements and bugfixes for the new filehandle cache, especially in the container case, from Trond - Also from Trond, better reporting of write errors. - Y2038 work from Arnd. -----BEGIN PGP SIGNATURE----- iQJJBAABCAAzFiEEYtFWavXG9hZotryuJ5vNeUKO4b4FAl490mAVHGJmaWVsZHNA ZmllbGRzZXMub3JnAAoJECebzXlCjuG+HkkP/33CsYXp0wvfNrfxCY3zHRxHpfw+ T9Ownxxw0RAJc/dRluC/2PIKJ20uVqtLrplU63bMBqJn84WF7OALq9twZ79a3fVF mvdmnZbNq9B3ncKJlT7akkEelyJCRap7NgG/oTyubE8MlPl6gKpD8c+G7XdW/uN+ r0fprQz4rW4CYCBGSHq7HusEKqY4Gw+gbyAfJ6A79TMjF1ei51PG+9c8rkIsI5CO 1TQ3gY1gSJmGf2DoF86Q9WTVb+DvRTEs+t7QkxY/Vlo+QXY8CZyu+qSxN7i/F20m gv2GrSpQMS9DEK/ZaG6cxaH+sM18Db4KLvcl3koL6lONHDR2OafSdKLyy0I60jhO WfDSHhfDCrAdASTjNlTPrjBrdK3gafiaJVL9vy901ZJjPaNb3EH0nMQ5bEvOBECq TCqPcQUcbku+qUVIcFwzSK1hXQFQHNh8WIuqXvNviZIzFDoipwsHVnQK02Owj89L R2tbZue1O8voacg/9xw3tWAT7pI+SaBb0EvJuqRxBshiZEU8kKKtMchOwSECRDcu k4lcqC5EFW7e4EzGlr6Wx8sI5lwCapva8ccjmPXX+R/vyM81oxWGB84GqWjjwubH 3Fcok23F9rW2IQJkqgPlNj/9hAjTn2+vM13UbfMlnchGNsQ2gbkc5CDGC/J6Wwpo tHVristV9Gu5bJym =FxLY -----END PGP SIGNATURE----- Merge tag 'nfsd-5.6' of git://linux-nfs.org/~bfields/linux Pull nfsd updates from Bruce Fields: "Highlights: - Server-to-server copy code from Olga. To use it, client and both servers must have support, the target server must be able to access the source server over NFSv4.2, and the target server must have the inter_copy_offload_enable module parameter set. - Improvements and bugfixes for the new filehandle cache, especially in the container case, from Trond - Also from Trond, better reporting of write errors. - Y2038 work from Arnd" * tag 'nfsd-5.6' of git://linux-nfs.org/~bfields/linux: (55 commits) sunrpc: expiry_time should be seconds not timeval nfsd: make nfsd_filecache_wq variable static nfsd4: fix double free in nfsd4_do_async_copy() nfsd: convert file cache to use over/underflow safe refcount nfsd: Define the file access mode enum for tracing nfsd: Fix a perf warning nfsd: Ensure sampling of the write verifier is atomic with the write nfsd: Ensure sampling of the commit verifier is atomic with the commit sunrpc: clean up cache entry add/remove from hashtable sunrpc: Fix potential leaks in sunrpc_cache_unhash() nfsd: Ensure exclusion between CLONE and WRITE errors nfsd: Pass the nfsd_file as arguments to nfsd4_clone_file_range() nfsd: Update the boot verifier on stable writes too. nfsd: Fix stable writes nfsd: Allow nfsd_vfs_write() to take the nfsd_file as an argument nfsd: Fix a soft lockup race in nfsd_file_mark_find_or_create() nfsd: Reduce the number of calls to nfsd_file_gc() nfsd: Schedule the laundrette regularly irrespective of file errors nfsd: Remove unused constant NFSD_FILE_LRU_RESCAN nfsd: Containerise filecache laundrette ...
This commit is contained in:
commit
08dffcc7d9
|
@ -134,6 +134,16 @@ config NFSD_FLEXFILELAYOUT
|
||||||
|
|
||||||
If unsure, say N.
|
If unsure, say N.
|
||||||
|
|
||||||
|
config NFSD_V4_2_INTER_SSC
|
||||||
|
bool "NFSv4.2 inter server to server COPY"
|
||||||
|
depends on NFSD_V4 && NFS_V4_1 && NFS_V4_2
|
||||||
|
help
|
||||||
|
This option enables support for NFSv4.2 inter server to
|
||||||
|
server copy where the destination server calls the NFSv4.2
|
||||||
|
client to read the data to copy from the source server.
|
||||||
|
|
||||||
|
If unsure, say N.
|
||||||
|
|
||||||
config NFSD_V4_SECURITY_LABEL
|
config NFSD_V4_SECURITY_LABEL
|
||||||
bool "Provide Security Label support for NFSv4 server"
|
bool "Provide Security Label support for NFSv4 server"
|
||||||
depends on NFSD_V4 && SECURITY
|
depends on NFSD_V4 && SECURITY
|
||||||
|
|
|
@ -27,7 +27,6 @@
|
||||||
#define NFSD_FILE_HASH_SIZE (1 << NFSD_FILE_HASH_BITS)
|
#define NFSD_FILE_HASH_SIZE (1 << NFSD_FILE_HASH_BITS)
|
||||||
#define NFSD_LAUNDRETTE_DELAY (2 * HZ)
|
#define NFSD_LAUNDRETTE_DELAY (2 * HZ)
|
||||||
|
|
||||||
#define NFSD_FILE_LRU_RESCAN (0)
|
|
||||||
#define NFSD_FILE_SHUTDOWN (1)
|
#define NFSD_FILE_SHUTDOWN (1)
|
||||||
#define NFSD_FILE_LRU_THRESHOLD (4096UL)
|
#define NFSD_FILE_LRU_THRESHOLD (4096UL)
|
||||||
#define NFSD_FILE_LRU_LIMIT (NFSD_FILE_LRU_THRESHOLD << 2)
|
#define NFSD_FILE_LRU_LIMIT (NFSD_FILE_LRU_THRESHOLD << 2)
|
||||||
|
@ -44,6 +43,17 @@ struct nfsd_fcache_bucket {
|
||||||
|
|
||||||
static DEFINE_PER_CPU(unsigned long, nfsd_file_cache_hits);
|
static DEFINE_PER_CPU(unsigned long, nfsd_file_cache_hits);
|
||||||
|
|
||||||
|
struct nfsd_fcache_disposal {
|
||||||
|
struct list_head list;
|
||||||
|
struct work_struct work;
|
||||||
|
struct net *net;
|
||||||
|
spinlock_t lock;
|
||||||
|
struct list_head freeme;
|
||||||
|
struct rcu_head rcu;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct workqueue_struct *nfsd_filecache_wq __read_mostly;
|
||||||
|
|
||||||
static struct kmem_cache *nfsd_file_slab;
|
static struct kmem_cache *nfsd_file_slab;
|
||||||
static struct kmem_cache *nfsd_file_mark_slab;
|
static struct kmem_cache *nfsd_file_mark_slab;
|
||||||
static struct nfsd_fcache_bucket *nfsd_file_hashtbl;
|
static struct nfsd_fcache_bucket *nfsd_file_hashtbl;
|
||||||
|
@ -52,32 +62,21 @@ static long nfsd_file_lru_flags;
|
||||||
static struct fsnotify_group *nfsd_file_fsnotify_group;
|
static struct fsnotify_group *nfsd_file_fsnotify_group;
|
||||||
static atomic_long_t nfsd_filecache_count;
|
static atomic_long_t nfsd_filecache_count;
|
||||||
static struct delayed_work nfsd_filecache_laundrette;
|
static struct delayed_work nfsd_filecache_laundrette;
|
||||||
|
static DEFINE_SPINLOCK(laundrette_lock);
|
||||||
|
static LIST_HEAD(laundrettes);
|
||||||
|
|
||||||
enum nfsd_file_laundrette_ctl {
|
static void nfsd_file_gc(void);
|
||||||
NFSD_FILE_LAUNDRETTE_NOFLUSH = 0,
|
|
||||||
NFSD_FILE_LAUNDRETTE_MAY_FLUSH
|
|
||||||
};
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
nfsd_file_schedule_laundrette(enum nfsd_file_laundrette_ctl ctl)
|
nfsd_file_schedule_laundrette(void)
|
||||||
{
|
{
|
||||||
long count = atomic_long_read(&nfsd_filecache_count);
|
long count = atomic_long_read(&nfsd_filecache_count);
|
||||||
|
|
||||||
if (count == 0 || test_bit(NFSD_FILE_SHUTDOWN, &nfsd_file_lru_flags))
|
if (count == 0 || test_bit(NFSD_FILE_SHUTDOWN, &nfsd_file_lru_flags))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Be more aggressive about scanning if over the threshold */
|
queue_delayed_work(system_wq, &nfsd_filecache_laundrette,
|
||||||
if (count > NFSD_FILE_LRU_THRESHOLD)
|
NFSD_LAUNDRETTE_DELAY);
|
||||||
mod_delayed_work(system_wq, &nfsd_filecache_laundrette, 0);
|
|
||||||
else
|
|
||||||
schedule_delayed_work(&nfsd_filecache_laundrette, NFSD_LAUNDRETTE_DELAY);
|
|
||||||
|
|
||||||
if (ctl == NFSD_FILE_LAUNDRETTE_NOFLUSH)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* ...and don't delay flushing if we're out of control */
|
|
||||||
if (count >= NFSD_FILE_LRU_LIMIT)
|
|
||||||
flush_delayed_work(&nfsd_filecache_laundrette);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -101,7 +100,7 @@ nfsd_file_mark_free(struct fsnotify_mark *mark)
|
||||||
static struct nfsd_file_mark *
|
static struct nfsd_file_mark *
|
||||||
nfsd_file_mark_get(struct nfsd_file_mark *nfm)
|
nfsd_file_mark_get(struct nfsd_file_mark *nfm)
|
||||||
{
|
{
|
||||||
if (!atomic_inc_not_zero(&nfm->nfm_ref))
|
if (!refcount_inc_not_zero(&nfm->nfm_ref))
|
||||||
return NULL;
|
return NULL;
|
||||||
return nfm;
|
return nfm;
|
||||||
}
|
}
|
||||||
|
@ -109,8 +108,7 @@ nfsd_file_mark_get(struct nfsd_file_mark *nfm)
|
||||||
static void
|
static void
|
||||||
nfsd_file_mark_put(struct nfsd_file_mark *nfm)
|
nfsd_file_mark_put(struct nfsd_file_mark *nfm)
|
||||||
{
|
{
|
||||||
if (atomic_dec_and_test(&nfm->nfm_ref)) {
|
if (refcount_dec_and_test(&nfm->nfm_ref)) {
|
||||||
|
|
||||||
fsnotify_destroy_mark(&nfm->nfm_mark, nfsd_file_fsnotify_group);
|
fsnotify_destroy_mark(&nfm->nfm_mark, nfsd_file_fsnotify_group);
|
||||||
fsnotify_put_mark(&nfm->nfm_mark);
|
fsnotify_put_mark(&nfm->nfm_mark);
|
||||||
}
|
}
|
||||||
|
@ -133,9 +131,13 @@ nfsd_file_mark_find_or_create(struct nfsd_file *nf)
|
||||||
struct nfsd_file_mark,
|
struct nfsd_file_mark,
|
||||||
nfm_mark));
|
nfm_mark));
|
||||||
mutex_unlock(&nfsd_file_fsnotify_group->mark_mutex);
|
mutex_unlock(&nfsd_file_fsnotify_group->mark_mutex);
|
||||||
fsnotify_put_mark(mark);
|
if (nfm) {
|
||||||
if (likely(nfm))
|
fsnotify_put_mark(mark);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
/* Avoid soft lockup race with nfsd_file_mark_put() */
|
||||||
|
fsnotify_destroy_mark(mark, nfsd_file_fsnotify_group);
|
||||||
|
fsnotify_put_mark(mark);
|
||||||
} else
|
} else
|
||||||
mutex_unlock(&nfsd_file_fsnotify_group->mark_mutex);
|
mutex_unlock(&nfsd_file_fsnotify_group->mark_mutex);
|
||||||
|
|
||||||
|
@ -145,7 +147,7 @@ nfsd_file_mark_find_or_create(struct nfsd_file *nf)
|
||||||
return NULL;
|
return NULL;
|
||||||
fsnotify_init_mark(&new->nfm_mark, nfsd_file_fsnotify_group);
|
fsnotify_init_mark(&new->nfm_mark, nfsd_file_fsnotify_group);
|
||||||
new->nfm_mark.mask = FS_ATTRIB|FS_DELETE_SELF;
|
new->nfm_mark.mask = FS_ATTRIB|FS_DELETE_SELF;
|
||||||
atomic_set(&new->nfm_ref, 1);
|
refcount_set(&new->nfm_ref, 1);
|
||||||
|
|
||||||
err = fsnotify_add_inode_mark(&new->nfm_mark, inode, 0);
|
err = fsnotify_add_inode_mark(&new->nfm_mark, inode, 0);
|
||||||
|
|
||||||
|
@ -183,7 +185,7 @@ nfsd_file_alloc(struct inode *inode, unsigned int may, unsigned int hashval,
|
||||||
nf->nf_flags = 0;
|
nf->nf_flags = 0;
|
||||||
nf->nf_inode = inode;
|
nf->nf_inode = inode;
|
||||||
nf->nf_hashval = hashval;
|
nf->nf_hashval = hashval;
|
||||||
atomic_set(&nf->nf_ref, 1);
|
refcount_set(&nf->nf_ref, 1);
|
||||||
nf->nf_may = may & NFSD_FILE_MAY_MASK;
|
nf->nf_may = may & NFSD_FILE_MAY_MASK;
|
||||||
if (may & NFSD_MAY_NOT_BREAK_LEASE) {
|
if (may & NFSD_MAY_NOT_BREAK_LEASE) {
|
||||||
if (may & NFSD_MAY_WRITE)
|
if (may & NFSD_MAY_WRITE)
|
||||||
|
@ -192,6 +194,7 @@ nfsd_file_alloc(struct inode *inode, unsigned int may, unsigned int hashval,
|
||||||
__set_bit(NFSD_FILE_BREAK_READ, &nf->nf_flags);
|
__set_bit(NFSD_FILE_BREAK_READ, &nf->nf_flags);
|
||||||
}
|
}
|
||||||
nf->nf_mark = NULL;
|
nf->nf_mark = NULL;
|
||||||
|
init_rwsem(&nf->nf_rwsem);
|
||||||
trace_nfsd_file_alloc(nf);
|
trace_nfsd_file_alloc(nf);
|
||||||
}
|
}
|
||||||
return nf;
|
return nf;
|
||||||
|
@ -238,13 +241,6 @@ nfsd_file_check_write_error(struct nfsd_file *nf)
|
||||||
return filemap_check_wb_err(file->f_mapping, READ_ONCE(file->f_wb_err));
|
return filemap_check_wb_err(file->f_mapping, READ_ONCE(file->f_wb_err));
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
|
||||||
nfsd_file_in_use(struct nfsd_file *nf)
|
|
||||||
{
|
|
||||||
return nfsd_file_check_writeback(nf) ||
|
|
||||||
nfsd_file_check_write_error(nf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
nfsd_file_do_unhash(struct nfsd_file *nf)
|
nfsd_file_do_unhash(struct nfsd_file *nf)
|
||||||
{
|
{
|
||||||
|
@ -256,8 +252,6 @@ nfsd_file_do_unhash(struct nfsd_file *nf)
|
||||||
nfsd_reset_boot_verifier(net_generic(nf->nf_net, nfsd_net_id));
|
nfsd_reset_boot_verifier(net_generic(nf->nf_net, nfsd_net_id));
|
||||||
--nfsd_file_hashtbl[nf->nf_hashval].nfb_count;
|
--nfsd_file_hashtbl[nf->nf_hashval].nfb_count;
|
||||||
hlist_del_rcu(&nf->nf_node);
|
hlist_del_rcu(&nf->nf_node);
|
||||||
if (!list_empty(&nf->nf_lru))
|
|
||||||
list_lru_del(&nfsd_file_lru, &nf->nf_lru);
|
|
||||||
atomic_long_dec(&nfsd_filecache_count);
|
atomic_long_dec(&nfsd_filecache_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,6 +260,8 @@ nfsd_file_unhash(struct nfsd_file *nf)
|
||||||
{
|
{
|
||||||
if (test_and_clear_bit(NFSD_FILE_HASHED, &nf->nf_flags)) {
|
if (test_and_clear_bit(NFSD_FILE_HASHED, &nf->nf_flags)) {
|
||||||
nfsd_file_do_unhash(nf);
|
nfsd_file_do_unhash(nf);
|
||||||
|
if (!list_empty(&nf->nf_lru))
|
||||||
|
list_lru_del(&nfsd_file_lru, &nf->nf_lru);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -283,42 +279,48 @@ nfsd_file_unhash_and_release_locked(struct nfsd_file *nf, struct list_head *disp
|
||||||
if (!nfsd_file_unhash(nf))
|
if (!nfsd_file_unhash(nf))
|
||||||
return false;
|
return false;
|
||||||
/* keep final reference for nfsd_file_lru_dispose */
|
/* keep final reference for nfsd_file_lru_dispose */
|
||||||
if (atomic_add_unless(&nf->nf_ref, -1, 1))
|
if (refcount_dec_not_one(&nf->nf_ref))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
list_add(&nf->nf_lru, dispose);
|
list_add(&nf->nf_lru, dispose);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static void
|
||||||
nfsd_file_put_noref(struct nfsd_file *nf)
|
nfsd_file_put_noref(struct nfsd_file *nf)
|
||||||
{
|
{
|
||||||
int count;
|
|
||||||
trace_nfsd_file_put(nf);
|
trace_nfsd_file_put(nf);
|
||||||
|
|
||||||
count = atomic_dec_return(&nf->nf_ref);
|
if (refcount_dec_and_test(&nf->nf_ref)) {
|
||||||
if (!count) {
|
|
||||||
WARN_ON(test_bit(NFSD_FILE_HASHED, &nf->nf_flags));
|
WARN_ON(test_bit(NFSD_FILE_HASHED, &nf->nf_flags));
|
||||||
nfsd_file_free(nf);
|
nfsd_file_free(nf);
|
||||||
}
|
}
|
||||||
return count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nfsd_file_put(struct nfsd_file *nf)
|
nfsd_file_put(struct nfsd_file *nf)
|
||||||
{
|
{
|
||||||
bool is_hashed = test_bit(NFSD_FILE_HASHED, &nf->nf_flags) != 0;
|
bool is_hashed;
|
||||||
bool unused = !nfsd_file_in_use(nf);
|
|
||||||
|
|
||||||
set_bit(NFSD_FILE_REFERENCED, &nf->nf_flags);
|
set_bit(NFSD_FILE_REFERENCED, &nf->nf_flags);
|
||||||
if (nfsd_file_put_noref(nf) == 1 && is_hashed && unused)
|
if (refcount_read(&nf->nf_ref) > 2 || !nf->nf_file) {
|
||||||
nfsd_file_schedule_laundrette(NFSD_FILE_LAUNDRETTE_MAY_FLUSH);
|
nfsd_file_put_noref(nf);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
filemap_flush(nf->nf_file->f_mapping);
|
||||||
|
is_hashed = test_bit(NFSD_FILE_HASHED, &nf->nf_flags) != 0;
|
||||||
|
nfsd_file_put_noref(nf);
|
||||||
|
if (is_hashed)
|
||||||
|
nfsd_file_schedule_laundrette();
|
||||||
|
if (atomic_long_read(&nfsd_filecache_count) >= NFSD_FILE_LRU_LIMIT)
|
||||||
|
nfsd_file_gc();
|
||||||
}
|
}
|
||||||
|
|
||||||
struct nfsd_file *
|
struct nfsd_file *
|
||||||
nfsd_file_get(struct nfsd_file *nf)
|
nfsd_file_get(struct nfsd_file *nf)
|
||||||
{
|
{
|
||||||
if (likely(atomic_inc_not_zero(&nf->nf_ref)))
|
if (likely(refcount_inc_not_zero(&nf->nf_ref)))
|
||||||
return nf;
|
return nf;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -344,7 +346,7 @@ nfsd_file_dispose_list_sync(struct list_head *dispose)
|
||||||
while(!list_empty(dispose)) {
|
while(!list_empty(dispose)) {
|
||||||
nf = list_first_entry(dispose, struct nfsd_file, nf_lru);
|
nf = list_first_entry(dispose, struct nfsd_file, nf_lru);
|
||||||
list_del(&nf->nf_lru);
|
list_del(&nf->nf_lru);
|
||||||
if (!atomic_dec_and_test(&nf->nf_ref))
|
if (!refcount_dec_and_test(&nf->nf_ref))
|
||||||
continue;
|
continue;
|
||||||
if (nfsd_file_free(nf))
|
if (nfsd_file_free(nf))
|
||||||
flush = true;
|
flush = true;
|
||||||
|
@ -353,6 +355,58 @@ nfsd_file_dispose_list_sync(struct list_head *dispose)
|
||||||
flush_delayed_fput();
|
flush_delayed_fput();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
nfsd_file_list_remove_disposal(struct list_head *dst,
|
||||||
|
struct nfsd_fcache_disposal *l)
|
||||||
|
{
|
||||||
|
spin_lock(&l->lock);
|
||||||
|
list_splice_init(&l->freeme, dst);
|
||||||
|
spin_unlock(&l->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
nfsd_file_list_add_disposal(struct list_head *files, struct net *net)
|
||||||
|
{
|
||||||
|
struct nfsd_fcache_disposal *l;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
list_for_each_entry_rcu(l, &laundrettes, list) {
|
||||||
|
if (l->net == net) {
|
||||||
|
spin_lock(&l->lock);
|
||||||
|
list_splice_tail_init(files, &l->freeme);
|
||||||
|
spin_unlock(&l->lock);
|
||||||
|
queue_work(nfsd_filecache_wq, &l->work);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
nfsd_file_list_add_pernet(struct list_head *dst, struct list_head *src,
|
||||||
|
struct net *net)
|
||||||
|
{
|
||||||
|
struct nfsd_file *nf, *tmp;
|
||||||
|
|
||||||
|
list_for_each_entry_safe(nf, tmp, src, nf_lru) {
|
||||||
|
if (nf->nf_net == net)
|
||||||
|
list_move_tail(&nf->nf_lru, dst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
nfsd_file_dispose_list_delayed(struct list_head *dispose)
|
||||||
|
{
|
||||||
|
LIST_HEAD(list);
|
||||||
|
struct nfsd_file *nf;
|
||||||
|
|
||||||
|
while(!list_empty(dispose)) {
|
||||||
|
nf = list_first_entry(dispose, struct nfsd_file, nf_lru);
|
||||||
|
nfsd_file_list_add_pernet(&list, dispose, nf->nf_net);
|
||||||
|
nfsd_file_list_add_disposal(&list, nf->nf_net);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Note this can deadlock with nfsd_file_cache_purge.
|
* Note this can deadlock with nfsd_file_cache_purge.
|
||||||
*/
|
*/
|
||||||
|
@ -375,7 +429,7 @@ nfsd_file_lru_cb(struct list_head *item, struct list_lru_one *lru,
|
||||||
* counter. Here we check the counter and then test and clear the flag.
|
* counter. Here we check the counter and then test and clear the flag.
|
||||||
* That order is deliberate to ensure that we can do this locklessly.
|
* That order is deliberate to ensure that we can do this locklessly.
|
||||||
*/
|
*/
|
||||||
if (atomic_read(&nf->nf_ref) > 1)
|
if (refcount_read(&nf->nf_ref) > 1)
|
||||||
goto out_skip;
|
goto out_skip;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -386,31 +440,51 @@ nfsd_file_lru_cb(struct list_head *item, struct list_lru_one *lru,
|
||||||
goto out_skip;
|
goto out_skip;
|
||||||
|
|
||||||
if (test_and_clear_bit(NFSD_FILE_REFERENCED, &nf->nf_flags))
|
if (test_and_clear_bit(NFSD_FILE_REFERENCED, &nf->nf_flags))
|
||||||
goto out_rescan;
|
goto out_skip;
|
||||||
|
|
||||||
if (!test_and_clear_bit(NFSD_FILE_HASHED, &nf->nf_flags))
|
if (!test_and_clear_bit(NFSD_FILE_HASHED, &nf->nf_flags))
|
||||||
goto out_skip;
|
goto out_skip;
|
||||||
|
|
||||||
list_lru_isolate_move(lru, &nf->nf_lru, head);
|
list_lru_isolate_move(lru, &nf->nf_lru, head);
|
||||||
return LRU_REMOVED;
|
return LRU_REMOVED;
|
||||||
out_rescan:
|
|
||||||
set_bit(NFSD_FILE_LRU_RESCAN, &nfsd_file_lru_flags);
|
|
||||||
out_skip:
|
out_skip:
|
||||||
return LRU_SKIP;
|
return LRU_SKIP;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static unsigned long
|
||||||
nfsd_file_lru_dispose(struct list_head *head)
|
nfsd_file_lru_walk_list(struct shrink_control *sc)
|
||||||
{
|
{
|
||||||
while(!list_empty(head)) {
|
LIST_HEAD(head);
|
||||||
struct nfsd_file *nf = list_first_entry(head,
|
struct nfsd_file *nf;
|
||||||
struct nfsd_file, nf_lru);
|
unsigned long ret;
|
||||||
list_del_init(&nf->nf_lru);
|
|
||||||
|
if (sc)
|
||||||
|
ret = list_lru_shrink_walk(&nfsd_file_lru, sc,
|
||||||
|
nfsd_file_lru_cb, &head);
|
||||||
|
else
|
||||||
|
ret = list_lru_walk(&nfsd_file_lru,
|
||||||
|
nfsd_file_lru_cb,
|
||||||
|
&head, LONG_MAX);
|
||||||
|
list_for_each_entry(nf, &head, nf_lru) {
|
||||||
spin_lock(&nfsd_file_hashtbl[nf->nf_hashval].nfb_lock);
|
spin_lock(&nfsd_file_hashtbl[nf->nf_hashval].nfb_lock);
|
||||||
nfsd_file_do_unhash(nf);
|
nfsd_file_do_unhash(nf);
|
||||||
spin_unlock(&nfsd_file_hashtbl[nf->nf_hashval].nfb_lock);
|
spin_unlock(&nfsd_file_hashtbl[nf->nf_hashval].nfb_lock);
|
||||||
nfsd_file_put_noref(nf);
|
|
||||||
}
|
}
|
||||||
|
nfsd_file_dispose_list_delayed(&head);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
nfsd_file_gc(void)
|
||||||
|
{
|
||||||
|
nfsd_file_lru_walk_list(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
nfsd_file_gc_worker(struct work_struct *work)
|
||||||
|
{
|
||||||
|
nfsd_file_gc();
|
||||||
|
nfsd_file_schedule_laundrette();
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned long
|
static unsigned long
|
||||||
|
@ -422,12 +496,7 @@ nfsd_file_lru_count(struct shrinker *s, struct shrink_control *sc)
|
||||||
static unsigned long
|
static unsigned long
|
||||||
nfsd_file_lru_scan(struct shrinker *s, struct shrink_control *sc)
|
nfsd_file_lru_scan(struct shrinker *s, struct shrink_control *sc)
|
||||||
{
|
{
|
||||||
LIST_HEAD(head);
|
return nfsd_file_lru_walk_list(sc);
|
||||||
unsigned long ret;
|
|
||||||
|
|
||||||
ret = list_lru_shrink_walk(&nfsd_file_lru, sc, nfsd_file_lru_cb, &head);
|
|
||||||
nfsd_file_lru_dispose(&head);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct shrinker nfsd_file_shrinker = {
|
static struct shrinker nfsd_file_shrinker = {
|
||||||
|
@ -489,7 +558,7 @@ nfsd_file_close_inode(struct inode *inode)
|
||||||
|
|
||||||
__nfsd_file_close_inode(inode, hashval, &dispose);
|
__nfsd_file_close_inode(inode, hashval, &dispose);
|
||||||
trace_nfsd_file_close_inode(inode, hashval, !list_empty(&dispose));
|
trace_nfsd_file_close_inode(inode, hashval, !list_empty(&dispose));
|
||||||
nfsd_file_dispose_list(&dispose);
|
nfsd_file_dispose_list_delayed(&dispose);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -505,16 +574,11 @@ static void
|
||||||
nfsd_file_delayed_close(struct work_struct *work)
|
nfsd_file_delayed_close(struct work_struct *work)
|
||||||
{
|
{
|
||||||
LIST_HEAD(head);
|
LIST_HEAD(head);
|
||||||
|
struct nfsd_fcache_disposal *l = container_of(work,
|
||||||
|
struct nfsd_fcache_disposal, work);
|
||||||
|
|
||||||
list_lru_walk(&nfsd_file_lru, nfsd_file_lru_cb, &head, LONG_MAX);
|
nfsd_file_list_remove_disposal(&head, l);
|
||||||
|
nfsd_file_dispose_list(&head);
|
||||||
if (test_and_clear_bit(NFSD_FILE_LRU_RESCAN, &nfsd_file_lru_flags))
|
|
||||||
nfsd_file_schedule_laundrette(NFSD_FILE_LAUNDRETTE_NOFLUSH);
|
|
||||||
|
|
||||||
if (!list_empty(&head)) {
|
|
||||||
nfsd_file_lru_dispose(&head);
|
|
||||||
flush_delayed_fput();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -575,6 +639,10 @@ nfsd_file_cache_init(void)
|
||||||
if (nfsd_file_hashtbl)
|
if (nfsd_file_hashtbl)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
nfsd_filecache_wq = alloc_workqueue("nfsd_filecache", 0, 0);
|
||||||
|
if (!nfsd_filecache_wq)
|
||||||
|
goto out;
|
||||||
|
|
||||||
nfsd_file_hashtbl = kcalloc(NFSD_FILE_HASH_SIZE,
|
nfsd_file_hashtbl = kcalloc(NFSD_FILE_HASH_SIZE,
|
||||||
sizeof(*nfsd_file_hashtbl), GFP_KERNEL);
|
sizeof(*nfsd_file_hashtbl), GFP_KERNEL);
|
||||||
if (!nfsd_file_hashtbl) {
|
if (!nfsd_file_hashtbl) {
|
||||||
|
@ -628,7 +696,7 @@ nfsd_file_cache_init(void)
|
||||||
spin_lock_init(&nfsd_file_hashtbl[i].nfb_lock);
|
spin_lock_init(&nfsd_file_hashtbl[i].nfb_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
INIT_DELAYED_WORK(&nfsd_filecache_laundrette, nfsd_file_delayed_close);
|
INIT_DELAYED_WORK(&nfsd_filecache_laundrette, nfsd_file_gc_worker);
|
||||||
out:
|
out:
|
||||||
return ret;
|
return ret;
|
||||||
out_notifier:
|
out_notifier:
|
||||||
|
@ -644,6 +712,8 @@ out_err:
|
||||||
nfsd_file_mark_slab = NULL;
|
nfsd_file_mark_slab = NULL;
|
||||||
kfree(nfsd_file_hashtbl);
|
kfree(nfsd_file_hashtbl);
|
||||||
nfsd_file_hashtbl = NULL;
|
nfsd_file_hashtbl = NULL;
|
||||||
|
destroy_workqueue(nfsd_filecache_wq);
|
||||||
|
nfsd_filecache_wq = NULL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -682,6 +752,88 @@ nfsd_file_cache_purge(struct net *net)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct nfsd_fcache_disposal *
|
||||||
|
nfsd_alloc_fcache_disposal(struct net *net)
|
||||||
|
{
|
||||||
|
struct nfsd_fcache_disposal *l;
|
||||||
|
|
||||||
|
l = kmalloc(sizeof(*l), GFP_KERNEL);
|
||||||
|
if (!l)
|
||||||
|
return NULL;
|
||||||
|
INIT_WORK(&l->work, nfsd_file_delayed_close);
|
||||||
|
l->net = net;
|
||||||
|
spin_lock_init(&l->lock);
|
||||||
|
INIT_LIST_HEAD(&l->freeme);
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
nfsd_free_fcache_disposal(struct nfsd_fcache_disposal *l)
|
||||||
|
{
|
||||||
|
rcu_assign_pointer(l->net, NULL);
|
||||||
|
cancel_work_sync(&l->work);
|
||||||
|
nfsd_file_dispose_list(&l->freeme);
|
||||||
|
kfree_rcu(l, rcu);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
nfsd_add_fcache_disposal(struct nfsd_fcache_disposal *l)
|
||||||
|
{
|
||||||
|
spin_lock(&laundrette_lock);
|
||||||
|
list_add_tail_rcu(&l->list, &laundrettes);
|
||||||
|
spin_unlock(&laundrette_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
nfsd_del_fcache_disposal(struct nfsd_fcache_disposal *l)
|
||||||
|
{
|
||||||
|
spin_lock(&laundrette_lock);
|
||||||
|
list_del_rcu(&l->list);
|
||||||
|
spin_unlock(&laundrette_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
nfsd_alloc_fcache_disposal_net(struct net *net)
|
||||||
|
{
|
||||||
|
struct nfsd_fcache_disposal *l;
|
||||||
|
|
||||||
|
l = nfsd_alloc_fcache_disposal(net);
|
||||||
|
if (!l)
|
||||||
|
return -ENOMEM;
|
||||||
|
nfsd_add_fcache_disposal(l);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
nfsd_free_fcache_disposal_net(struct net *net)
|
||||||
|
{
|
||||||
|
struct nfsd_fcache_disposal *l;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
list_for_each_entry_rcu(l, &laundrettes, list) {
|
||||||
|
if (l->net != net)
|
||||||
|
continue;
|
||||||
|
nfsd_del_fcache_disposal(l);
|
||||||
|
rcu_read_unlock();
|
||||||
|
nfsd_free_fcache_disposal(l);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
nfsd_file_cache_start_net(struct net *net)
|
||||||
|
{
|
||||||
|
return nfsd_alloc_fcache_disposal_net(net);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nfsd_file_cache_shutdown_net(struct net *net)
|
||||||
|
{
|
||||||
|
nfsd_file_cache_purge(net);
|
||||||
|
nfsd_free_fcache_disposal_net(net);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nfsd_file_cache_shutdown(void)
|
nfsd_file_cache_shutdown(void)
|
||||||
{
|
{
|
||||||
|
@ -706,6 +858,8 @@ nfsd_file_cache_shutdown(void)
|
||||||
nfsd_file_mark_slab = NULL;
|
nfsd_file_mark_slab = NULL;
|
||||||
kfree(nfsd_file_hashtbl);
|
kfree(nfsd_file_hashtbl);
|
||||||
nfsd_file_hashtbl = NULL;
|
nfsd_file_hashtbl = NULL;
|
||||||
|
destroy_workqueue(nfsd_filecache_wq);
|
||||||
|
nfsd_filecache_wq = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
@ -789,6 +943,7 @@ nfsd_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||||
struct nfsd_file *nf, *new;
|
struct nfsd_file *nf, *new;
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
unsigned int hashval;
|
unsigned int hashval;
|
||||||
|
bool retry = true;
|
||||||
|
|
||||||
/* FIXME: skip this if fh_dentry is already set? */
|
/* FIXME: skip this if fh_dentry is already set? */
|
||||||
status = fh_verify(rqstp, fhp, S_IFREG,
|
status = fh_verify(rqstp, fhp, S_IFREG,
|
||||||
|
@ -824,6 +979,11 @@ wait_for_construction:
|
||||||
|
|
||||||
/* Did construction of this file fail? */
|
/* Did construction of this file fail? */
|
||||||
if (!test_bit(NFSD_FILE_HASHED, &nf->nf_flags)) {
|
if (!test_bit(NFSD_FILE_HASHED, &nf->nf_flags)) {
|
||||||
|
if (!retry) {
|
||||||
|
status = nfserr_jukebox;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
retry = false;
|
||||||
nfsd_file_put_noref(nf);
|
nfsd_file_put_noref(nf);
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
|
@ -858,7 +1018,7 @@ out:
|
||||||
open_file:
|
open_file:
|
||||||
nf = new;
|
nf = new;
|
||||||
/* Take reference for the hashtable */
|
/* Take reference for the hashtable */
|
||||||
atomic_inc(&nf->nf_ref);
|
refcount_inc(&nf->nf_ref);
|
||||||
__set_bit(NFSD_FILE_HASHED, &nf->nf_flags);
|
__set_bit(NFSD_FILE_HASHED, &nf->nf_flags);
|
||||||
__set_bit(NFSD_FILE_PENDING, &nf->nf_flags);
|
__set_bit(NFSD_FILE_PENDING, &nf->nf_flags);
|
||||||
list_lru_add(&nfsd_file_lru, &nf->nf_lru);
|
list_lru_add(&nfsd_file_lru, &nf->nf_lru);
|
||||||
|
@ -867,7 +1027,8 @@ open_file:
|
||||||
nfsd_file_hashtbl[hashval].nfb_maxcount = max(nfsd_file_hashtbl[hashval].nfb_maxcount,
|
nfsd_file_hashtbl[hashval].nfb_maxcount = max(nfsd_file_hashtbl[hashval].nfb_maxcount,
|
||||||
nfsd_file_hashtbl[hashval].nfb_count);
|
nfsd_file_hashtbl[hashval].nfb_count);
|
||||||
spin_unlock(&nfsd_file_hashtbl[hashval].nfb_lock);
|
spin_unlock(&nfsd_file_hashtbl[hashval].nfb_lock);
|
||||||
atomic_long_inc(&nfsd_filecache_count);
|
if (atomic_long_inc_return(&nfsd_filecache_count) >= NFSD_FILE_LRU_THRESHOLD)
|
||||||
|
nfsd_file_gc();
|
||||||
|
|
||||||
nf->nf_mark = nfsd_file_mark_find_or_create(nf);
|
nf->nf_mark = nfsd_file_mark_find_or_create(nf);
|
||||||
if (nf->nf_mark)
|
if (nf->nf_mark)
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
*/
|
*/
|
||||||
struct nfsd_file_mark {
|
struct nfsd_file_mark {
|
||||||
struct fsnotify_mark nfm_mark;
|
struct fsnotify_mark nfm_mark;
|
||||||
atomic_t nfm_ref;
|
refcount_t nfm_ref;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -43,14 +43,17 @@ struct nfsd_file {
|
||||||
unsigned long nf_flags;
|
unsigned long nf_flags;
|
||||||
struct inode *nf_inode;
|
struct inode *nf_inode;
|
||||||
unsigned int nf_hashval;
|
unsigned int nf_hashval;
|
||||||
atomic_t nf_ref;
|
refcount_t nf_ref;
|
||||||
unsigned char nf_may;
|
unsigned char nf_may;
|
||||||
struct nfsd_file_mark *nf_mark;
|
struct nfsd_file_mark *nf_mark;
|
||||||
|
struct rw_semaphore nf_rwsem;
|
||||||
};
|
};
|
||||||
|
|
||||||
int nfsd_file_cache_init(void);
|
int nfsd_file_cache_init(void);
|
||||||
void nfsd_file_cache_purge(struct net *);
|
void nfsd_file_cache_purge(struct net *);
|
||||||
void nfsd_file_cache_shutdown(void);
|
void nfsd_file_cache_shutdown(void);
|
||||||
|
int nfsd_file_cache_start_net(struct net *net);
|
||||||
|
void nfsd_file_cache_shutdown_net(struct net *net);
|
||||||
void nfsd_file_put(struct nfsd_file *nf);
|
void nfsd_file_put(struct nfsd_file *nf);
|
||||||
struct nfsd_file *nfsd_file_get(struct nfsd_file *nf);
|
struct nfsd_file *nfsd_file_get(struct nfsd_file *nf);
|
||||||
void nfsd_file_close_inode_sync(struct inode *inode);
|
void nfsd_file_close_inode_sync(struct inode *inode);
|
||||||
|
|
|
@ -40,7 +40,7 @@ struct nfsd_net {
|
||||||
|
|
||||||
struct lock_manager nfsd4_manager;
|
struct lock_manager nfsd4_manager;
|
||||||
bool grace_ended;
|
bool grace_ended;
|
||||||
time_t boot_time;
|
time64_t boot_time;
|
||||||
|
|
||||||
/* internal mount of the "nfsd" pseudofilesystem: */
|
/* internal mount of the "nfsd" pseudofilesystem: */
|
||||||
struct vfsmount *nfsd_mnt;
|
struct vfsmount *nfsd_mnt;
|
||||||
|
@ -92,8 +92,8 @@ struct nfsd_net {
|
||||||
bool in_grace;
|
bool in_grace;
|
||||||
const struct nfsd4_client_tracking_ops *client_tracking_ops;
|
const struct nfsd4_client_tracking_ops *client_tracking_ops;
|
||||||
|
|
||||||
time_t nfsd4_lease;
|
time64_t nfsd4_lease;
|
||||||
time_t nfsd4_grace;
|
time64_t nfsd4_grace;
|
||||||
bool somebody_reclaimed;
|
bool somebody_reclaimed;
|
||||||
|
|
||||||
bool track_reclaim_completes;
|
bool track_reclaim_completes;
|
||||||
|
|
|
@ -203,7 +203,7 @@ nfsd3_proc_write(struct svc_rqst *rqstp)
|
||||||
RETURN_STATUS(nfserr_io);
|
RETURN_STATUS(nfserr_io);
|
||||||
nfserr = nfsd_write(rqstp, &resp->fh, argp->offset,
|
nfserr = nfsd_write(rqstp, &resp->fh, argp->offset,
|
||||||
rqstp->rq_vec, nvecs, &cnt,
|
rqstp->rq_vec, nvecs, &cnt,
|
||||||
resp->committed);
|
resp->committed, resp->verf);
|
||||||
resp->count = cnt;
|
resp->count = cnt;
|
||||||
RETURN_STATUS(nfserr);
|
RETURN_STATUS(nfserr);
|
||||||
}
|
}
|
||||||
|
@ -683,7 +683,8 @@ nfsd3_proc_commit(struct svc_rqst *rqstp)
|
||||||
RETURN_STATUS(nfserr_inval);
|
RETURN_STATUS(nfserr_inval);
|
||||||
|
|
||||||
fh_copy(&resp->fh, &argp->fh);
|
fh_copy(&resp->fh, &argp->fh);
|
||||||
nfserr = nfsd_commit(rqstp, &resp->fh, argp->offset, argp->count);
|
nfserr = nfsd_commit(rqstp, &resp->fh, argp->offset, argp->count,
|
||||||
|
resp->verf);
|
||||||
|
|
||||||
RETURN_STATUS(nfserr);
|
RETURN_STATUS(nfserr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,14 +32,14 @@ static u32 nfs3_ftypes[] = {
|
||||||
* XDR functions for basic NFS types
|
* XDR functions for basic NFS types
|
||||||
*/
|
*/
|
||||||
static __be32 *
|
static __be32 *
|
||||||
encode_time3(__be32 *p, struct timespec *time)
|
encode_time3(__be32 *p, struct timespec64 *time)
|
||||||
{
|
{
|
||||||
*p++ = htonl((u32) time->tv_sec); *p++ = htonl(time->tv_nsec);
|
*p++ = htonl((u32) time->tv_sec); *p++ = htonl(time->tv_nsec);
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
static __be32 *
|
static __be32 *
|
||||||
decode_time3(__be32 *p, struct timespec *time)
|
decode_time3(__be32 *p, struct timespec64 *time)
|
||||||
{
|
{
|
||||||
time->tv_sec = ntohl(*p++);
|
time->tv_sec = ntohl(*p++);
|
||||||
time->tv_nsec = ntohl(*p++);
|
time->tv_nsec = ntohl(*p++);
|
||||||
|
@ -167,7 +167,6 @@ encode_fattr3(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp,
|
||||||
struct kstat *stat)
|
struct kstat *stat)
|
||||||
{
|
{
|
||||||
struct user_namespace *userns = nfsd_user_namespace(rqstp);
|
struct user_namespace *userns = nfsd_user_namespace(rqstp);
|
||||||
struct timespec ts;
|
|
||||||
*p++ = htonl(nfs3_ftypes[(stat->mode & S_IFMT) >> 12]);
|
*p++ = htonl(nfs3_ftypes[(stat->mode & S_IFMT) >> 12]);
|
||||||
*p++ = htonl((u32) (stat->mode & S_IALLUGO));
|
*p++ = htonl((u32) (stat->mode & S_IALLUGO));
|
||||||
*p++ = htonl((u32) stat->nlink);
|
*p++ = htonl((u32) stat->nlink);
|
||||||
|
@ -183,12 +182,9 @@ encode_fattr3(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp,
|
||||||
*p++ = htonl((u32) MINOR(stat->rdev));
|
*p++ = htonl((u32) MINOR(stat->rdev));
|
||||||
p = encode_fsid(p, fhp);
|
p = encode_fsid(p, fhp);
|
||||||
p = xdr_encode_hyper(p, stat->ino);
|
p = xdr_encode_hyper(p, stat->ino);
|
||||||
ts = timespec64_to_timespec(stat->atime);
|
p = encode_time3(p, &stat->atime);
|
||||||
p = encode_time3(p, &ts);
|
p = encode_time3(p, &stat->mtime);
|
||||||
ts = timespec64_to_timespec(stat->mtime);
|
p = encode_time3(p, &stat->ctime);
|
||||||
p = encode_time3(p, &ts);
|
|
||||||
ts = timespec64_to_timespec(stat->ctime);
|
|
||||||
p = encode_time3(p, &ts);
|
|
||||||
|
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
@ -277,8 +273,8 @@ void fill_pre_wcc(struct svc_fh *fhp)
|
||||||
stat.size = inode->i_size;
|
stat.size = inode->i_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
fhp->fh_pre_mtime = timespec64_to_timespec(stat.mtime);
|
fhp->fh_pre_mtime = stat.mtime;
|
||||||
fhp->fh_pre_ctime = timespec64_to_timespec(stat.ctime);
|
fhp->fh_pre_ctime = stat.ctime;
|
||||||
fhp->fh_pre_size = stat.size;
|
fhp->fh_pre_size = stat.size;
|
||||||
fhp->fh_pre_change = nfsd4_change_attribute(&stat, inode);
|
fhp->fh_pre_change = nfsd4_change_attribute(&stat, inode);
|
||||||
fhp->fh_pre_saved = true;
|
fhp->fh_pre_saved = true;
|
||||||
|
@ -330,7 +326,7 @@ nfs3svc_decode_sattrargs(struct svc_rqst *rqstp, __be32 *p)
|
||||||
p = decode_sattr3(p, &args->attrs, nfsd_user_namespace(rqstp));
|
p = decode_sattr3(p, &args->attrs, nfsd_user_namespace(rqstp));
|
||||||
|
|
||||||
if ((args->check_guard = ntohl(*p++)) != 0) {
|
if ((args->check_guard = ntohl(*p++)) != 0) {
|
||||||
struct timespec time;
|
struct timespec64 time;
|
||||||
p = decode_time3(p, &time);
|
p = decode_time3(p, &time);
|
||||||
args->guardtime = time.tv_sec;
|
args->guardtime = time.tv_sec;
|
||||||
}
|
}
|
||||||
|
@ -751,17 +747,13 @@ int
|
||||||
nfs3svc_encode_writeres(struct svc_rqst *rqstp, __be32 *p)
|
nfs3svc_encode_writeres(struct svc_rqst *rqstp, __be32 *p)
|
||||||
{
|
{
|
||||||
struct nfsd3_writeres *resp = rqstp->rq_resp;
|
struct nfsd3_writeres *resp = rqstp->rq_resp;
|
||||||
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
|
|
||||||
__be32 verf[2];
|
|
||||||
|
|
||||||
p = encode_wcc_data(rqstp, p, &resp->fh);
|
p = encode_wcc_data(rqstp, p, &resp->fh);
|
||||||
if (resp->status == 0) {
|
if (resp->status == 0) {
|
||||||
*p++ = htonl(resp->count);
|
*p++ = htonl(resp->count);
|
||||||
*p++ = htonl(resp->committed);
|
*p++ = htonl(resp->committed);
|
||||||
/* unique identifier, y2038 overflow can be ignored */
|
*p++ = resp->verf[0];
|
||||||
nfsd_copy_boot_verifier(verf, nn);
|
*p++ = resp->verf[1];
|
||||||
*p++ = verf[0];
|
|
||||||
*p++ = verf[1];
|
|
||||||
}
|
}
|
||||||
return xdr_ressize_check(rqstp, p);
|
return xdr_ressize_check(rqstp, p);
|
||||||
}
|
}
|
||||||
|
@ -1125,16 +1117,12 @@ int
|
||||||
nfs3svc_encode_commitres(struct svc_rqst *rqstp, __be32 *p)
|
nfs3svc_encode_commitres(struct svc_rqst *rqstp, __be32 *p)
|
||||||
{
|
{
|
||||||
struct nfsd3_commitres *resp = rqstp->rq_resp;
|
struct nfsd3_commitres *resp = rqstp->rq_resp;
|
||||||
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
|
|
||||||
__be32 verf[2];
|
|
||||||
|
|
||||||
p = encode_wcc_data(rqstp, p, &resp->fh);
|
p = encode_wcc_data(rqstp, p, &resp->fh);
|
||||||
/* Write verifier */
|
/* Write verifier */
|
||||||
if (resp->status == 0) {
|
if (resp->status == 0) {
|
||||||
/* unique identifier, y2038 overflow can be ignored */
|
*p++ = resp->verf[0];
|
||||||
nfsd_copy_boot_verifier(verf, nn);
|
*p++ = resp->verf[1];
|
||||||
*p++ = verf[0];
|
|
||||||
*p++ = verf[1];
|
|
||||||
}
|
}
|
||||||
return xdr_ressize_check(rqstp, p);
|
return xdr_ressize_check(rqstp, p);
|
||||||
}
|
}
|
||||||
|
|
|
@ -823,7 +823,16 @@ static const struct rpc_program cb_program = {
|
||||||
static int max_cb_time(struct net *net)
|
static int max_cb_time(struct net *net)
|
||||||
{
|
{
|
||||||
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
|
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
|
||||||
return max(nn->nfsd4_lease/10, (time_t)1) * HZ;
|
|
||||||
|
/*
|
||||||
|
* nfsd4_lease is set to at most one hour in __nfsd4_write_time,
|
||||||
|
* so we can use 32-bit math on it. Warn if that assumption
|
||||||
|
* ever stops being true.
|
||||||
|
*/
|
||||||
|
if (WARN_ON_ONCE(nn->nfsd4_lease > 3600))
|
||||||
|
return 360 * HZ;
|
||||||
|
|
||||||
|
return max(((u32)nn->nfsd4_lease)/10, 1u) * HZ;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct workqueue_struct *callback_wq;
|
static struct workqueue_struct *callback_wq;
|
||||||
|
|
|
@ -675,7 +675,7 @@ nfsd4_cb_layout_done(struct nfsd4_callback *cb, struct rpc_task *task)
|
||||||
|
|
||||||
/* Client gets 2 lease periods to return it */
|
/* Client gets 2 lease periods to return it */
|
||||||
cutoff = ktime_add_ns(task->tk_start,
|
cutoff = ktime_add_ns(task->tk_start,
|
||||||
nn->nfsd4_lease * NSEC_PER_SEC * 2);
|
(u64)nn->nfsd4_lease * NSEC_PER_SEC * 2);
|
||||||
|
|
||||||
if (ktime_before(now, cutoff)) {
|
if (ktime_before(now, cutoff)) {
|
||||||
rpc_delay(task, HZ/100); /* 10 mili-seconds */
|
rpc_delay(task, HZ/100); /* 10 mili-seconds */
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
#include <linux/falloc.h>
|
#include <linux/falloc.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/kthread.h>
|
#include <linux/kthread.h>
|
||||||
|
#include <linux/sunrpc/addr.h>
|
||||||
|
|
||||||
#include "idmap.h"
|
#include "idmap.h"
|
||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
|
@ -232,7 +233,7 @@ do_open_lookup(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, stru
|
||||||
if (!*resfh)
|
if (!*resfh)
|
||||||
return nfserr_jukebox;
|
return nfserr_jukebox;
|
||||||
fh_init(*resfh, NFS4_FHSIZE);
|
fh_init(*resfh, NFS4_FHSIZE);
|
||||||
open->op_truncate = 0;
|
open->op_truncate = false;
|
||||||
|
|
||||||
if (open->op_create) {
|
if (open->op_create) {
|
||||||
/* FIXME: check session persistence and pnfs flags.
|
/* FIXME: check session persistence and pnfs flags.
|
||||||
|
@ -365,7 +366,7 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||||
if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL)
|
if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL)
|
||||||
return nfserr_inval;
|
return nfserr_inval;
|
||||||
|
|
||||||
open->op_created = 0;
|
open->op_created = false;
|
||||||
/*
|
/*
|
||||||
* RFC5661 18.51.3
|
* RFC5661 18.51.3
|
||||||
* Before RECLAIM_COMPLETE done, server should deny new lock
|
* Before RECLAIM_COMPLETE done, server should deny new lock
|
||||||
|
@ -503,12 +504,20 @@ nfsd4_putfh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||||
union nfsd4_op_u *u)
|
union nfsd4_op_u *u)
|
||||||
{
|
{
|
||||||
struct nfsd4_putfh *putfh = &u->putfh;
|
struct nfsd4_putfh *putfh = &u->putfh;
|
||||||
|
__be32 ret;
|
||||||
|
|
||||||
fh_put(&cstate->current_fh);
|
fh_put(&cstate->current_fh);
|
||||||
cstate->current_fh.fh_handle.fh_size = putfh->pf_fhlen;
|
cstate->current_fh.fh_handle.fh_size = putfh->pf_fhlen;
|
||||||
memcpy(&cstate->current_fh.fh_handle.fh_base, putfh->pf_fhval,
|
memcpy(&cstate->current_fh.fh_handle.fh_base, putfh->pf_fhval,
|
||||||
putfh->pf_fhlen);
|
putfh->pf_fhlen);
|
||||||
return fh_verify(rqstp, &cstate->current_fh, 0, NFSD_MAY_BYPASS_GSS);
|
ret = fh_verify(rqstp, &cstate->current_fh, 0, NFSD_MAY_BYPASS_GSS);
|
||||||
|
#ifdef CONFIG_NFSD_V4_2_INTER_SSC
|
||||||
|
if (ret == nfserr_stale && putfh->no_verify) {
|
||||||
|
SET_FH_FLAG(&cstate->current_fh, NFSD4_FH_FOREIGN);
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static __be32
|
static __be32
|
||||||
|
@ -530,9 +539,9 @@ nfsd4_restorefh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||||
return nfserr_restorefh;
|
return nfserr_restorefh;
|
||||||
|
|
||||||
fh_dup2(&cstate->current_fh, &cstate->save_fh);
|
fh_dup2(&cstate->current_fh, &cstate->save_fh);
|
||||||
if (HAS_STATE_ID(cstate, SAVED_STATE_ID_FLAG)) {
|
if (HAS_CSTATE_FLAG(cstate, SAVED_STATE_ID_FLAG)) {
|
||||||
memcpy(&cstate->current_stateid, &cstate->save_stateid, sizeof(stateid_t));
|
memcpy(&cstate->current_stateid, &cstate->save_stateid, sizeof(stateid_t));
|
||||||
SET_STATE_ID(cstate, CURRENT_STATE_ID_FLAG);
|
SET_CSTATE_FLAG(cstate, CURRENT_STATE_ID_FLAG);
|
||||||
}
|
}
|
||||||
return nfs_ok;
|
return nfs_ok;
|
||||||
}
|
}
|
||||||
|
@ -542,9 +551,9 @@ nfsd4_savefh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||||
union nfsd4_op_u *u)
|
union nfsd4_op_u *u)
|
||||||
{
|
{
|
||||||
fh_dup2(&cstate->save_fh, &cstate->current_fh);
|
fh_dup2(&cstate->save_fh, &cstate->current_fh);
|
||||||
if (HAS_STATE_ID(cstate, CURRENT_STATE_ID_FLAG)) {
|
if (HAS_CSTATE_FLAG(cstate, CURRENT_STATE_ID_FLAG)) {
|
||||||
memcpy(&cstate->save_stateid, &cstate->current_stateid, sizeof(stateid_t));
|
memcpy(&cstate->save_stateid, &cstate->current_stateid, sizeof(stateid_t));
|
||||||
SET_STATE_ID(cstate, SAVED_STATE_ID_FLAG);
|
SET_CSTATE_FLAG(cstate, SAVED_STATE_ID_FLAG);
|
||||||
}
|
}
|
||||||
return nfs_ok;
|
return nfs_ok;
|
||||||
}
|
}
|
||||||
|
@ -581,9 +590,9 @@ nfsd4_commit(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||||
{
|
{
|
||||||
struct nfsd4_commit *commit = &u->commit;
|
struct nfsd4_commit *commit = &u->commit;
|
||||||
|
|
||||||
gen_boot_verifier(&commit->co_verf, SVC_NET(rqstp));
|
|
||||||
return nfsd_commit(rqstp, &cstate->current_fh, commit->co_offset,
|
return nfsd_commit(rqstp, &cstate->current_fh, commit->co_offset,
|
||||||
commit->co_count);
|
commit->co_count,
|
||||||
|
(__be32 *)commit->co_verf.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static __be32
|
static __be32
|
||||||
|
@ -776,7 +785,7 @@ nfsd4_read(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||||
/* check stateid */
|
/* check stateid */
|
||||||
status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
|
status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
|
||||||
&read->rd_stateid, RD_STATE,
|
&read->rd_stateid, RD_STATE,
|
||||||
&read->rd_nf);
|
&read->rd_nf, NULL);
|
||||||
if (status) {
|
if (status) {
|
||||||
dprintk("NFSD: nfsd4_read: couldn't process stateid!\n");
|
dprintk("NFSD: nfsd4_read: couldn't process stateid!\n");
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -948,7 +957,7 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||||
if (setattr->sa_iattr.ia_valid & ATTR_SIZE) {
|
if (setattr->sa_iattr.ia_valid & ATTR_SIZE) {
|
||||||
status = nfs4_preprocess_stateid_op(rqstp, cstate,
|
status = nfs4_preprocess_stateid_op(rqstp, cstate,
|
||||||
&cstate->current_fh, &setattr->sa_stateid,
|
&cstate->current_fh, &setattr->sa_stateid,
|
||||||
WR_STATE, NULL);
|
WR_STATE, NULL, NULL);
|
||||||
if (status) {
|
if (status) {
|
||||||
dprintk("NFSD: nfsd4_setattr: couldn't process stateid!\n");
|
dprintk("NFSD: nfsd4_setattr: couldn't process stateid!\n");
|
||||||
return status;
|
return status;
|
||||||
|
@ -975,7 +984,7 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||||
if (status)
|
if (status)
|
||||||
goto out;
|
goto out;
|
||||||
status = nfsd_setattr(rqstp, &cstate->current_fh, &setattr->sa_iattr,
|
status = nfsd_setattr(rqstp, &cstate->current_fh, &setattr->sa_iattr,
|
||||||
0, (time_t)0);
|
0, (time64_t)0);
|
||||||
out:
|
out:
|
||||||
fh_drop_write(&cstate->current_fh);
|
fh_drop_write(&cstate->current_fh);
|
||||||
return status;
|
return status;
|
||||||
|
@ -999,22 +1008,22 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||||
trace_nfsd_write_start(rqstp, &cstate->current_fh,
|
trace_nfsd_write_start(rqstp, &cstate->current_fh,
|
||||||
write->wr_offset, cnt);
|
write->wr_offset, cnt);
|
||||||
status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
|
status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
|
||||||
stateid, WR_STATE, &nf);
|
stateid, WR_STATE, &nf, NULL);
|
||||||
if (status) {
|
if (status) {
|
||||||
dprintk("NFSD: nfsd4_write: couldn't process stateid!\n");
|
dprintk("NFSD: nfsd4_write: couldn't process stateid!\n");
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
write->wr_how_written = write->wr_stable_how;
|
write->wr_how_written = write->wr_stable_how;
|
||||||
gen_boot_verifier(&write->wr_verifier, SVC_NET(rqstp));
|
|
||||||
|
|
||||||
nvecs = svc_fill_write_vector(rqstp, write->wr_pagelist,
|
nvecs = svc_fill_write_vector(rqstp, write->wr_pagelist,
|
||||||
&write->wr_head, write->wr_buflen);
|
&write->wr_head, write->wr_buflen);
|
||||||
WARN_ON_ONCE(nvecs > ARRAY_SIZE(rqstp->rq_vec));
|
WARN_ON_ONCE(nvecs > ARRAY_SIZE(rqstp->rq_vec));
|
||||||
|
|
||||||
status = nfsd_vfs_write(rqstp, &cstate->current_fh, nf->nf_file,
|
status = nfsd_vfs_write(rqstp, &cstate->current_fh, nf,
|
||||||
write->wr_offset, rqstp->rq_vec, nvecs, &cnt,
|
write->wr_offset, rqstp->rq_vec, nvecs, &cnt,
|
||||||
write->wr_how_written);
|
write->wr_how_written,
|
||||||
|
(__be32 *)write->wr_verifier.data);
|
||||||
nfsd_file_put(nf);
|
nfsd_file_put(nf);
|
||||||
|
|
||||||
write->wr_bytes_written = cnt;
|
write->wr_bytes_written = cnt;
|
||||||
|
@ -1034,14 +1043,14 @@ nfsd4_verify_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||||
return nfserr_nofilehandle;
|
return nfserr_nofilehandle;
|
||||||
|
|
||||||
status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->save_fh,
|
status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->save_fh,
|
||||||
src_stateid, RD_STATE, src);
|
src_stateid, RD_STATE, src, NULL);
|
||||||
if (status) {
|
if (status) {
|
||||||
dprintk("NFSD: %s: couldn't process src stateid!\n", __func__);
|
dprintk("NFSD: %s: couldn't process src stateid!\n", __func__);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
|
status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
|
||||||
dst_stateid, WR_STATE, dst);
|
dst_stateid, WR_STATE, dst, NULL);
|
||||||
if (status) {
|
if (status) {
|
||||||
dprintk("NFSD: %s: couldn't process dst stateid!\n", __func__);
|
dprintk("NFSD: %s: couldn't process dst stateid!\n", __func__);
|
||||||
goto out_put_src;
|
goto out_put_src;
|
||||||
|
@ -1076,8 +1085,8 @@ nfsd4_clone(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||||
if (status)
|
if (status)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
status = nfsd4_clone_file_range(src->nf_file, clone->cl_src_pos,
|
status = nfsd4_clone_file_range(src, clone->cl_src_pos,
|
||||||
dst->nf_file, clone->cl_dst_pos, clone->cl_count,
|
dst, clone->cl_dst_pos, clone->cl_count,
|
||||||
EX_ISSYNC(cstate->current_fh.fh_export));
|
EX_ISSYNC(cstate->current_fh.fh_export));
|
||||||
|
|
||||||
nfsd_file_put(dst);
|
nfsd_file_put(dst);
|
||||||
|
@ -1135,6 +1144,207 @@ void nfsd4_shutdown_copy(struct nfs4_client *clp)
|
||||||
while ((copy = nfsd4_get_copy(clp)) != NULL)
|
while ((copy = nfsd4_get_copy(clp)) != NULL)
|
||||||
nfsd4_stop_copy(copy);
|
nfsd4_stop_copy(copy);
|
||||||
}
|
}
|
||||||
|
#ifdef CONFIG_NFSD_V4_2_INTER_SSC
|
||||||
|
|
||||||
|
extern struct file *nfs42_ssc_open(struct vfsmount *ss_mnt,
|
||||||
|
struct nfs_fh *src_fh,
|
||||||
|
nfs4_stateid *stateid);
|
||||||
|
extern void nfs42_ssc_close(struct file *filep);
|
||||||
|
|
||||||
|
extern void nfs_sb_deactive(struct super_block *sb);
|
||||||
|
|
||||||
|
#define NFSD42_INTERSSC_MOUNTOPS "vers=4.2,addr=%s,sec=sys"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Support one copy source server for now.
|
||||||
|
*/
|
||||||
|
static __be32
|
||||||
|
nfsd4_interssc_connect(struct nl4_server *nss, struct svc_rqst *rqstp,
|
||||||
|
struct vfsmount **mount)
|
||||||
|
{
|
||||||
|
struct file_system_type *type;
|
||||||
|
struct vfsmount *ss_mnt;
|
||||||
|
struct nfs42_netaddr *naddr;
|
||||||
|
struct sockaddr_storage tmp_addr;
|
||||||
|
size_t tmp_addrlen, match_netid_len = 3;
|
||||||
|
char *startsep = "", *endsep = "", *match_netid = "tcp";
|
||||||
|
char *ipaddr, *dev_name, *raw_data;
|
||||||
|
int len, raw_len;
|
||||||
|
__be32 status = nfserr_inval;
|
||||||
|
|
||||||
|
naddr = &nss->u.nl4_addr;
|
||||||
|
tmp_addrlen = rpc_uaddr2sockaddr(SVC_NET(rqstp), naddr->addr,
|
||||||
|
naddr->addr_len,
|
||||||
|
(struct sockaddr *)&tmp_addr,
|
||||||
|
sizeof(tmp_addr));
|
||||||
|
if (tmp_addrlen == 0)
|
||||||
|
goto out_err;
|
||||||
|
|
||||||
|
if (tmp_addr.ss_family == AF_INET6) {
|
||||||
|
startsep = "[";
|
||||||
|
endsep = "]";
|
||||||
|
match_netid = "tcp6";
|
||||||
|
match_netid_len = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (naddr->netid_len != match_netid_len ||
|
||||||
|
strncmp(naddr->netid, match_netid, naddr->netid_len))
|
||||||
|
goto out_err;
|
||||||
|
|
||||||
|
/* Construct the raw data for the vfs_kern_mount call */
|
||||||
|
len = RPC_MAX_ADDRBUFLEN + 1;
|
||||||
|
ipaddr = kzalloc(len, GFP_KERNEL);
|
||||||
|
if (!ipaddr)
|
||||||
|
goto out_err;
|
||||||
|
|
||||||
|
rpc_ntop((struct sockaddr *)&tmp_addr, ipaddr, len);
|
||||||
|
|
||||||
|
/* 2 for ipv6 endsep and startsep. 3 for ":/" and trailing '/0'*/
|
||||||
|
|
||||||
|
raw_len = strlen(NFSD42_INTERSSC_MOUNTOPS) + strlen(ipaddr);
|
||||||
|
raw_data = kzalloc(raw_len, GFP_KERNEL);
|
||||||
|
if (!raw_data)
|
||||||
|
goto out_free_ipaddr;
|
||||||
|
|
||||||
|
snprintf(raw_data, raw_len, NFSD42_INTERSSC_MOUNTOPS, ipaddr);
|
||||||
|
|
||||||
|
status = nfserr_nodev;
|
||||||
|
type = get_fs_type("nfs");
|
||||||
|
if (!type)
|
||||||
|
goto out_free_rawdata;
|
||||||
|
|
||||||
|
/* Set the server:<export> for the vfs_kern_mount call */
|
||||||
|
dev_name = kzalloc(len + 5, GFP_KERNEL);
|
||||||
|
if (!dev_name)
|
||||||
|
goto out_free_rawdata;
|
||||||
|
snprintf(dev_name, len + 5, "%s%s%s:/", startsep, ipaddr, endsep);
|
||||||
|
|
||||||
|
/* Use an 'internal' mount: SB_KERNMOUNT -> MNT_INTERNAL */
|
||||||
|
ss_mnt = vfs_kern_mount(type, SB_KERNMOUNT, dev_name, raw_data);
|
||||||
|
module_put(type->owner);
|
||||||
|
if (IS_ERR(ss_mnt))
|
||||||
|
goto out_free_devname;
|
||||||
|
|
||||||
|
status = 0;
|
||||||
|
*mount = ss_mnt;
|
||||||
|
|
||||||
|
out_free_devname:
|
||||||
|
kfree(dev_name);
|
||||||
|
out_free_rawdata:
|
||||||
|
kfree(raw_data);
|
||||||
|
out_free_ipaddr:
|
||||||
|
kfree(ipaddr);
|
||||||
|
out_err:
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
nfsd4_interssc_disconnect(struct vfsmount *ss_mnt)
|
||||||
|
{
|
||||||
|
nfs_sb_deactive(ss_mnt->mnt_sb);
|
||||||
|
mntput(ss_mnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nfsd4_setup_inter_ssc
|
||||||
|
*
|
||||||
|
* Verify COPY destination stateid.
|
||||||
|
* Connect to the source server with NFSv4.1.
|
||||||
|
* Create the source struct file for nfsd_copy_range.
|
||||||
|
* Called with COPY cstate:
|
||||||
|
* SAVED_FH: source filehandle
|
||||||
|
* CURRENT_FH: destination filehandle
|
||||||
|
*/
|
||||||
|
static __be32
|
||||||
|
nfsd4_setup_inter_ssc(struct svc_rqst *rqstp,
|
||||||
|
struct nfsd4_compound_state *cstate,
|
||||||
|
struct nfsd4_copy *copy, struct vfsmount **mount)
|
||||||
|
{
|
||||||
|
struct svc_fh *s_fh = NULL;
|
||||||
|
stateid_t *s_stid = ©->cp_src_stateid;
|
||||||
|
__be32 status = nfserr_inval;
|
||||||
|
|
||||||
|
/* Verify the destination stateid and set dst struct file*/
|
||||||
|
status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
|
||||||
|
©->cp_dst_stateid,
|
||||||
|
WR_STATE, ©->nf_dst, NULL);
|
||||||
|
if (status)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
status = nfsd4_interssc_connect(©->cp_src, rqstp, mount);
|
||||||
|
if (status)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
s_fh = &cstate->save_fh;
|
||||||
|
|
||||||
|
copy->c_fh.size = s_fh->fh_handle.fh_size;
|
||||||
|
memcpy(copy->c_fh.data, &s_fh->fh_handle.fh_base, copy->c_fh.size);
|
||||||
|
copy->stateid.seqid = cpu_to_be32(s_stid->si_generation);
|
||||||
|
memcpy(copy->stateid.other, (void *)&s_stid->si_opaque,
|
||||||
|
sizeof(stateid_opaque_t));
|
||||||
|
|
||||||
|
status = 0;
|
||||||
|
out:
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
nfsd4_cleanup_inter_ssc(struct vfsmount *ss_mnt, struct nfsd_file *src,
|
||||||
|
struct nfsd_file *dst)
|
||||||
|
{
|
||||||
|
nfs42_ssc_close(src->nf_file);
|
||||||
|
nfsd_file_put(src);
|
||||||
|
nfsd_file_put(dst);
|
||||||
|
mntput(ss_mnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else /* CONFIG_NFSD_V4_2_INTER_SSC */
|
||||||
|
|
||||||
|
static __be32
|
||||||
|
nfsd4_setup_inter_ssc(struct svc_rqst *rqstp,
|
||||||
|
struct nfsd4_compound_state *cstate,
|
||||||
|
struct nfsd4_copy *copy,
|
||||||
|
struct vfsmount **mount)
|
||||||
|
{
|
||||||
|
*mount = NULL;
|
||||||
|
return nfserr_inval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
nfsd4_cleanup_inter_ssc(struct vfsmount *ss_mnt, struct nfsd_file *src,
|
||||||
|
struct nfsd_file *dst)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
nfsd4_interssc_disconnect(struct vfsmount *ss_mnt)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct file *nfs42_ssc_open(struct vfsmount *ss_mnt,
|
||||||
|
struct nfs_fh *src_fh,
|
||||||
|
nfs4_stateid *stateid)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_NFSD_V4_2_INTER_SSC */
|
||||||
|
|
||||||
|
static __be32
|
||||||
|
nfsd4_setup_intra_ssc(struct svc_rqst *rqstp,
|
||||||
|
struct nfsd4_compound_state *cstate,
|
||||||
|
struct nfsd4_copy *copy)
|
||||||
|
{
|
||||||
|
return nfsd4_verify_copy(rqstp, cstate, ©->cp_src_stateid,
|
||||||
|
©->nf_src, ©->cp_dst_stateid,
|
||||||
|
©->nf_dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
nfsd4_cleanup_intra_ssc(struct nfsd_file *src, struct nfsd_file *dst)
|
||||||
|
{
|
||||||
|
nfsd_file_put(src);
|
||||||
|
nfsd_file_put(dst);
|
||||||
|
}
|
||||||
|
|
||||||
static void nfsd4_cb_offload_release(struct nfsd4_callback *cb)
|
static void nfsd4_cb_offload_release(struct nfsd4_callback *cb)
|
||||||
{
|
{
|
||||||
|
@ -1200,12 +1410,16 @@ static __be32 nfsd4_do_copy(struct nfsd4_copy *copy, bool sync)
|
||||||
status = nfs_ok;
|
status = nfs_ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
nfsd_file_put(copy->nf_src);
|
if (!copy->cp_intra) /* Inter server SSC */
|
||||||
nfsd_file_put(copy->nf_dst);
|
nfsd4_cleanup_inter_ssc(copy->ss_mnt, copy->nf_src,
|
||||||
|
copy->nf_dst);
|
||||||
|
else
|
||||||
|
nfsd4_cleanup_intra_ssc(copy->nf_src, copy->nf_dst);
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dup_copy_fields(struct nfsd4_copy *src, struct nfsd4_copy *dst)
|
static int dup_copy_fields(struct nfsd4_copy *src, struct nfsd4_copy *dst)
|
||||||
{
|
{
|
||||||
dst->cp_src_pos = src->cp_src_pos;
|
dst->cp_src_pos = src->cp_src_pos;
|
||||||
dst->cp_dst_pos = src->cp_dst_pos;
|
dst->cp_dst_pos = src->cp_dst_pos;
|
||||||
|
@ -1215,15 +1429,25 @@ static void dup_copy_fields(struct nfsd4_copy *src, struct nfsd4_copy *dst)
|
||||||
memcpy(&dst->fh, &src->fh, sizeof(src->fh));
|
memcpy(&dst->fh, &src->fh, sizeof(src->fh));
|
||||||
dst->cp_clp = src->cp_clp;
|
dst->cp_clp = src->cp_clp;
|
||||||
dst->nf_dst = nfsd_file_get(src->nf_dst);
|
dst->nf_dst = nfsd_file_get(src->nf_dst);
|
||||||
dst->nf_src = nfsd_file_get(src->nf_src);
|
dst->cp_intra = src->cp_intra;
|
||||||
|
if (src->cp_intra) /* for inter, file_src doesn't exist yet */
|
||||||
|
dst->nf_src = nfsd_file_get(src->nf_src);
|
||||||
|
|
||||||
memcpy(&dst->cp_stateid, &src->cp_stateid, sizeof(src->cp_stateid));
|
memcpy(&dst->cp_stateid, &src->cp_stateid, sizeof(src->cp_stateid));
|
||||||
|
memcpy(&dst->cp_src, &src->cp_src, sizeof(struct nl4_server));
|
||||||
|
memcpy(&dst->stateid, &src->stateid, sizeof(src->stateid));
|
||||||
|
memcpy(&dst->c_fh, &src->c_fh, sizeof(src->c_fh));
|
||||||
|
dst->ss_mnt = src->ss_mnt;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cleanup_async_copy(struct nfsd4_copy *copy)
|
static void cleanup_async_copy(struct nfsd4_copy *copy)
|
||||||
{
|
{
|
||||||
nfs4_free_cp_state(copy);
|
nfs4_free_copy_state(copy);
|
||||||
nfsd_file_put(copy->nf_dst);
|
nfsd_file_put(copy->nf_dst);
|
||||||
nfsd_file_put(copy->nf_src);
|
if (copy->cp_intra)
|
||||||
|
nfsd_file_put(copy->nf_src);
|
||||||
spin_lock(©->cp_clp->async_lock);
|
spin_lock(©->cp_clp->async_lock);
|
||||||
list_del(©->copies);
|
list_del(©->copies);
|
||||||
spin_unlock(©->cp_clp->async_lock);
|
spin_unlock(©->cp_clp->async_lock);
|
||||||
|
@ -1235,7 +1459,24 @@ static int nfsd4_do_async_copy(void *data)
|
||||||
struct nfsd4_copy *copy = (struct nfsd4_copy *)data;
|
struct nfsd4_copy *copy = (struct nfsd4_copy *)data;
|
||||||
struct nfsd4_copy *cb_copy;
|
struct nfsd4_copy *cb_copy;
|
||||||
|
|
||||||
|
if (!copy->cp_intra) { /* Inter server SSC */
|
||||||
|
copy->nf_src = kzalloc(sizeof(struct nfsd_file), GFP_KERNEL);
|
||||||
|
if (!copy->nf_src) {
|
||||||
|
copy->nfserr = nfserr_serverfault;
|
||||||
|
nfsd4_interssc_disconnect(copy->ss_mnt);
|
||||||
|
goto do_callback;
|
||||||
|
}
|
||||||
|
copy->nf_src->nf_file = nfs42_ssc_open(copy->ss_mnt, ©->c_fh,
|
||||||
|
©->stateid);
|
||||||
|
if (IS_ERR(copy->nf_src->nf_file)) {
|
||||||
|
copy->nfserr = nfserr_offload_denied;
|
||||||
|
nfsd4_interssc_disconnect(copy->ss_mnt);
|
||||||
|
goto do_callback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
copy->nfserr = nfsd4_do_copy(copy, 0);
|
copy->nfserr = nfsd4_do_copy(copy, 0);
|
||||||
|
do_callback:
|
||||||
cb_copy = kzalloc(sizeof(struct nfsd4_copy), GFP_KERNEL);
|
cb_copy = kzalloc(sizeof(struct nfsd4_copy), GFP_KERNEL);
|
||||||
if (!cb_copy)
|
if (!cb_copy)
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -1247,6 +1488,8 @@ static int nfsd4_do_async_copy(void *data)
|
||||||
&nfsd4_cb_offload_ops, NFSPROC4_CLNT_CB_OFFLOAD);
|
&nfsd4_cb_offload_ops, NFSPROC4_CLNT_CB_OFFLOAD);
|
||||||
nfsd4_run_cb(&cb_copy->cp_cb);
|
nfsd4_run_cb(&cb_copy->cp_cb);
|
||||||
out:
|
out:
|
||||||
|
if (!copy->cp_intra)
|
||||||
|
kfree(copy->nf_src);
|
||||||
cleanup_async_copy(copy);
|
cleanup_async_copy(copy);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1259,11 +1502,20 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||||
__be32 status;
|
__be32 status;
|
||||||
struct nfsd4_copy *async_copy = NULL;
|
struct nfsd4_copy *async_copy = NULL;
|
||||||
|
|
||||||
status = nfsd4_verify_copy(rqstp, cstate, ©->cp_src_stateid,
|
if (!copy->cp_intra) { /* Inter server SSC */
|
||||||
©->nf_src, ©->cp_dst_stateid,
|
if (!inter_copy_offload_enable || copy->cp_synchronous) {
|
||||||
©->nf_dst);
|
status = nfserr_notsupp;
|
||||||
if (status)
|
goto out;
|
||||||
goto out;
|
}
|
||||||
|
status = nfsd4_setup_inter_ssc(rqstp, cstate, copy,
|
||||||
|
©->ss_mnt);
|
||||||
|
if (status)
|
||||||
|
return nfserr_offload_denied;
|
||||||
|
} else {
|
||||||
|
status = nfsd4_setup_intra_ssc(rqstp, cstate, copy);
|
||||||
|
if (status)
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
copy->cp_clp = cstate->clp;
|
copy->cp_clp = cstate->clp;
|
||||||
memcpy(©->fh, &cstate->current_fh.fh_handle,
|
memcpy(©->fh, &cstate->current_fh.fh_handle,
|
||||||
|
@ -1274,15 +1526,15 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||||
status = nfserrno(-ENOMEM);
|
status = nfserrno(-ENOMEM);
|
||||||
async_copy = kzalloc(sizeof(struct nfsd4_copy), GFP_KERNEL);
|
async_copy = kzalloc(sizeof(struct nfsd4_copy), GFP_KERNEL);
|
||||||
if (!async_copy)
|
if (!async_copy)
|
||||||
goto out;
|
goto out_err;
|
||||||
if (!nfs4_init_cp_state(nn, copy)) {
|
if (!nfs4_init_copy_state(nn, copy))
|
||||||
kfree(async_copy);
|
goto out_err;
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
refcount_set(&async_copy->refcount, 1);
|
refcount_set(&async_copy->refcount, 1);
|
||||||
memcpy(©->cp_res.cb_stateid, ©->cp_stateid,
|
memcpy(©->cp_res.cb_stateid, ©->cp_stateid,
|
||||||
sizeof(copy->cp_stateid));
|
sizeof(copy->cp_stateid));
|
||||||
dup_copy_fields(copy, async_copy);
|
status = dup_copy_fields(copy, async_copy);
|
||||||
|
if (status)
|
||||||
|
goto out_err;
|
||||||
async_copy->copy_task = kthread_create(nfsd4_do_async_copy,
|
async_copy->copy_task = kthread_create(nfsd4_do_async_copy,
|
||||||
async_copy, "%s", "copy thread");
|
async_copy, "%s", "copy thread");
|
||||||
if (IS_ERR(async_copy->copy_task))
|
if (IS_ERR(async_copy->copy_task))
|
||||||
|
@ -1293,13 +1545,17 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||||
spin_unlock(&async_copy->cp_clp->async_lock);
|
spin_unlock(&async_copy->cp_clp->async_lock);
|
||||||
wake_up_process(async_copy->copy_task);
|
wake_up_process(async_copy->copy_task);
|
||||||
status = nfs_ok;
|
status = nfs_ok;
|
||||||
} else
|
} else {
|
||||||
status = nfsd4_do_copy(copy, 1);
|
status = nfsd4_do_copy(copy, 1);
|
||||||
|
}
|
||||||
out:
|
out:
|
||||||
return status;
|
return status;
|
||||||
out_err:
|
out_err:
|
||||||
if (async_copy)
|
if (async_copy)
|
||||||
cleanup_async_copy(async_copy);
|
cleanup_async_copy(async_copy);
|
||||||
|
status = nfserrno(-ENOMEM);
|
||||||
|
if (!copy->cp_intra)
|
||||||
|
nfsd4_interssc_disconnect(copy->ss_mnt);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1310,7 +1566,7 @@ find_async_copy(struct nfs4_client *clp, stateid_t *stateid)
|
||||||
|
|
||||||
spin_lock(&clp->async_lock);
|
spin_lock(&clp->async_lock);
|
||||||
list_for_each_entry(copy, &clp->async_copies, copies) {
|
list_for_each_entry(copy, &clp->async_copies, copies) {
|
||||||
if (memcmp(©->cp_stateid, stateid, NFS4_STATEID_SIZE))
|
if (memcmp(©->cp_stateid.stid, stateid, NFS4_STATEID_SIZE))
|
||||||
continue;
|
continue;
|
||||||
refcount_inc(©->refcount);
|
refcount_inc(©->refcount);
|
||||||
spin_unlock(&clp->async_lock);
|
spin_unlock(&clp->async_lock);
|
||||||
|
@ -1326,16 +1582,61 @@ nfsd4_offload_cancel(struct svc_rqst *rqstp,
|
||||||
union nfsd4_op_u *u)
|
union nfsd4_op_u *u)
|
||||||
{
|
{
|
||||||
struct nfsd4_offload_status *os = &u->offload_status;
|
struct nfsd4_offload_status *os = &u->offload_status;
|
||||||
__be32 status = 0;
|
|
||||||
struct nfsd4_copy *copy;
|
struct nfsd4_copy *copy;
|
||||||
struct nfs4_client *clp = cstate->clp;
|
struct nfs4_client *clp = cstate->clp;
|
||||||
|
|
||||||
copy = find_async_copy(clp, &os->stateid);
|
copy = find_async_copy(clp, &os->stateid);
|
||||||
if (copy)
|
if (!copy) {
|
||||||
nfsd4_stop_copy(copy);
|
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
|
||||||
else
|
|
||||||
status = nfserr_bad_stateid;
|
|
||||||
|
|
||||||
|
return manage_cpntf_state(nn, &os->stateid, clp, NULL);
|
||||||
|
} else
|
||||||
|
nfsd4_stop_copy(copy);
|
||||||
|
|
||||||
|
return nfs_ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
static __be32
|
||||||
|
nfsd4_copy_notify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||||
|
union nfsd4_op_u *u)
|
||||||
|
{
|
||||||
|
struct nfsd4_copy_notify *cn = &u->copy_notify;
|
||||||
|
__be32 status;
|
||||||
|
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
|
||||||
|
struct nfs4_stid *stid;
|
||||||
|
struct nfs4_cpntf_state *cps;
|
||||||
|
struct nfs4_client *clp = cstate->clp;
|
||||||
|
|
||||||
|
status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
|
||||||
|
&cn->cpn_src_stateid, RD_STATE, NULL,
|
||||||
|
&stid);
|
||||||
|
if (status)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
cn->cpn_sec = nn->nfsd4_lease;
|
||||||
|
cn->cpn_nsec = 0;
|
||||||
|
|
||||||
|
status = nfserrno(-ENOMEM);
|
||||||
|
cps = nfs4_alloc_init_cpntf_state(nn, stid);
|
||||||
|
if (!cps)
|
||||||
|
goto out;
|
||||||
|
memcpy(&cn->cpn_cnr_stateid, &cps->cp_stateid.stid, sizeof(stateid_t));
|
||||||
|
memcpy(&cps->cp_p_stateid, &stid->sc_stateid, sizeof(stateid_t));
|
||||||
|
memcpy(&cps->cp_p_clid, &clp->cl_clientid, sizeof(clientid_t));
|
||||||
|
|
||||||
|
/* For now, only return one server address in cpn_src, the
|
||||||
|
* address used by the client to connect to this server.
|
||||||
|
*/
|
||||||
|
cn->cpn_src.nl4_type = NL4_NETADDR;
|
||||||
|
status = nfsd4_set_netaddr((struct sockaddr *)&rqstp->rq_daddr,
|
||||||
|
&cn->cpn_src.u.nl4_addr);
|
||||||
|
WARN_ON_ONCE(status);
|
||||||
|
if (status) {
|
||||||
|
nfs4_put_cpntf_state(nn, cps);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
nfs4_put_stid(stid);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1348,7 +1649,7 @@ nfsd4_fallocate(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||||
|
|
||||||
status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
|
status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
|
||||||
&fallocate->falloc_stateid,
|
&fallocate->falloc_stateid,
|
||||||
WR_STATE, &nf);
|
WR_STATE, &nf, NULL);
|
||||||
if (status != nfs_ok) {
|
if (status != nfs_ok) {
|
||||||
dprintk("NFSD: nfsd4_fallocate: couldn't process stateid!\n");
|
dprintk("NFSD: nfsd4_fallocate: couldn't process stateid!\n");
|
||||||
return status;
|
return status;
|
||||||
|
@ -1407,7 +1708,7 @@ nfsd4_seek(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||||
|
|
||||||
status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
|
status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
|
||||||
&seek->seek_stateid,
|
&seek->seek_stateid,
|
||||||
RD_STATE, &nf);
|
RD_STATE, &nf, NULL);
|
||||||
if (status) {
|
if (status) {
|
||||||
dprintk("NFSD: nfsd4_seek: couldn't process stateid!\n");
|
dprintk("NFSD: nfsd4_seek: couldn't process stateid!\n");
|
||||||
return status;
|
return status;
|
||||||
|
@ -1912,6 +2213,45 @@ static void svcxdr_init_encode(struct svc_rqst *rqstp,
|
||||||
- rqstp->rq_auth_slack;
|
- rqstp->rq_auth_slack;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_NFSD_V4_2_INTER_SSC
|
||||||
|
static void
|
||||||
|
check_if_stalefh_allowed(struct nfsd4_compoundargs *args)
|
||||||
|
{
|
||||||
|
struct nfsd4_op *op, *current_op = NULL, *saved_op = NULL;
|
||||||
|
struct nfsd4_copy *copy;
|
||||||
|
struct nfsd4_putfh *putfh;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* traverse all operation and if it's a COPY compound, mark the
|
||||||
|
* source filehandle to skip verification
|
||||||
|
*/
|
||||||
|
for (i = 0; i < args->opcnt; i++) {
|
||||||
|
op = &args->ops[i];
|
||||||
|
if (op->opnum == OP_PUTFH)
|
||||||
|
current_op = op;
|
||||||
|
else if (op->opnum == OP_SAVEFH)
|
||||||
|
saved_op = current_op;
|
||||||
|
else if (op->opnum == OP_RESTOREFH)
|
||||||
|
current_op = saved_op;
|
||||||
|
else if (op->opnum == OP_COPY) {
|
||||||
|
copy = (struct nfsd4_copy *)&op->u;
|
||||||
|
if (!saved_op) {
|
||||||
|
op->status = nfserr_nofilehandle;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
putfh = (struct nfsd4_putfh *)&saved_op->u;
|
||||||
|
if (!copy->cp_intra)
|
||||||
|
putfh->no_verify = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static void
|
||||||
|
check_if_stalefh_allowed(struct nfsd4_compoundargs *args)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* COMPOUND call.
|
* COMPOUND call.
|
||||||
*/
|
*/
|
||||||
|
@ -1960,6 +2300,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp)
|
||||||
resp->opcnt = 1;
|
resp->opcnt = 1;
|
||||||
goto encode_op;
|
goto encode_op;
|
||||||
}
|
}
|
||||||
|
check_if_stalefh_allowed(args);
|
||||||
|
|
||||||
trace_nfsd_compound(rqstp, args->opcnt);
|
trace_nfsd_compound(rqstp, args->opcnt);
|
||||||
while (!status && resp->opcnt < args->opcnt) {
|
while (!status && resp->opcnt < args->opcnt) {
|
||||||
|
@ -1975,13 +2316,14 @@ nfsd4_proc_compound(struct svc_rqst *rqstp)
|
||||||
op->status = nfsd4_open_omfg(rqstp, cstate, op);
|
op->status = nfsd4_open_omfg(rqstp, cstate, op);
|
||||||
goto encode_op;
|
goto encode_op;
|
||||||
}
|
}
|
||||||
|
if (!current_fh->fh_dentry &&
|
||||||
if (!current_fh->fh_dentry) {
|
!HAS_FH_FLAG(current_fh, NFSD4_FH_FOREIGN)) {
|
||||||
if (!(op->opdesc->op_flags & ALLOWED_WITHOUT_FH)) {
|
if (!(op->opdesc->op_flags & ALLOWED_WITHOUT_FH)) {
|
||||||
op->status = nfserr_nofilehandle;
|
op->status = nfserr_nofilehandle;
|
||||||
goto encode_op;
|
goto encode_op;
|
||||||
}
|
}
|
||||||
} else if (current_fh->fh_export->ex_fslocs.migrated &&
|
} else if (current_fh->fh_export &&
|
||||||
|
current_fh->fh_export->ex_fslocs.migrated &&
|
||||||
!(op->opdesc->op_flags & ALLOWED_ON_ABSENT_FS)) {
|
!(op->opdesc->op_flags & ALLOWED_ON_ABSENT_FS)) {
|
||||||
op->status = nfserr_moved;
|
op->status = nfserr_moved;
|
||||||
goto encode_op;
|
goto encode_op;
|
||||||
|
@ -2025,7 +2367,8 @@ nfsd4_proc_compound(struct svc_rqst *rqstp)
|
||||||
if (op->opdesc->op_flags & OP_CLEAR_STATEID)
|
if (op->opdesc->op_flags & OP_CLEAR_STATEID)
|
||||||
clear_current_stateid(cstate);
|
clear_current_stateid(cstate);
|
||||||
|
|
||||||
if (need_wrongsec_check(rqstp))
|
if (current_fh->fh_export &&
|
||||||
|
need_wrongsec_check(rqstp))
|
||||||
op->status = check_nfsd_access(current_fh->fh_export, rqstp);
|
op->status = check_nfsd_access(current_fh->fh_export, rqstp);
|
||||||
}
|
}
|
||||||
encode_op:
|
encode_op:
|
||||||
|
@ -2292,6 +2635,21 @@ static inline u32 nfsd4_offload_status_rsize(struct svc_rqst *rqstp,
|
||||||
1 /* osr_complete<1> optional 0 for now */) * sizeof(__be32);
|
1 /* osr_complete<1> optional 0 for now */) * sizeof(__be32);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline u32 nfsd4_copy_notify_rsize(struct svc_rqst *rqstp,
|
||||||
|
struct nfsd4_op *op)
|
||||||
|
{
|
||||||
|
return (op_encode_hdr_size +
|
||||||
|
3 /* cnr_lease_time */ +
|
||||||
|
1 /* We support one cnr_source_server */ +
|
||||||
|
1 /* cnr_stateid seq */ +
|
||||||
|
op_encode_stateid_maxsz /* cnr_stateid */ +
|
||||||
|
1 /* num cnr_source_server*/ +
|
||||||
|
1 /* nl4_type */ +
|
||||||
|
1 /* nl4 size */ +
|
||||||
|
XDR_QUADLEN(NFS4_OPAQUE_LIMIT) /*nl4_loc + nl4_loc_sz */)
|
||||||
|
* sizeof(__be32);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_NFSD_PNFS
|
#ifdef CONFIG_NFSD_PNFS
|
||||||
static inline u32 nfsd4_getdeviceinfo_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
|
static inline u32 nfsd4_getdeviceinfo_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
|
||||||
{
|
{
|
||||||
|
@ -2716,6 +3074,12 @@ static const struct nfsd4_operation nfsd4_ops[] = {
|
||||||
.op_name = "OP_OFFLOAD_CANCEL",
|
.op_name = "OP_OFFLOAD_CANCEL",
|
||||||
.op_rsize_bop = nfsd4_only_status_rsize,
|
.op_rsize_bop = nfsd4_only_status_rsize,
|
||||||
},
|
},
|
||||||
|
[OP_COPY_NOTIFY] = {
|
||||||
|
.op_func = nfsd4_copy_notify,
|
||||||
|
.op_flags = OP_MODIFIES_SOMETHING,
|
||||||
|
.op_name = "OP_COPY_NOTIFY",
|
||||||
|
.op_rsize_bop = nfsd4_copy_notify_rsize,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1445,7 +1445,7 @@ nfsd4_cld_grace_done_v0(struct nfsd_net *nn)
|
||||||
}
|
}
|
||||||
|
|
||||||
cup->cu_u.cu_msg.cm_cmd = Cld_GraceDone;
|
cup->cu_u.cu_msg.cm_cmd = Cld_GraceDone;
|
||||||
cup->cu_u.cu_msg.cm_u.cm_gracetime = (int64_t)nn->boot_time;
|
cup->cu_u.cu_msg.cm_u.cm_gracetime = nn->boot_time;
|
||||||
ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg);
|
ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
ret = cup->cu_u.cu_msg.cm_status;
|
ret = cup->cu_u.cu_msg.cm_status;
|
||||||
|
@ -1782,7 +1782,7 @@ nfsd4_cltrack_client_has_session(struct nfs4_client *clp)
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
nfsd4_cltrack_grace_start(time_t grace_start)
|
nfsd4_cltrack_grace_start(time64_t grace_start)
|
||||||
{
|
{
|
||||||
int copied;
|
int copied;
|
||||||
size_t len;
|
size_t len;
|
||||||
|
@ -1795,7 +1795,7 @@ nfsd4_cltrack_grace_start(time_t grace_start)
|
||||||
if (!result)
|
if (!result)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
copied = snprintf(result, len, GRACE_START_ENV_PREFIX "%ld",
|
copied = snprintf(result, len, GRACE_START_ENV_PREFIX "%lld",
|
||||||
grace_start);
|
grace_start);
|
||||||
if (copied >= len) {
|
if (copied >= len) {
|
||||||
/* just return nothing if output was truncated */
|
/* just return nothing if output was truncated */
|
||||||
|
@ -2004,7 +2004,7 @@ nfsd4_umh_cltrack_grace_done(struct nfsd_net *nn)
|
||||||
char *legacy;
|
char *legacy;
|
||||||
char timestr[22]; /* FIXME: better way to determine max size? */
|
char timestr[22]; /* FIXME: better way to determine max size? */
|
||||||
|
|
||||||
sprintf(timestr, "%ld", nn->boot_time);
|
sprintf(timestr, "%lld", nn->boot_time);
|
||||||
legacy = nfsd4_cltrack_legacy_topdir();
|
legacy = nfsd4_cltrack_legacy_topdir();
|
||||||
nfsd4_umh_cltrack_upcall("gracedone", timestr, legacy, NULL);
|
nfsd4_umh_cltrack_upcall("gracedone", timestr, legacy, NULL);
|
||||||
kfree(legacy);
|
kfree(legacy);
|
||||||
|
|
|
@ -80,6 +80,7 @@ static u64 current_sessionid = 1;
|
||||||
static bool check_for_locks(struct nfs4_file *fp, struct nfs4_lockowner *lowner);
|
static bool check_for_locks(struct nfs4_file *fp, struct nfs4_lockowner *lowner);
|
||||||
static void nfs4_free_ol_stateid(struct nfs4_stid *stid);
|
static void nfs4_free_ol_stateid(struct nfs4_stid *stid);
|
||||||
void nfsd4_end_grace(struct nfsd_net *nn);
|
void nfsd4_end_grace(struct nfsd_net *nn);
|
||||||
|
static void _free_cpntf_state_locked(struct nfsd_net *nn, struct nfs4_cpntf_state *cps);
|
||||||
|
|
||||||
/* Locking: */
|
/* Locking: */
|
||||||
|
|
||||||
|
@ -170,7 +171,7 @@ renew_client_locked(struct nfs4_client *clp)
|
||||||
clp->cl_clientid.cl_boot,
|
clp->cl_clientid.cl_boot,
|
||||||
clp->cl_clientid.cl_id);
|
clp->cl_clientid.cl_id);
|
||||||
list_move_tail(&clp->cl_lru, &nn->client_lru);
|
list_move_tail(&clp->cl_lru, &nn->client_lru);
|
||||||
clp->cl_time = get_seconds();
|
clp->cl_time = ktime_get_boottime_seconds();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void put_client_renew_locked(struct nfs4_client *clp)
|
static void put_client_renew_locked(struct nfs4_client *clp)
|
||||||
|
@ -722,6 +723,7 @@ struct nfs4_stid *nfs4_alloc_stid(struct nfs4_client *cl, struct kmem_cache *sla
|
||||||
/* Will be incremented before return to client: */
|
/* Will be incremented before return to client: */
|
||||||
refcount_set(&stid->sc_count, 1);
|
refcount_set(&stid->sc_count, 1);
|
||||||
spin_lock_init(&stid->sc_lock);
|
spin_lock_init(&stid->sc_lock);
|
||||||
|
INIT_LIST_HEAD(&stid->sc_cp_list);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* It shouldn't be a problem to reuse an opaque stateid value.
|
* It shouldn't be a problem to reuse an opaque stateid value.
|
||||||
|
@ -741,30 +743,76 @@ out_free:
|
||||||
/*
|
/*
|
||||||
* Create a unique stateid_t to represent each COPY.
|
* Create a unique stateid_t to represent each COPY.
|
||||||
*/
|
*/
|
||||||
int nfs4_init_cp_state(struct nfsd_net *nn, struct nfsd4_copy *copy)
|
static int nfs4_init_cp_state(struct nfsd_net *nn, copy_stateid_t *stid,
|
||||||
|
unsigned char sc_type)
|
||||||
{
|
{
|
||||||
int new_id;
|
int new_id;
|
||||||
|
|
||||||
|
stid->stid.si_opaque.so_clid.cl_boot = (u32)nn->boot_time;
|
||||||
|
stid->stid.si_opaque.so_clid.cl_id = nn->s2s_cp_cl_id;
|
||||||
|
stid->sc_type = sc_type;
|
||||||
|
|
||||||
idr_preload(GFP_KERNEL);
|
idr_preload(GFP_KERNEL);
|
||||||
spin_lock(&nn->s2s_cp_lock);
|
spin_lock(&nn->s2s_cp_lock);
|
||||||
new_id = idr_alloc_cyclic(&nn->s2s_cp_stateids, copy, 0, 0, GFP_NOWAIT);
|
new_id = idr_alloc_cyclic(&nn->s2s_cp_stateids, stid, 0, 0, GFP_NOWAIT);
|
||||||
|
stid->stid.si_opaque.so_id = new_id;
|
||||||
spin_unlock(&nn->s2s_cp_lock);
|
spin_unlock(&nn->s2s_cp_lock);
|
||||||
idr_preload_end();
|
idr_preload_end();
|
||||||
if (new_id < 0)
|
if (new_id < 0)
|
||||||
return 0;
|
return 0;
|
||||||
copy->cp_stateid.si_opaque.so_id = new_id;
|
|
||||||
copy->cp_stateid.si_opaque.so_clid.cl_boot = nn->boot_time;
|
|
||||||
copy->cp_stateid.si_opaque.so_clid.cl_id = nn->s2s_cp_cl_id;
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nfs4_free_cp_state(struct nfsd4_copy *copy)
|
int nfs4_init_copy_state(struct nfsd_net *nn, struct nfsd4_copy *copy)
|
||||||
|
{
|
||||||
|
return nfs4_init_cp_state(nn, ©->cp_stateid, NFS4_COPY_STID);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct nfs4_cpntf_state *nfs4_alloc_init_cpntf_state(struct nfsd_net *nn,
|
||||||
|
struct nfs4_stid *p_stid)
|
||||||
|
{
|
||||||
|
struct nfs4_cpntf_state *cps;
|
||||||
|
|
||||||
|
cps = kzalloc(sizeof(struct nfs4_cpntf_state), GFP_KERNEL);
|
||||||
|
if (!cps)
|
||||||
|
return NULL;
|
||||||
|
cps->cpntf_time = ktime_get_boottime_seconds();
|
||||||
|
refcount_set(&cps->cp_stateid.sc_count, 1);
|
||||||
|
if (!nfs4_init_cp_state(nn, &cps->cp_stateid, NFS4_COPYNOTIFY_STID))
|
||||||
|
goto out_free;
|
||||||
|
spin_lock(&nn->s2s_cp_lock);
|
||||||
|
list_add(&cps->cp_list, &p_stid->sc_cp_list);
|
||||||
|
spin_unlock(&nn->s2s_cp_lock);
|
||||||
|
return cps;
|
||||||
|
out_free:
|
||||||
|
kfree(cps);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfs4_free_copy_state(struct nfsd4_copy *copy)
|
||||||
{
|
{
|
||||||
struct nfsd_net *nn;
|
struct nfsd_net *nn;
|
||||||
|
|
||||||
|
WARN_ON_ONCE(copy->cp_stateid.sc_type != NFS4_COPY_STID);
|
||||||
nn = net_generic(copy->cp_clp->net, nfsd_net_id);
|
nn = net_generic(copy->cp_clp->net, nfsd_net_id);
|
||||||
spin_lock(&nn->s2s_cp_lock);
|
spin_lock(&nn->s2s_cp_lock);
|
||||||
idr_remove(&nn->s2s_cp_stateids, copy->cp_stateid.si_opaque.so_id);
|
idr_remove(&nn->s2s_cp_stateids,
|
||||||
|
copy->cp_stateid.stid.si_opaque.so_id);
|
||||||
|
spin_unlock(&nn->s2s_cp_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nfs4_free_cpntf_statelist(struct net *net, struct nfs4_stid *stid)
|
||||||
|
{
|
||||||
|
struct nfs4_cpntf_state *cps;
|
||||||
|
struct nfsd_net *nn;
|
||||||
|
|
||||||
|
nn = net_generic(net, nfsd_net_id);
|
||||||
|
spin_lock(&nn->s2s_cp_lock);
|
||||||
|
while (!list_empty(&stid->sc_cp_list)) {
|
||||||
|
cps = list_first_entry(&stid->sc_cp_list,
|
||||||
|
struct nfs4_cpntf_state, cp_list);
|
||||||
|
_free_cpntf_state_locked(nn, cps);
|
||||||
|
}
|
||||||
spin_unlock(&nn->s2s_cp_lock);
|
spin_unlock(&nn->s2s_cp_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -806,7 +854,7 @@ static void nfs4_free_deleg(struct nfs4_stid *stid)
|
||||||
static DEFINE_SPINLOCK(blocked_delegations_lock);
|
static DEFINE_SPINLOCK(blocked_delegations_lock);
|
||||||
static struct bloom_pair {
|
static struct bloom_pair {
|
||||||
int entries, old_entries;
|
int entries, old_entries;
|
||||||
time_t swap_time;
|
time64_t swap_time;
|
||||||
int new; /* index into 'set' */
|
int new; /* index into 'set' */
|
||||||
DECLARE_BITMAP(set[2], 256);
|
DECLARE_BITMAP(set[2], 256);
|
||||||
} blocked_delegations;
|
} blocked_delegations;
|
||||||
|
@ -818,15 +866,15 @@ static int delegation_blocked(struct knfsd_fh *fh)
|
||||||
|
|
||||||
if (bd->entries == 0)
|
if (bd->entries == 0)
|
||||||
return 0;
|
return 0;
|
||||||
if (seconds_since_boot() - bd->swap_time > 30) {
|
if (ktime_get_seconds() - bd->swap_time > 30) {
|
||||||
spin_lock(&blocked_delegations_lock);
|
spin_lock(&blocked_delegations_lock);
|
||||||
if (seconds_since_boot() - bd->swap_time > 30) {
|
if (ktime_get_seconds() - bd->swap_time > 30) {
|
||||||
bd->entries -= bd->old_entries;
|
bd->entries -= bd->old_entries;
|
||||||
bd->old_entries = bd->entries;
|
bd->old_entries = bd->entries;
|
||||||
memset(bd->set[bd->new], 0,
|
memset(bd->set[bd->new], 0,
|
||||||
sizeof(bd->set[0]));
|
sizeof(bd->set[0]));
|
||||||
bd->new = 1-bd->new;
|
bd->new = 1-bd->new;
|
||||||
bd->swap_time = seconds_since_boot();
|
bd->swap_time = ktime_get_seconds();
|
||||||
}
|
}
|
||||||
spin_unlock(&blocked_delegations_lock);
|
spin_unlock(&blocked_delegations_lock);
|
||||||
}
|
}
|
||||||
|
@ -856,7 +904,7 @@ static void block_delegations(struct knfsd_fh *fh)
|
||||||
__set_bit((hash>>8)&255, bd->set[bd->new]);
|
__set_bit((hash>>8)&255, bd->set[bd->new]);
|
||||||
__set_bit((hash>>16)&255, bd->set[bd->new]);
|
__set_bit((hash>>16)&255, bd->set[bd->new]);
|
||||||
if (bd->entries == 0)
|
if (bd->entries == 0)
|
||||||
bd->swap_time = seconds_since_boot();
|
bd->swap_time = ktime_get_seconds();
|
||||||
bd->entries += 1;
|
bd->entries += 1;
|
||||||
spin_unlock(&blocked_delegations_lock);
|
spin_unlock(&blocked_delegations_lock);
|
||||||
}
|
}
|
||||||
|
@ -915,6 +963,7 @@ nfs4_put_stid(struct nfs4_stid *s)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
idr_remove(&clp->cl_stateids, s->sc_stateid.si_opaque.so_id);
|
idr_remove(&clp->cl_stateids, s->sc_stateid.si_opaque.so_id);
|
||||||
|
nfs4_free_cpntf_statelist(clp->net, s);
|
||||||
spin_unlock(&clp->cl_lock);
|
spin_unlock(&clp->cl_lock);
|
||||||
s->sc_free(s);
|
s->sc_free(s);
|
||||||
if (fp)
|
if (fp)
|
||||||
|
@ -1862,7 +1911,7 @@ STALE_CLIENTID(clientid_t *clid, struct nfsd_net *nn)
|
||||||
*/
|
*/
|
||||||
if (clid->cl_boot == (u32)nn->boot_time)
|
if (clid->cl_boot == (u32)nn->boot_time)
|
||||||
return 0;
|
return 0;
|
||||||
dprintk("NFSD stale clientid (%08x/%08x) boot_time %08lx\n",
|
dprintk("NFSD stale clientid (%08x/%08x) boot_time %08llx\n",
|
||||||
clid->cl_boot, clid->cl_id, nn->boot_time);
|
clid->cl_boot, clid->cl_id, nn->boot_time);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -2215,14 +2264,14 @@ static void gen_confirm(struct nfs4_client *clp, struct nfsd_net *nn)
|
||||||
* This is opaque to client, so no need to byte-swap. Use
|
* This is opaque to client, so no need to byte-swap. Use
|
||||||
* __force to keep sparse happy
|
* __force to keep sparse happy
|
||||||
*/
|
*/
|
||||||
verf[0] = (__force __be32)get_seconds();
|
verf[0] = (__force __be32)(u32)ktime_get_real_seconds();
|
||||||
verf[1] = (__force __be32)nn->clverifier_counter++;
|
verf[1] = (__force __be32)nn->clverifier_counter++;
|
||||||
memcpy(clp->cl_confirm.data, verf, sizeof(clp->cl_confirm.data));
|
memcpy(clp->cl_confirm.data, verf, sizeof(clp->cl_confirm.data));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gen_clid(struct nfs4_client *clp, struct nfsd_net *nn)
|
static void gen_clid(struct nfs4_client *clp, struct nfsd_net *nn)
|
||||||
{
|
{
|
||||||
clp->cl_clientid.cl_boot = nn->boot_time;
|
clp->cl_clientid.cl_boot = (u32)nn->boot_time;
|
||||||
clp->cl_clientid.cl_id = nn->clientid_counter++;
|
clp->cl_clientid.cl_id = nn->clientid_counter++;
|
||||||
gen_confirm(clp, nn);
|
gen_confirm(clp, nn);
|
||||||
}
|
}
|
||||||
|
@ -2292,7 +2341,7 @@ static int client_info_show(struct seq_file *m, void *v)
|
||||||
clp->cl_nii_domain.len);
|
clp->cl_nii_domain.len);
|
||||||
seq_printf(m, "\nImplementation name: ");
|
seq_printf(m, "\nImplementation name: ");
|
||||||
seq_quote_mem(m, clp->cl_nii_name.data, clp->cl_nii_name.len);
|
seq_quote_mem(m, clp->cl_nii_name.data, clp->cl_nii_name.len);
|
||||||
seq_printf(m, "\nImplementation time: [%ld, %ld]\n",
|
seq_printf(m, "\nImplementation time: [%lld, %ld]\n",
|
||||||
clp->cl_nii_time.tv_sec, clp->cl_nii_time.tv_nsec);
|
clp->cl_nii_time.tv_sec, clp->cl_nii_time.tv_nsec);
|
||||||
}
|
}
|
||||||
drop_client(clp);
|
drop_client(clp);
|
||||||
|
@ -2612,7 +2661,7 @@ static struct nfs4_client *create_client(struct xdr_netobj name,
|
||||||
gen_clid(clp, nn);
|
gen_clid(clp, nn);
|
||||||
kref_init(&clp->cl_nfsdfs.cl_ref);
|
kref_init(&clp->cl_nfsdfs.cl_ref);
|
||||||
nfsd4_init_cb(&clp->cl_cb_null, clp, NULL, NFSPROC4_CLNT_CB_NULL);
|
nfsd4_init_cb(&clp->cl_cb_null, clp, NULL, NFSPROC4_CLNT_CB_NULL);
|
||||||
clp->cl_time = get_seconds();
|
clp->cl_time = ktime_get_boottime_seconds();
|
||||||
clear_bit(0, &clp->cl_cb_slot_busy);
|
clear_bit(0, &clp->cl_cb_slot_busy);
|
||||||
copy_verf(clp, verf);
|
copy_verf(clp, verf);
|
||||||
memcpy(&clp->cl_addr, sa, sizeof(struct sockaddr_storage));
|
memcpy(&clp->cl_addr, sa, sizeof(struct sockaddr_storage));
|
||||||
|
@ -2946,8 +2995,7 @@ static __be32 copy_impl_id(struct nfs4_client *clp,
|
||||||
xdr_netobj_dup(&clp->cl_nii_name, &exid->nii_name, GFP_KERNEL);
|
xdr_netobj_dup(&clp->cl_nii_name, &exid->nii_name, GFP_KERNEL);
|
||||||
if (!clp->cl_nii_name.data)
|
if (!clp->cl_nii_name.data)
|
||||||
return nfserr_jukebox;
|
return nfserr_jukebox;
|
||||||
clp->cl_nii_time.tv_sec = exid->nii_time.tv_sec;
|
clp->cl_nii_time = exid->nii_time;
|
||||||
clp->cl_nii_time.tv_nsec = exid->nii_time.tv_nsec;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3373,7 +3421,7 @@ static __be32 nfsd4_map_bcts_dir(u32 *dir)
|
||||||
case NFS4_CDFC4_BACK_OR_BOTH:
|
case NFS4_CDFC4_BACK_OR_BOTH:
|
||||||
*dir = NFS4_CDFC4_BOTH;
|
*dir = NFS4_CDFC4_BOTH;
|
||||||
return nfs_ok;
|
return nfs_ok;
|
||||||
};
|
}
|
||||||
return nfserr_inval;
|
return nfserr_inval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4283,7 +4331,7 @@ move_to_close_lru(struct nfs4_ol_stateid *s, struct net *net)
|
||||||
last = oo->oo_last_closed_stid;
|
last = oo->oo_last_closed_stid;
|
||||||
oo->oo_last_closed_stid = s;
|
oo->oo_last_closed_stid = s;
|
||||||
list_move_tail(&oo->oo_close_lru, &nn->close_lru);
|
list_move_tail(&oo->oo_close_lru, &nn->close_lru);
|
||||||
oo->oo_time = get_seconds();
|
oo->oo_time = ktime_get_boottime_seconds();
|
||||||
spin_unlock(&nn->client_lock);
|
spin_unlock(&nn->client_lock);
|
||||||
if (last)
|
if (last)
|
||||||
nfs4_put_stid(&last->st_stid);
|
nfs4_put_stid(&last->st_stid);
|
||||||
|
@ -4378,7 +4426,7 @@ static void nfsd4_cb_recall_prepare(struct nfsd4_callback *cb)
|
||||||
*/
|
*/
|
||||||
spin_lock(&state_lock);
|
spin_lock(&state_lock);
|
||||||
if (dp->dl_time == 0) {
|
if (dp->dl_time == 0) {
|
||||||
dp->dl_time = get_seconds();
|
dp->dl_time = ktime_get_boottime_seconds();
|
||||||
list_add_tail(&dp->dl_recall_lru, &nn->del_recall_lru);
|
list_add_tail(&dp->dl_recall_lru, &nn->del_recall_lru);
|
||||||
}
|
}
|
||||||
spin_unlock(&state_lock);
|
spin_unlock(&state_lock);
|
||||||
|
@ -4490,7 +4538,8 @@ static __be32 nfsd4_check_seqid(struct nfsd4_compound_state *cstate, struct nfs4
|
||||||
|
|
||||||
static __be32 lookup_clientid(clientid_t *clid,
|
static __be32 lookup_clientid(clientid_t *clid,
|
||||||
struct nfsd4_compound_state *cstate,
|
struct nfsd4_compound_state *cstate,
|
||||||
struct nfsd_net *nn)
|
struct nfsd_net *nn,
|
||||||
|
bool sessions)
|
||||||
{
|
{
|
||||||
struct nfs4_client *found;
|
struct nfs4_client *found;
|
||||||
|
|
||||||
|
@ -4511,7 +4560,7 @@ static __be32 lookup_clientid(clientid_t *clid,
|
||||||
*/
|
*/
|
||||||
WARN_ON_ONCE(cstate->session);
|
WARN_ON_ONCE(cstate->session);
|
||||||
spin_lock(&nn->client_lock);
|
spin_lock(&nn->client_lock);
|
||||||
found = find_confirmed_client(clid, false, nn);
|
found = find_confirmed_client(clid, sessions, nn);
|
||||||
if (!found) {
|
if (!found) {
|
||||||
spin_unlock(&nn->client_lock);
|
spin_unlock(&nn->client_lock);
|
||||||
return nfserr_expired;
|
return nfserr_expired;
|
||||||
|
@ -4544,7 +4593,7 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate,
|
||||||
if (open->op_file == NULL)
|
if (open->op_file == NULL)
|
||||||
return nfserr_jukebox;
|
return nfserr_jukebox;
|
||||||
|
|
||||||
status = lookup_clientid(clientid, cstate, nn);
|
status = lookup_clientid(clientid, cstate, nn, false);
|
||||||
if (status)
|
if (status)
|
||||||
return status;
|
return status;
|
||||||
clp = cstate->clp;
|
clp = cstate->clp;
|
||||||
|
@ -4672,7 +4721,7 @@ nfsd4_truncate(struct svc_rqst *rqstp, struct svc_fh *fh,
|
||||||
return 0;
|
return 0;
|
||||||
if (!(open->op_share_access & NFS4_SHARE_ACCESS_WRITE))
|
if (!(open->op_share_access & NFS4_SHARE_ACCESS_WRITE))
|
||||||
return nfserr_inval;
|
return nfserr_inval;
|
||||||
return nfsd_setattr(rqstp, fh, &iattr, 0, (time_t)0);
|
return nfsd_setattr(rqstp, fh, &iattr, 0, (time64_t)0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp,
|
static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp,
|
||||||
|
@ -5133,7 +5182,7 @@ nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||||
|
|
||||||
dprintk("process_renew(%08x/%08x): starting\n",
|
dprintk("process_renew(%08x/%08x): starting\n",
|
||||||
clid->cl_boot, clid->cl_id);
|
clid->cl_boot, clid->cl_id);
|
||||||
status = lookup_clientid(clid, cstate, nn);
|
status = lookup_clientid(clid, cstate, nn, false);
|
||||||
if (status)
|
if (status)
|
||||||
goto out;
|
goto out;
|
||||||
clp = cstate->clp;
|
clp = cstate->clp;
|
||||||
|
@ -5184,9 +5233,8 @@ nfsd4_end_grace(struct nfsd_net *nn)
|
||||||
*/
|
*/
|
||||||
static bool clients_still_reclaiming(struct nfsd_net *nn)
|
static bool clients_still_reclaiming(struct nfsd_net *nn)
|
||||||
{
|
{
|
||||||
unsigned long now = get_seconds();
|
time64_t double_grace_period_end = nn->boot_time +
|
||||||
unsigned long double_grace_period_end = nn->boot_time +
|
2 * nn->nfsd4_lease;
|
||||||
2 * nn->nfsd4_lease;
|
|
||||||
|
|
||||||
if (nn->track_reclaim_completes &&
|
if (nn->track_reclaim_completes &&
|
||||||
atomic_read(&nn->nr_reclaim_complete) ==
|
atomic_read(&nn->nr_reclaim_complete) ==
|
||||||
|
@ -5199,12 +5247,12 @@ static bool clients_still_reclaiming(struct nfsd_net *nn)
|
||||||
* If we've given them *two* lease times to reclaim, and they're
|
* If we've given them *two* lease times to reclaim, and they're
|
||||||
* still not done, give up:
|
* still not done, give up:
|
||||||
*/
|
*/
|
||||||
if (time_after(now, double_grace_period_end))
|
if (ktime_get_boottime_seconds() > double_grace_period_end)
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static time_t
|
static time64_t
|
||||||
nfs4_laundromat(struct nfsd_net *nn)
|
nfs4_laundromat(struct nfsd_net *nn)
|
||||||
{
|
{
|
||||||
struct nfs4_client *clp;
|
struct nfs4_client *clp;
|
||||||
|
@ -5213,8 +5261,11 @@ nfs4_laundromat(struct nfsd_net *nn)
|
||||||
struct nfs4_ol_stateid *stp;
|
struct nfs4_ol_stateid *stp;
|
||||||
struct nfsd4_blocked_lock *nbl;
|
struct nfsd4_blocked_lock *nbl;
|
||||||
struct list_head *pos, *next, reaplist;
|
struct list_head *pos, *next, reaplist;
|
||||||
time_t cutoff = get_seconds() - nn->nfsd4_lease;
|
time64_t cutoff = ktime_get_boottime_seconds() - nn->nfsd4_lease;
|
||||||
time_t t, new_timeo = nn->nfsd4_lease;
|
time64_t t, new_timeo = nn->nfsd4_lease;
|
||||||
|
struct nfs4_cpntf_state *cps;
|
||||||
|
copy_stateid_t *cps_t;
|
||||||
|
int i;
|
||||||
|
|
||||||
dprintk("NFSD: laundromat service - starting\n");
|
dprintk("NFSD: laundromat service - starting\n");
|
||||||
|
|
||||||
|
@ -5225,10 +5276,20 @@ nfs4_laundromat(struct nfsd_net *nn)
|
||||||
dprintk("NFSD: end of grace period\n");
|
dprintk("NFSD: end of grace period\n");
|
||||||
nfsd4_end_grace(nn);
|
nfsd4_end_grace(nn);
|
||||||
INIT_LIST_HEAD(&reaplist);
|
INIT_LIST_HEAD(&reaplist);
|
||||||
|
|
||||||
|
spin_lock(&nn->s2s_cp_lock);
|
||||||
|
idr_for_each_entry(&nn->s2s_cp_stateids, cps_t, i) {
|
||||||
|
cps = container_of(cps_t, struct nfs4_cpntf_state, cp_stateid);
|
||||||
|
if (cps->cp_stateid.sc_type == NFS4_COPYNOTIFY_STID &&
|
||||||
|
cps->cpntf_time > cutoff)
|
||||||
|
_free_cpntf_state_locked(nn, cps);
|
||||||
|
}
|
||||||
|
spin_unlock(&nn->s2s_cp_lock);
|
||||||
|
|
||||||
spin_lock(&nn->client_lock);
|
spin_lock(&nn->client_lock);
|
||||||
list_for_each_safe(pos, next, &nn->client_lru) {
|
list_for_each_safe(pos, next, &nn->client_lru) {
|
||||||
clp = list_entry(pos, struct nfs4_client, cl_lru);
|
clp = list_entry(pos, struct nfs4_client, cl_lru);
|
||||||
if (time_after((unsigned long)clp->cl_time, (unsigned long)cutoff)) {
|
if (clp->cl_time > cutoff) {
|
||||||
t = clp->cl_time - cutoff;
|
t = clp->cl_time - cutoff;
|
||||||
new_timeo = min(new_timeo, t);
|
new_timeo = min(new_timeo, t);
|
||||||
break;
|
break;
|
||||||
|
@ -5251,7 +5312,7 @@ nfs4_laundromat(struct nfsd_net *nn)
|
||||||
spin_lock(&state_lock);
|
spin_lock(&state_lock);
|
||||||
list_for_each_safe(pos, next, &nn->del_recall_lru) {
|
list_for_each_safe(pos, next, &nn->del_recall_lru) {
|
||||||
dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru);
|
dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru);
|
||||||
if (time_after((unsigned long)dp->dl_time, (unsigned long)cutoff)) {
|
if (dp->dl_time > cutoff) {
|
||||||
t = dp->dl_time - cutoff;
|
t = dp->dl_time - cutoff;
|
||||||
new_timeo = min(new_timeo, t);
|
new_timeo = min(new_timeo, t);
|
||||||
break;
|
break;
|
||||||
|
@ -5271,8 +5332,7 @@ nfs4_laundromat(struct nfsd_net *nn)
|
||||||
while (!list_empty(&nn->close_lru)) {
|
while (!list_empty(&nn->close_lru)) {
|
||||||
oo = list_first_entry(&nn->close_lru, struct nfs4_openowner,
|
oo = list_first_entry(&nn->close_lru, struct nfs4_openowner,
|
||||||
oo_close_lru);
|
oo_close_lru);
|
||||||
if (time_after((unsigned long)oo->oo_time,
|
if (oo->oo_time > cutoff) {
|
||||||
(unsigned long)cutoff)) {
|
|
||||||
t = oo->oo_time - cutoff;
|
t = oo->oo_time - cutoff;
|
||||||
new_timeo = min(new_timeo, t);
|
new_timeo = min(new_timeo, t);
|
||||||
break;
|
break;
|
||||||
|
@ -5302,8 +5362,7 @@ nfs4_laundromat(struct nfsd_net *nn)
|
||||||
while (!list_empty(&nn->blocked_locks_lru)) {
|
while (!list_empty(&nn->blocked_locks_lru)) {
|
||||||
nbl = list_first_entry(&nn->blocked_locks_lru,
|
nbl = list_first_entry(&nn->blocked_locks_lru,
|
||||||
struct nfsd4_blocked_lock, nbl_lru);
|
struct nfsd4_blocked_lock, nbl_lru);
|
||||||
if (time_after((unsigned long)nbl->nbl_time,
|
if (nbl->nbl_time > cutoff) {
|
||||||
(unsigned long)cutoff)) {
|
|
||||||
t = nbl->nbl_time - cutoff;
|
t = nbl->nbl_time - cutoff;
|
||||||
new_timeo = min(new_timeo, t);
|
new_timeo = min(new_timeo, t);
|
||||||
break;
|
break;
|
||||||
|
@ -5320,7 +5379,7 @@ nfs4_laundromat(struct nfsd_net *nn)
|
||||||
free_blocked_lock(nbl);
|
free_blocked_lock(nbl);
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
new_timeo = max_t(time_t, new_timeo, NFSD_LAUNDROMAT_MINTIMEOUT);
|
new_timeo = max_t(time64_t, new_timeo, NFSD_LAUNDROMAT_MINTIMEOUT);
|
||||||
return new_timeo;
|
return new_timeo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5330,13 +5389,13 @@ static void laundromat_main(struct work_struct *);
|
||||||
static void
|
static void
|
||||||
laundromat_main(struct work_struct *laundry)
|
laundromat_main(struct work_struct *laundry)
|
||||||
{
|
{
|
||||||
time_t t;
|
time64_t t;
|
||||||
struct delayed_work *dwork = to_delayed_work(laundry);
|
struct delayed_work *dwork = to_delayed_work(laundry);
|
||||||
struct nfsd_net *nn = container_of(dwork, struct nfsd_net,
|
struct nfsd_net *nn = container_of(dwork, struct nfsd_net,
|
||||||
laundromat_work);
|
laundromat_work);
|
||||||
|
|
||||||
t = nfs4_laundromat(nn);
|
t = nfs4_laundromat(nn);
|
||||||
dprintk("NFSD: laundromat_main - sleeping for %ld seconds\n", t);
|
dprintk("NFSD: laundromat_main - sleeping for %lld seconds\n", t);
|
||||||
queue_delayed_work(laundry_wq, &nn->laundromat_work, t*HZ);
|
queue_delayed_work(laundry_wq, &nn->laundromat_work, t*HZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5521,7 +5580,8 @@ nfsd4_lookup_stateid(struct nfsd4_compound_state *cstate,
|
||||||
if (ZERO_STATEID(stateid) || ONE_STATEID(stateid) ||
|
if (ZERO_STATEID(stateid) || ONE_STATEID(stateid) ||
|
||||||
CLOSE_STATEID(stateid))
|
CLOSE_STATEID(stateid))
|
||||||
return nfserr_bad_stateid;
|
return nfserr_bad_stateid;
|
||||||
status = lookup_clientid(&stateid->si_opaque.so_clid, cstate, nn);
|
status = lookup_clientid(&stateid->si_opaque.so_clid, cstate, nn,
|
||||||
|
false);
|
||||||
if (status == nfserr_stale_clientid) {
|
if (status == nfserr_stale_clientid) {
|
||||||
if (cstate->session)
|
if (cstate->session)
|
||||||
return nfserr_bad_stateid;
|
return nfserr_bad_stateid;
|
||||||
|
@ -5600,6 +5660,85 @@ nfs4_check_file(struct svc_rqst *rqstp, struct svc_fh *fhp, struct nfs4_stid *s,
|
||||||
out:
|
out:
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
static void
|
||||||
|
_free_cpntf_state_locked(struct nfsd_net *nn, struct nfs4_cpntf_state *cps)
|
||||||
|
{
|
||||||
|
WARN_ON_ONCE(cps->cp_stateid.sc_type != NFS4_COPYNOTIFY_STID);
|
||||||
|
if (!refcount_dec_and_test(&cps->cp_stateid.sc_count))
|
||||||
|
return;
|
||||||
|
list_del(&cps->cp_list);
|
||||||
|
idr_remove(&nn->s2s_cp_stateids,
|
||||||
|
cps->cp_stateid.stid.si_opaque.so_id);
|
||||||
|
kfree(cps);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* A READ from an inter server to server COPY will have a
|
||||||
|
* copy stateid. Look up the copy notify stateid from the
|
||||||
|
* idr structure and take a reference on it.
|
||||||
|
*/
|
||||||
|
__be32 manage_cpntf_state(struct nfsd_net *nn, stateid_t *st,
|
||||||
|
struct nfs4_client *clp,
|
||||||
|
struct nfs4_cpntf_state **cps)
|
||||||
|
{
|
||||||
|
copy_stateid_t *cps_t;
|
||||||
|
struct nfs4_cpntf_state *state = NULL;
|
||||||
|
|
||||||
|
if (st->si_opaque.so_clid.cl_id != nn->s2s_cp_cl_id)
|
||||||
|
return nfserr_bad_stateid;
|
||||||
|
spin_lock(&nn->s2s_cp_lock);
|
||||||
|
cps_t = idr_find(&nn->s2s_cp_stateids, st->si_opaque.so_id);
|
||||||
|
if (cps_t) {
|
||||||
|
state = container_of(cps_t, struct nfs4_cpntf_state,
|
||||||
|
cp_stateid);
|
||||||
|
if (state->cp_stateid.sc_type != NFS4_COPYNOTIFY_STID) {
|
||||||
|
state = NULL;
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
if (!clp)
|
||||||
|
refcount_inc(&state->cp_stateid.sc_count);
|
||||||
|
else
|
||||||
|
_free_cpntf_state_locked(nn, state);
|
||||||
|
}
|
||||||
|
unlock:
|
||||||
|
spin_unlock(&nn->s2s_cp_lock);
|
||||||
|
if (!state)
|
||||||
|
return nfserr_bad_stateid;
|
||||||
|
if (!clp && state)
|
||||||
|
*cps = state;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static __be32 find_cpntf_state(struct nfsd_net *nn, stateid_t *st,
|
||||||
|
struct nfs4_stid **stid)
|
||||||
|
{
|
||||||
|
__be32 status;
|
||||||
|
struct nfs4_cpntf_state *cps = NULL;
|
||||||
|
struct nfsd4_compound_state cstate;
|
||||||
|
|
||||||
|
status = manage_cpntf_state(nn, st, NULL, &cps);
|
||||||
|
if (status)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
cps->cpntf_time = ktime_get_boottime_seconds();
|
||||||
|
memset(&cstate, 0, sizeof(cstate));
|
||||||
|
status = lookup_clientid(&cps->cp_p_clid, &cstate, nn, true);
|
||||||
|
if (status)
|
||||||
|
goto out;
|
||||||
|
status = nfsd4_lookup_stateid(&cstate, &cps->cp_p_stateid,
|
||||||
|
NFS4_DELEG_STID|NFS4_OPEN_STID|NFS4_LOCK_STID,
|
||||||
|
stid, nn);
|
||||||
|
put_client_renew(cstate.clp);
|
||||||
|
out:
|
||||||
|
nfs4_put_cpntf_state(nn, cps);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfs4_put_cpntf_state(struct nfsd_net *nn, struct nfs4_cpntf_state *cps)
|
||||||
|
{
|
||||||
|
spin_lock(&nn->s2s_cp_lock);
|
||||||
|
_free_cpntf_state_locked(nn, cps);
|
||||||
|
spin_unlock(&nn->s2s_cp_lock);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Checks for stateid operations
|
* Checks for stateid operations
|
||||||
|
@ -5607,7 +5746,8 @@ out:
|
||||||
__be32
|
__be32
|
||||||
nfs4_preprocess_stateid_op(struct svc_rqst *rqstp,
|
nfs4_preprocess_stateid_op(struct svc_rqst *rqstp,
|
||||||
struct nfsd4_compound_state *cstate, struct svc_fh *fhp,
|
struct nfsd4_compound_state *cstate, struct svc_fh *fhp,
|
||||||
stateid_t *stateid, int flags, struct nfsd_file **nfp)
|
stateid_t *stateid, int flags, struct nfsd_file **nfp,
|
||||||
|
struct nfs4_stid **cstid)
|
||||||
{
|
{
|
||||||
struct inode *ino = d_inode(fhp->fh_dentry);
|
struct inode *ino = d_inode(fhp->fh_dentry);
|
||||||
struct net *net = SVC_NET(rqstp);
|
struct net *net = SVC_NET(rqstp);
|
||||||
|
@ -5629,6 +5769,8 @@ nfs4_preprocess_stateid_op(struct svc_rqst *rqstp,
|
||||||
status = nfsd4_lookup_stateid(cstate, stateid,
|
status = nfsd4_lookup_stateid(cstate, stateid,
|
||||||
NFS4_DELEG_STID|NFS4_OPEN_STID|NFS4_LOCK_STID,
|
NFS4_DELEG_STID|NFS4_OPEN_STID|NFS4_LOCK_STID,
|
||||||
&s, nn);
|
&s, nn);
|
||||||
|
if (status == nfserr_bad_stateid)
|
||||||
|
status = find_cpntf_state(nn, stateid, &s);
|
||||||
if (status)
|
if (status)
|
||||||
return status;
|
return status;
|
||||||
status = nfsd4_stid_check_stateid_generation(stateid, s,
|
status = nfsd4_stid_check_stateid_generation(stateid, s,
|
||||||
|
@ -5656,8 +5798,12 @@ done:
|
||||||
if (status == nfs_ok && nfp)
|
if (status == nfs_ok && nfp)
|
||||||
status = nfs4_check_file(rqstp, fhp, s, nfp, flags);
|
status = nfs4_check_file(rqstp, fhp, s, nfp, flags);
|
||||||
out:
|
out:
|
||||||
if (s)
|
if (s) {
|
||||||
nfs4_put_stid(s);
|
if (!status && cstid)
|
||||||
|
*cstid = s;
|
||||||
|
else
|
||||||
|
nfs4_put_stid(s);
|
||||||
|
}
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6550,7 +6696,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fl_flags & FL_SLEEP) {
|
if (fl_flags & FL_SLEEP) {
|
||||||
nbl->nbl_time = jiffies;
|
nbl->nbl_time = ktime_get_boottime_seconds();
|
||||||
spin_lock(&nn->blocked_locks_lock);
|
spin_lock(&nn->blocked_locks_lock);
|
||||||
list_add_tail(&nbl->nbl_list, &lock_sop->lo_blocked);
|
list_add_tail(&nbl->nbl_list, &lock_sop->lo_blocked);
|
||||||
list_add_tail(&nbl->nbl_lru, &nn->blocked_locks_lru);
|
list_add_tail(&nbl->nbl_lru, &nn->blocked_locks_lru);
|
||||||
|
@ -6657,7 +6803,8 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||||
return nfserr_inval;
|
return nfserr_inval;
|
||||||
|
|
||||||
if (!nfsd4_has_session(cstate)) {
|
if (!nfsd4_has_session(cstate)) {
|
||||||
status = lookup_clientid(&lockt->lt_clientid, cstate, nn);
|
status = lookup_clientid(&lockt->lt_clientid, cstate, nn,
|
||||||
|
false);
|
||||||
if (status)
|
if (status)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
@ -6841,7 +6988,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,
|
||||||
dprintk("nfsd4_release_lockowner clientid: (%08x/%08x):\n",
|
dprintk("nfsd4_release_lockowner clientid: (%08x/%08x):\n",
|
||||||
clid->cl_boot, clid->cl_id);
|
clid->cl_boot, clid->cl_id);
|
||||||
|
|
||||||
status = lookup_clientid(clid, cstate, nn);
|
status = lookup_clientid(clid, cstate, nn, false);
|
||||||
if (status)
|
if (status)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
|
@ -6988,7 +7135,7 @@ nfs4_check_open_reclaim(clientid_t *clid,
|
||||||
__be32 status;
|
__be32 status;
|
||||||
|
|
||||||
/* find clientid in conf_id_hashtbl */
|
/* find clientid in conf_id_hashtbl */
|
||||||
status = lookup_clientid(clid, cstate, nn);
|
status = lookup_clientid(clid, cstate, nn, false);
|
||||||
if (status)
|
if (status)
|
||||||
return nfserr_reclaim_bad;
|
return nfserr_reclaim_bad;
|
||||||
|
|
||||||
|
@ -7641,7 +7788,7 @@ static int nfs4_state_create_net(struct net *net)
|
||||||
INIT_LIST_HEAD(&nn->sessionid_hashtbl[i]);
|
INIT_LIST_HEAD(&nn->sessionid_hashtbl[i]);
|
||||||
nn->conf_name_tree = RB_ROOT;
|
nn->conf_name_tree = RB_ROOT;
|
||||||
nn->unconf_name_tree = RB_ROOT;
|
nn->unconf_name_tree = RB_ROOT;
|
||||||
nn->boot_time = get_seconds();
|
nn->boot_time = ktime_get_real_seconds();
|
||||||
nn->grace_ended = false;
|
nn->grace_ended = false;
|
||||||
nn->nfsd4_manager.block_opens = true;
|
nn->nfsd4_manager.block_opens = true;
|
||||||
INIT_LIST_HEAD(&nn->nfsd4_manager.list);
|
INIT_LIST_HEAD(&nn->nfsd4_manager.list);
|
||||||
|
@ -7710,7 +7857,7 @@ nfs4_state_start_net(struct net *net)
|
||||||
nfsd4_client_tracking_init(net);
|
nfsd4_client_tracking_init(net);
|
||||||
if (nn->track_reclaim_completes && nn->reclaim_str_hashtbl_size == 0)
|
if (nn->track_reclaim_completes && nn->reclaim_str_hashtbl_size == 0)
|
||||||
goto skip_grace;
|
goto skip_grace;
|
||||||
printk(KERN_INFO "NFSD: starting %ld-second grace period (net %x)\n",
|
printk(KERN_INFO "NFSD: starting %lld-second grace period (net %x)\n",
|
||||||
nn->nfsd4_grace, net->ns.inum);
|
nn->nfsd4_grace, net->ns.inum);
|
||||||
queue_delayed_work(laundry_wq, &nn->laundromat_work, nn->nfsd4_grace * HZ);
|
queue_delayed_work(laundry_wq, &nn->laundromat_work, nn->nfsd4_grace * HZ);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -7786,7 +7933,8 @@ nfs4_state_shutdown(void)
|
||||||
static void
|
static void
|
||||||
get_stateid(struct nfsd4_compound_state *cstate, stateid_t *stateid)
|
get_stateid(struct nfsd4_compound_state *cstate, stateid_t *stateid)
|
||||||
{
|
{
|
||||||
if (HAS_STATE_ID(cstate, CURRENT_STATE_ID_FLAG) && CURRENT_STATEID(stateid))
|
if (HAS_CSTATE_FLAG(cstate, CURRENT_STATE_ID_FLAG) &&
|
||||||
|
CURRENT_STATEID(stateid))
|
||||||
memcpy(stateid, &cstate->current_stateid, sizeof(stateid_t));
|
memcpy(stateid, &cstate->current_stateid, sizeof(stateid_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7795,14 +7943,14 @@ put_stateid(struct nfsd4_compound_state *cstate, stateid_t *stateid)
|
||||||
{
|
{
|
||||||
if (cstate->minorversion) {
|
if (cstate->minorversion) {
|
||||||
memcpy(&cstate->current_stateid, stateid, sizeof(stateid_t));
|
memcpy(&cstate->current_stateid, stateid, sizeof(stateid_t));
|
||||||
SET_STATE_ID(cstate, CURRENT_STATE_ID_FLAG);
|
SET_CSTATE_FLAG(cstate, CURRENT_STATE_ID_FLAG);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
clear_current_stateid(struct nfsd4_compound_state *cstate)
|
clear_current_stateid(struct nfsd4_compound_state *cstate)
|
||||||
{
|
{
|
||||||
CLEAR_STATE_ID(cstate, CURRENT_STATE_ID_FLAG);
|
CLEAR_CSTATE_FLAG(cstate, CURRENT_STATE_ID_FLAG);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
#include <linux/utsname.h>
|
#include <linux/utsname.h>
|
||||||
#include <linux/pagemap.h>
|
#include <linux/pagemap.h>
|
||||||
#include <linux/sunrpc/svcauth_gss.h>
|
#include <linux/sunrpc/svcauth_gss.h>
|
||||||
|
#include <linux/sunrpc/addr.h>
|
||||||
|
|
||||||
#include "idmap.h"
|
#include "idmap.h"
|
||||||
#include "acl.h"
|
#include "acl.h"
|
||||||
|
@ -1744,10 +1745,47 @@ nfsd4_decode_clone(struct nfsd4_compoundargs *argp, struct nfsd4_clone *clone)
|
||||||
DECODE_TAIL;
|
DECODE_TAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static __be32 nfsd4_decode_nl4_server(struct nfsd4_compoundargs *argp,
|
||||||
|
struct nl4_server *ns)
|
||||||
|
{
|
||||||
|
DECODE_HEAD;
|
||||||
|
struct nfs42_netaddr *naddr;
|
||||||
|
|
||||||
|
READ_BUF(4);
|
||||||
|
ns->nl4_type = be32_to_cpup(p++);
|
||||||
|
|
||||||
|
/* currently support for 1 inter-server source server */
|
||||||
|
switch (ns->nl4_type) {
|
||||||
|
case NL4_NETADDR:
|
||||||
|
naddr = &ns->u.nl4_addr;
|
||||||
|
|
||||||
|
READ_BUF(4);
|
||||||
|
naddr->netid_len = be32_to_cpup(p++);
|
||||||
|
if (naddr->netid_len > RPCBIND_MAXNETIDLEN)
|
||||||
|
goto xdr_error;
|
||||||
|
|
||||||
|
READ_BUF(naddr->netid_len + 4); /* 4 for uaddr len */
|
||||||
|
COPYMEM(naddr->netid, naddr->netid_len);
|
||||||
|
|
||||||
|
naddr->addr_len = be32_to_cpup(p++);
|
||||||
|
if (naddr->addr_len > RPCBIND_MAXUADDRLEN)
|
||||||
|
goto xdr_error;
|
||||||
|
|
||||||
|
READ_BUF(naddr->addr_len);
|
||||||
|
COPYMEM(naddr->addr, naddr->addr_len);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
goto xdr_error;
|
||||||
|
}
|
||||||
|
DECODE_TAIL;
|
||||||
|
}
|
||||||
|
|
||||||
static __be32
|
static __be32
|
||||||
nfsd4_decode_copy(struct nfsd4_compoundargs *argp, struct nfsd4_copy *copy)
|
nfsd4_decode_copy(struct nfsd4_compoundargs *argp, struct nfsd4_copy *copy)
|
||||||
{
|
{
|
||||||
DECODE_HEAD;
|
DECODE_HEAD;
|
||||||
|
struct nl4_server *ns_dummy;
|
||||||
|
int i, count;
|
||||||
|
|
||||||
status = nfsd4_decode_stateid(argp, ©->cp_src_stateid);
|
status = nfsd4_decode_stateid(argp, ©->cp_src_stateid);
|
||||||
if (status)
|
if (status)
|
||||||
|
@ -1762,7 +1800,32 @@ nfsd4_decode_copy(struct nfsd4_compoundargs *argp, struct nfsd4_copy *copy)
|
||||||
p = xdr_decode_hyper(p, ©->cp_count);
|
p = xdr_decode_hyper(p, ©->cp_count);
|
||||||
p++; /* ca_consecutive: we always do consecutive copies */
|
p++; /* ca_consecutive: we always do consecutive copies */
|
||||||
copy->cp_synchronous = be32_to_cpup(p++);
|
copy->cp_synchronous = be32_to_cpup(p++);
|
||||||
/* tmp = be32_to_cpup(p); Source server list not supported */
|
|
||||||
|
count = be32_to_cpup(p++);
|
||||||
|
|
||||||
|
copy->cp_intra = false;
|
||||||
|
if (count == 0) { /* intra-server copy */
|
||||||
|
copy->cp_intra = true;
|
||||||
|
goto intra;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* decode all the supplied server addresses but use first */
|
||||||
|
status = nfsd4_decode_nl4_server(argp, ©->cp_src);
|
||||||
|
if (status)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
ns_dummy = kmalloc(sizeof(struct nl4_server), GFP_KERNEL);
|
||||||
|
if (ns_dummy == NULL)
|
||||||
|
return nfserrno(-ENOMEM);
|
||||||
|
for (i = 0; i < count - 1; i++) {
|
||||||
|
status = nfsd4_decode_nl4_server(argp, ns_dummy);
|
||||||
|
if (status) {
|
||||||
|
kfree(ns_dummy);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
kfree(ns_dummy);
|
||||||
|
intra:
|
||||||
|
|
||||||
DECODE_TAIL;
|
DECODE_TAIL;
|
||||||
}
|
}
|
||||||
|
@ -1774,6 +1837,18 @@ nfsd4_decode_offload_status(struct nfsd4_compoundargs *argp,
|
||||||
return nfsd4_decode_stateid(argp, &os->stateid);
|
return nfsd4_decode_stateid(argp, &os->stateid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static __be32
|
||||||
|
nfsd4_decode_copy_notify(struct nfsd4_compoundargs *argp,
|
||||||
|
struct nfsd4_copy_notify *cn)
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
|
||||||
|
status = nfsd4_decode_stateid(argp, &cn->cpn_src_stateid);
|
||||||
|
if (status)
|
||||||
|
return status;
|
||||||
|
return nfsd4_decode_nl4_server(argp, &cn->cpn_dst);
|
||||||
|
}
|
||||||
|
|
||||||
static __be32
|
static __be32
|
||||||
nfsd4_decode_seek(struct nfsd4_compoundargs *argp, struct nfsd4_seek *seek)
|
nfsd4_decode_seek(struct nfsd4_compoundargs *argp, struct nfsd4_seek *seek)
|
||||||
{
|
{
|
||||||
|
@ -1875,7 +1950,7 @@ static const nfsd4_dec nfsd4_dec_ops[] = {
|
||||||
/* new operations for NFSv4.2 */
|
/* new operations for NFSv4.2 */
|
||||||
[OP_ALLOCATE] = (nfsd4_dec)nfsd4_decode_fallocate,
|
[OP_ALLOCATE] = (nfsd4_dec)nfsd4_decode_fallocate,
|
||||||
[OP_COPY] = (nfsd4_dec)nfsd4_decode_copy,
|
[OP_COPY] = (nfsd4_dec)nfsd4_decode_copy,
|
||||||
[OP_COPY_NOTIFY] = (nfsd4_dec)nfsd4_decode_notsupp,
|
[OP_COPY_NOTIFY] = (nfsd4_dec)nfsd4_decode_copy_notify,
|
||||||
[OP_DEALLOCATE] = (nfsd4_dec)nfsd4_decode_fallocate,
|
[OP_DEALLOCATE] = (nfsd4_dec)nfsd4_decode_fallocate,
|
||||||
[OP_IO_ADVISE] = (nfsd4_dec)nfsd4_decode_notsupp,
|
[OP_IO_ADVISE] = (nfsd4_dec)nfsd4_decode_notsupp,
|
||||||
[OP_LAYOUTERROR] = (nfsd4_dec)nfsd4_decode_notsupp,
|
[OP_LAYOUTERROR] = (nfsd4_dec)nfsd4_decode_notsupp,
|
||||||
|
@ -2024,11 +2099,11 @@ static __be32 *encode_change(__be32 *p, struct kstat *stat, struct inode *inode,
|
||||||
*/
|
*/
|
||||||
static __be32 *encode_time_delta(__be32 *p, struct inode *inode)
|
static __be32 *encode_time_delta(__be32 *p, struct inode *inode)
|
||||||
{
|
{
|
||||||
struct timespec ts;
|
struct timespec64 ts;
|
||||||
u32 ns;
|
u32 ns;
|
||||||
|
|
||||||
ns = max_t(u32, NSEC_PER_SEC/HZ, inode->i_sb->s_time_gran);
|
ns = max_t(u32, NSEC_PER_SEC/HZ, inode->i_sb->s_time_gran);
|
||||||
ts = ns_to_timespec(ns);
|
ts = ns_to_timespec64(ns);
|
||||||
|
|
||||||
p = xdr_encode_hyper(p, ts.tv_sec);
|
p = xdr_encode_hyper(p, ts.tv_sec);
|
||||||
*p++ = cpu_to_be32(ts.tv_nsec);
|
*p++ = cpu_to_be32(ts.tv_nsec);
|
||||||
|
@ -4243,6 +4318,46 @@ nfsd42_encode_write_res(struct nfsd4_compoundres *resp,
|
||||||
return nfs_ok;
|
return nfs_ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static __be32
|
||||||
|
nfsd42_encode_nl4_server(struct nfsd4_compoundres *resp, struct nl4_server *ns)
|
||||||
|
{
|
||||||
|
struct xdr_stream *xdr = &resp->xdr;
|
||||||
|
struct nfs42_netaddr *addr;
|
||||||
|
__be32 *p;
|
||||||
|
|
||||||
|
p = xdr_reserve_space(xdr, 4);
|
||||||
|
*p++ = cpu_to_be32(ns->nl4_type);
|
||||||
|
|
||||||
|
switch (ns->nl4_type) {
|
||||||
|
case NL4_NETADDR:
|
||||||
|
addr = &ns->u.nl4_addr;
|
||||||
|
|
||||||
|
/* netid_len, netid, uaddr_len, uaddr (port included
|
||||||
|
* in RPCBIND_MAXUADDRLEN)
|
||||||
|
*/
|
||||||
|
p = xdr_reserve_space(xdr,
|
||||||
|
4 /* netid len */ +
|
||||||
|
(XDR_QUADLEN(addr->netid_len) * 4) +
|
||||||
|
4 /* uaddr len */ +
|
||||||
|
(XDR_QUADLEN(addr->addr_len) * 4));
|
||||||
|
if (!p)
|
||||||
|
return nfserr_resource;
|
||||||
|
|
||||||
|
*p++ = cpu_to_be32(addr->netid_len);
|
||||||
|
p = xdr_encode_opaque_fixed(p, addr->netid,
|
||||||
|
addr->netid_len);
|
||||||
|
*p++ = cpu_to_be32(addr->addr_len);
|
||||||
|
p = xdr_encode_opaque_fixed(p, addr->addr,
|
||||||
|
addr->addr_len);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
WARN_ON_ONCE(ns->nl4_type != NL4_NETADDR);
|
||||||
|
return nfserr_inval;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static __be32
|
static __be32
|
||||||
nfsd4_encode_copy(struct nfsd4_compoundres *resp, __be32 nfserr,
|
nfsd4_encode_copy(struct nfsd4_compoundres *resp, __be32 nfserr,
|
||||||
struct nfsd4_copy *copy)
|
struct nfsd4_copy *copy)
|
||||||
|
@ -4276,6 +4391,40 @@ nfsd4_encode_offload_status(struct nfsd4_compoundres *resp, __be32 nfserr,
|
||||||
return nfserr;
|
return nfserr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static __be32
|
||||||
|
nfsd4_encode_copy_notify(struct nfsd4_compoundres *resp, __be32 nfserr,
|
||||||
|
struct nfsd4_copy_notify *cn)
|
||||||
|
{
|
||||||
|
struct xdr_stream *xdr = &resp->xdr;
|
||||||
|
__be32 *p;
|
||||||
|
|
||||||
|
if (nfserr)
|
||||||
|
return nfserr;
|
||||||
|
|
||||||
|
/* 8 sec, 4 nsec */
|
||||||
|
p = xdr_reserve_space(xdr, 12);
|
||||||
|
if (!p)
|
||||||
|
return nfserr_resource;
|
||||||
|
|
||||||
|
/* cnr_lease_time */
|
||||||
|
p = xdr_encode_hyper(p, cn->cpn_sec);
|
||||||
|
*p++ = cpu_to_be32(cn->cpn_nsec);
|
||||||
|
|
||||||
|
/* cnr_stateid */
|
||||||
|
nfserr = nfsd4_encode_stateid(xdr, &cn->cpn_cnr_stateid);
|
||||||
|
if (nfserr)
|
||||||
|
return nfserr;
|
||||||
|
|
||||||
|
/* cnr_src.nl_nsvr */
|
||||||
|
p = xdr_reserve_space(xdr, 4);
|
||||||
|
if (!p)
|
||||||
|
return nfserr_resource;
|
||||||
|
|
||||||
|
*p++ = cpu_to_be32(1);
|
||||||
|
|
||||||
|
return nfsd42_encode_nl4_server(resp, &cn->cpn_src);
|
||||||
|
}
|
||||||
|
|
||||||
static __be32
|
static __be32
|
||||||
nfsd4_encode_seek(struct nfsd4_compoundres *resp, __be32 nfserr,
|
nfsd4_encode_seek(struct nfsd4_compoundres *resp, __be32 nfserr,
|
||||||
struct nfsd4_seek *seek)
|
struct nfsd4_seek *seek)
|
||||||
|
@ -4373,7 +4522,7 @@ static const nfsd4_enc nfsd4_enc_ops[] = {
|
||||||
/* NFSv4.2 operations */
|
/* NFSv4.2 operations */
|
||||||
[OP_ALLOCATE] = (nfsd4_enc)nfsd4_encode_noop,
|
[OP_ALLOCATE] = (nfsd4_enc)nfsd4_encode_noop,
|
||||||
[OP_COPY] = (nfsd4_enc)nfsd4_encode_copy,
|
[OP_COPY] = (nfsd4_enc)nfsd4_encode_copy,
|
||||||
[OP_COPY_NOTIFY] = (nfsd4_enc)nfsd4_encode_noop,
|
[OP_COPY_NOTIFY] = (nfsd4_enc)nfsd4_encode_copy_notify,
|
||||||
[OP_DEALLOCATE] = (nfsd4_enc)nfsd4_encode_noop,
|
[OP_DEALLOCATE] = (nfsd4_enc)nfsd4_encode_noop,
|
||||||
[OP_IO_ADVISE] = (nfsd4_enc)nfsd4_encode_noop,
|
[OP_IO_ADVISE] = (nfsd4_enc)nfsd4_encode_noop,
|
||||||
[OP_LAYOUTERROR] = (nfsd4_enc)nfsd4_encode_noop,
|
[OP_LAYOUTERROR] = (nfsd4_enc)nfsd4_encode_noop,
|
||||||
|
@ -4500,8 +4649,6 @@ nfsd4_encode_replay(struct xdr_stream *xdr, struct nfsd4_op *op)
|
||||||
__be32 *p;
|
__be32 *p;
|
||||||
struct nfs4_replay *rp = op->replay;
|
struct nfs4_replay *rp = op->replay;
|
||||||
|
|
||||||
BUG_ON(!rp);
|
|
||||||
|
|
||||||
p = xdr_reserve_space(xdr, 8 + rp->rp_buflen);
|
p = xdr_reserve_space(xdr, 8 + rp->rp_buflen);
|
||||||
if (!p) {
|
if (!p) {
|
||||||
WARN_ON_ONCE(1);
|
WARN_ON_ONCE(1);
|
||||||
|
|
|
@ -956,7 +956,7 @@ static ssize_t write_maxconn(struct file *file, char *buf, size_t size)
|
||||||
|
|
||||||
#ifdef CONFIG_NFSD_V4
|
#ifdef CONFIG_NFSD_V4
|
||||||
static ssize_t __nfsd4_write_time(struct file *file, char *buf, size_t size,
|
static ssize_t __nfsd4_write_time(struct file *file, char *buf, size_t size,
|
||||||
time_t *time, struct nfsd_net *nn)
|
time64_t *time, struct nfsd_net *nn)
|
||||||
{
|
{
|
||||||
char *mesg = buf;
|
char *mesg = buf;
|
||||||
int rv, i;
|
int rv, i;
|
||||||
|
@ -984,11 +984,11 @@ static ssize_t __nfsd4_write_time(struct file *file, char *buf, size_t size,
|
||||||
*time = i;
|
*time = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%ld\n", *time);
|
return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%lld\n", *time);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t nfsd4_write_time(struct file *file, char *buf, size_t size,
|
static ssize_t nfsd4_write_time(struct file *file, char *buf, size_t size,
|
||||||
time_t *time, struct nfsd_net *nn)
|
time64_t *time, struct nfsd_net *nn)
|
||||||
{
|
{
|
||||||
ssize_t rv;
|
ssize_t rv;
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include <linux/sunrpc/svc.h>
|
#include <linux/sunrpc/svc.h>
|
||||||
#include <linux/sunrpc/svc_xprt.h>
|
#include <linux/sunrpc/svc_xprt.h>
|
||||||
#include <linux/sunrpc/msg_prot.h>
|
#include <linux/sunrpc/msg_prot.h>
|
||||||
|
#include <linux/sunrpc/addr.h>
|
||||||
|
|
||||||
#include <uapi/linux/nfsd/debug.h>
|
#include <uapi/linux/nfsd/debug.h>
|
||||||
|
|
||||||
|
@ -142,7 +143,6 @@ int nfs4_state_start(void);
|
||||||
int nfs4_state_start_net(struct net *net);
|
int nfs4_state_start_net(struct net *net);
|
||||||
void nfs4_state_shutdown(void);
|
void nfs4_state_shutdown(void);
|
||||||
void nfs4_state_shutdown_net(struct net *net);
|
void nfs4_state_shutdown_net(struct net *net);
|
||||||
void nfs4_reset_lease(time_t leasetime);
|
|
||||||
int nfs4_reset_recoverydir(char *recdir);
|
int nfs4_reset_recoverydir(char *recdir);
|
||||||
char * nfs4_recoverydir(void);
|
char * nfs4_recoverydir(void);
|
||||||
bool nfsd4_spo_must_allow(struct svc_rqst *rqstp);
|
bool nfsd4_spo_must_allow(struct svc_rqst *rqstp);
|
||||||
|
@ -153,7 +153,6 @@ static inline int nfs4_state_start(void) { return 0; }
|
||||||
static inline int nfs4_state_start_net(struct net *net) { return 0; }
|
static inline int nfs4_state_start_net(struct net *net) { return 0; }
|
||||||
static inline void nfs4_state_shutdown(void) { }
|
static inline void nfs4_state_shutdown(void) { }
|
||||||
static inline void nfs4_state_shutdown_net(struct net *net) { }
|
static inline void nfs4_state_shutdown_net(struct net *net) { }
|
||||||
static inline void nfs4_reset_lease(time_t leasetime) { }
|
|
||||||
static inline int nfs4_reset_recoverydir(char *recdir) { return 0; }
|
static inline int nfs4_reset_recoverydir(char *recdir) { return 0; }
|
||||||
static inline char * nfs4_recoverydir(void) {return NULL; }
|
static inline char * nfs4_recoverydir(void) {return NULL; }
|
||||||
static inline bool nfsd4_spo_must_allow(struct svc_rqst *rqstp)
|
static inline bool nfsd4_spo_must_allow(struct svc_rqst *rqstp)
|
||||||
|
@ -387,6 +386,37 @@ void nfsd_lockd_shutdown(void);
|
||||||
|
|
||||||
extern const u32 nfsd_suppattrs[3][3];
|
extern const u32 nfsd_suppattrs[3][3];
|
||||||
|
|
||||||
|
static inline __be32 nfsd4_set_netaddr(struct sockaddr *addr,
|
||||||
|
struct nfs42_netaddr *netaddr)
|
||||||
|
{
|
||||||
|
struct sockaddr_in *sin = (struct sockaddr_in *)addr;
|
||||||
|
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr;
|
||||||
|
unsigned int port;
|
||||||
|
size_t ret_addr, ret_port;
|
||||||
|
|
||||||
|
switch (addr->sa_family) {
|
||||||
|
case AF_INET:
|
||||||
|
port = ntohs(sin->sin_port);
|
||||||
|
sprintf(netaddr->netid, "tcp");
|
||||||
|
netaddr->netid_len = 3;
|
||||||
|
break;
|
||||||
|
case AF_INET6:
|
||||||
|
port = ntohs(sin6->sin6_port);
|
||||||
|
sprintf(netaddr->netid, "tcp6");
|
||||||
|
netaddr->netid_len = 4;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return nfserr_inval;
|
||||||
|
}
|
||||||
|
ret_addr = rpc_ntop(addr, netaddr->addr, sizeof(netaddr->addr));
|
||||||
|
ret_port = snprintf(netaddr->addr + ret_addr,
|
||||||
|
RPCBIND_MAXUADDRLEN + 1 - ret_addr,
|
||||||
|
".%u.%u", port >> 8, port & 0xff);
|
||||||
|
WARN_ON(ret_port >= RPCBIND_MAXUADDRLEN + 1 - ret_addr);
|
||||||
|
netaddr->addr_len = ret_addr + ret_port;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static inline bool bmval_is_subset(const u32 *bm1, const u32 *bm2)
|
static inline bool bmval_is_subset(const u32 *bm1, const u32 *bm2)
|
||||||
{
|
{
|
||||||
return !((bm1[0] & ~bm2[0]) ||
|
return !((bm1[0] & ~bm2[0]) ||
|
||||||
|
|
|
@ -35,15 +35,15 @@ typedef struct svc_fh {
|
||||||
|
|
||||||
bool fh_locked; /* inode locked by us */
|
bool fh_locked; /* inode locked by us */
|
||||||
bool fh_want_write; /* remount protection taken */
|
bool fh_want_write; /* remount protection taken */
|
||||||
|
int fh_flags; /* FH flags */
|
||||||
#ifdef CONFIG_NFSD_V3
|
#ifdef CONFIG_NFSD_V3
|
||||||
bool fh_post_saved; /* post-op attrs saved */
|
bool fh_post_saved; /* post-op attrs saved */
|
||||||
bool fh_pre_saved; /* pre-op attrs saved */
|
bool fh_pre_saved; /* pre-op attrs saved */
|
||||||
|
|
||||||
/* Pre-op attributes saved during fh_lock */
|
/* Pre-op attributes saved during fh_lock */
|
||||||
__u64 fh_pre_size; /* size before operation */
|
__u64 fh_pre_size; /* size before operation */
|
||||||
struct timespec fh_pre_mtime; /* mtime before oper */
|
struct timespec64 fh_pre_mtime; /* mtime before oper */
|
||||||
struct timespec fh_pre_ctime; /* ctime before oper */
|
struct timespec64 fh_pre_ctime; /* ctime before oper */
|
||||||
/*
|
/*
|
||||||
* pre-op nfsv4 change attr: note must check IS_I_VERSION(inode)
|
* pre-op nfsv4 change attr: note must check IS_I_VERSION(inode)
|
||||||
* to find out if it is valid.
|
* to find out if it is valid.
|
||||||
|
@ -56,6 +56,9 @@ typedef struct svc_fh {
|
||||||
#endif /* CONFIG_NFSD_V3 */
|
#endif /* CONFIG_NFSD_V3 */
|
||||||
|
|
||||||
} svc_fh;
|
} svc_fh;
|
||||||
|
#define NFSD4_FH_FOREIGN (1<<0)
|
||||||
|
#define SET_FH_FLAG(c, f) ((c)->fh_flags |= (f))
|
||||||
|
#define HAS_FH_FLAG(c, f) ((c)->fh_flags & (f))
|
||||||
|
|
||||||
enum nfsd_fsid {
|
enum nfsd_fsid {
|
||||||
FSID_DEV = 0,
|
FSID_DEV = 0,
|
||||||
|
|
|
@ -94,7 +94,7 @@ nfsd_proc_setattr(struct svc_rqst *rqstp)
|
||||||
* Solaris, at least, doesn't seem to care what the time
|
* Solaris, at least, doesn't seem to care what the time
|
||||||
* request is. We require it be within 30 minutes of now.
|
* request is. We require it be within 30 minutes of now.
|
||||||
*/
|
*/
|
||||||
time_t delta = iap->ia_atime.tv_sec - get_seconds();
|
time64_t delta = iap->ia_atime.tv_sec - ktime_get_real_seconds();
|
||||||
|
|
||||||
nfserr = fh_verify(rqstp, fhp, 0, NFSD_MAY_NOP);
|
nfserr = fh_verify(rqstp, fhp, 0, NFSD_MAY_NOP);
|
||||||
if (nfserr)
|
if (nfserr)
|
||||||
|
@ -113,7 +113,7 @@ nfsd_proc_setattr(struct svc_rqst *rqstp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nfserr = nfsd_setattr(rqstp, fhp, iap, 0, (time_t)0);
|
nfserr = nfsd_setattr(rqstp, fhp, iap, 0, (time64_t)0);
|
||||||
done:
|
done:
|
||||||
return nfsd_return_attrs(nfserr, resp);
|
return nfsd_return_attrs(nfserr, resp);
|
||||||
}
|
}
|
||||||
|
@ -226,7 +226,7 @@ nfsd_proc_write(struct svc_rqst *rqstp)
|
||||||
return nfserr_io;
|
return nfserr_io;
|
||||||
nfserr = nfsd_write(rqstp, fh_copy(&resp->fh, &argp->fh),
|
nfserr = nfsd_write(rqstp, fh_copy(&resp->fh, &argp->fh),
|
||||||
argp->offset, rqstp->rq_vec, nvecs,
|
argp->offset, rqstp->rq_vec, nvecs,
|
||||||
&cnt, NFS_DATA_SYNC);
|
&cnt, NFS_DATA_SYNC, NULL);
|
||||||
return nfsd_return_attrs(nfserr, resp);
|
return nfsd_return_attrs(nfserr, resp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -380,7 +380,7 @@ nfsd_proc_create(struct svc_rqst *rqstp)
|
||||||
*/
|
*/
|
||||||
attr->ia_valid &= ATTR_SIZE;
|
attr->ia_valid &= ATTR_SIZE;
|
||||||
if (attr->ia_valid)
|
if (attr->ia_valid)
|
||||||
nfserr = nfsd_setattr(rqstp, newfhp, attr, 0, (time_t)0);
|
nfserr = nfsd_setattr(rqstp, newfhp, attr, 0, (time64_t)0);
|
||||||
}
|
}
|
||||||
|
|
||||||
out_unlock:
|
out_unlock:
|
||||||
|
|
|
@ -31,6 +31,12 @@
|
||||||
|
|
||||||
#define NFSDDBG_FACILITY NFSDDBG_SVC
|
#define NFSDDBG_FACILITY NFSDDBG_SVC
|
||||||
|
|
||||||
|
bool inter_copy_offload_enable;
|
||||||
|
EXPORT_SYMBOL_GPL(inter_copy_offload_enable);
|
||||||
|
module_param(inter_copy_offload_enable, bool, 0644);
|
||||||
|
MODULE_PARM_DESC(inter_copy_offload_enable,
|
||||||
|
"Enable inter server to server copy offload. Default: false");
|
||||||
|
|
||||||
extern struct svc_program nfsd_program;
|
extern struct svc_program nfsd_program;
|
||||||
static int nfsd(void *vrqstp);
|
static int nfsd(void *vrqstp);
|
||||||
#if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
|
#if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
|
||||||
|
@ -391,20 +397,25 @@ static int nfsd_startup_net(int nrservs, struct net *net, const struct cred *cre
|
||||||
ret = lockd_up(net, cred);
|
ret = lockd_up(net, cred);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_socks;
|
goto out_socks;
|
||||||
nn->lockd_up = 1;
|
nn->lockd_up = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = nfs4_state_start_net(net);
|
ret = nfsd_file_cache_start_net(net);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_lockd;
|
goto out_lockd;
|
||||||
|
ret = nfs4_state_start_net(net);
|
||||||
|
if (ret)
|
||||||
|
goto out_filecache;
|
||||||
|
|
||||||
nn->nfsd_net_up = true;
|
nn->nfsd_net_up = true;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
out_filecache:
|
||||||
|
nfsd_file_cache_shutdown_net(net);
|
||||||
out_lockd:
|
out_lockd:
|
||||||
if (nn->lockd_up) {
|
if (nn->lockd_up) {
|
||||||
lockd_down(net);
|
lockd_down(net);
|
||||||
nn->lockd_up = 0;
|
nn->lockd_up = false;
|
||||||
}
|
}
|
||||||
out_socks:
|
out_socks:
|
||||||
nfsd_shutdown_generic();
|
nfsd_shutdown_generic();
|
||||||
|
@ -415,11 +426,11 @@ static void nfsd_shutdown_net(struct net *net)
|
||||||
{
|
{
|
||||||
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
|
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
|
||||||
|
|
||||||
nfsd_file_cache_purge(net);
|
nfsd_file_cache_shutdown_net(net);
|
||||||
nfs4_state_shutdown_net(net);
|
nfs4_state_shutdown_net(net);
|
||||||
if (nn->lockd_up) {
|
if (nn->lockd_up) {
|
||||||
lockd_down(net);
|
lockd_down(net);
|
||||||
nn->lockd_up = 0;
|
nn->lockd_up = false;
|
||||||
}
|
}
|
||||||
nn->nfsd_net_up = false;
|
nn->nfsd_net_up = false;
|
||||||
nfsd_shutdown_generic();
|
nfsd_shutdown_generic();
|
||||||
|
|
|
@ -56,6 +56,14 @@ typedef struct {
|
||||||
stateid_opaque_t si_opaque;
|
stateid_opaque_t si_opaque;
|
||||||
} stateid_t;
|
} stateid_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
stateid_t stid;
|
||||||
|
#define NFS4_COPY_STID 1
|
||||||
|
#define NFS4_COPYNOTIFY_STID 2
|
||||||
|
unsigned char sc_type;
|
||||||
|
refcount_t sc_count;
|
||||||
|
} copy_stateid_t;
|
||||||
|
|
||||||
#define STATEID_FMT "(%08x/%08x/%08x/%08x)"
|
#define STATEID_FMT "(%08x/%08x/%08x/%08x)"
|
||||||
#define STATEID_VAL(s) \
|
#define STATEID_VAL(s) \
|
||||||
(s)->si_opaque.so_clid.cl_boot, \
|
(s)->si_opaque.so_clid.cl_boot, \
|
||||||
|
@ -96,6 +104,7 @@ struct nfs4_stid {
|
||||||
#define NFS4_REVOKED_DELEG_STID 16
|
#define NFS4_REVOKED_DELEG_STID 16
|
||||||
#define NFS4_CLOSED_DELEG_STID 32
|
#define NFS4_CLOSED_DELEG_STID 32
|
||||||
#define NFS4_LAYOUT_STID 64
|
#define NFS4_LAYOUT_STID 64
|
||||||
|
struct list_head sc_cp_list;
|
||||||
unsigned char sc_type;
|
unsigned char sc_type;
|
||||||
stateid_t sc_stateid;
|
stateid_t sc_stateid;
|
||||||
spinlock_t sc_lock;
|
spinlock_t sc_lock;
|
||||||
|
@ -104,6 +113,17 @@ struct nfs4_stid {
|
||||||
void (*sc_free)(struct nfs4_stid *);
|
void (*sc_free)(struct nfs4_stid *);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Keep a list of stateids issued by the COPY_NOTIFY, associate it with the
|
||||||
|
* parent OPEN/LOCK/DELEG stateid.
|
||||||
|
*/
|
||||||
|
struct nfs4_cpntf_state {
|
||||||
|
copy_stateid_t cp_stateid;
|
||||||
|
struct list_head cp_list; /* per parent nfs4_stid */
|
||||||
|
stateid_t cp_p_stateid; /* copy of parent's stateid */
|
||||||
|
clientid_t cp_p_clid; /* copy of parent's clid */
|
||||||
|
time64_t cpntf_time; /* last time stateid used */
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Represents a delegation stateid. The nfs4_client holds references to these
|
* Represents a delegation stateid. The nfs4_client holds references to these
|
||||||
* and they are put when it is being destroyed or when the delegation is
|
* and they are put when it is being destroyed or when the delegation is
|
||||||
|
@ -132,7 +152,7 @@ struct nfs4_delegation {
|
||||||
struct list_head dl_recall_lru; /* delegation recalled */
|
struct list_head dl_recall_lru; /* delegation recalled */
|
||||||
struct nfs4_clnt_odstate *dl_clnt_odstate;
|
struct nfs4_clnt_odstate *dl_clnt_odstate;
|
||||||
u32 dl_type;
|
u32 dl_type;
|
||||||
time_t dl_time;
|
time64_t dl_time;
|
||||||
/* For recall: */
|
/* For recall: */
|
||||||
int dl_retries;
|
int dl_retries;
|
||||||
struct nfsd4_callback dl_recall;
|
struct nfsd4_callback dl_recall;
|
||||||
|
@ -310,7 +330,7 @@ struct nfs4_client {
|
||||||
#endif
|
#endif
|
||||||
struct xdr_netobj cl_name; /* id generated by client */
|
struct xdr_netobj cl_name; /* id generated by client */
|
||||||
nfs4_verifier cl_verifier; /* generated by client */
|
nfs4_verifier cl_verifier; /* generated by client */
|
||||||
time_t cl_time; /* time of last lease renewal */
|
time64_t cl_time; /* time of last lease renewal */
|
||||||
struct sockaddr_storage cl_addr; /* client ipaddress */
|
struct sockaddr_storage cl_addr; /* client ipaddress */
|
||||||
bool cl_mach_cred; /* SP4_MACH_CRED in force */
|
bool cl_mach_cred; /* SP4_MACH_CRED in force */
|
||||||
struct svc_cred cl_cred; /* setclientid principal */
|
struct svc_cred cl_cred; /* setclientid principal */
|
||||||
|
@ -320,7 +340,7 @@ struct nfs4_client {
|
||||||
/* NFSv4.1 client implementation id: */
|
/* NFSv4.1 client implementation id: */
|
||||||
struct xdr_netobj cl_nii_domain;
|
struct xdr_netobj cl_nii_domain;
|
||||||
struct xdr_netobj cl_nii_name;
|
struct xdr_netobj cl_nii_name;
|
||||||
struct timespec cl_nii_time;
|
struct timespec64 cl_nii_time;
|
||||||
|
|
||||||
/* for v4.0 and v4.1 callbacks: */
|
/* for v4.0 and v4.1 callbacks: */
|
||||||
struct nfs4_cb_conn cl_cb_conn;
|
struct nfs4_cb_conn cl_cb_conn;
|
||||||
|
@ -449,7 +469,7 @@ struct nfs4_openowner {
|
||||||
*/
|
*/
|
||||||
struct list_head oo_close_lru;
|
struct list_head oo_close_lru;
|
||||||
struct nfs4_ol_stateid *oo_last_closed_stid;
|
struct nfs4_ol_stateid *oo_last_closed_stid;
|
||||||
time_t oo_time; /* time of placement on so_close_lru */
|
time64_t oo_time; /* time of placement on so_close_lru */
|
||||||
#define NFS4_OO_CONFIRMED 1
|
#define NFS4_OO_CONFIRMED 1
|
||||||
unsigned char oo_flags;
|
unsigned char oo_flags;
|
||||||
};
|
};
|
||||||
|
@ -606,7 +626,7 @@ static inline bool nfsd4_stateid_generation_after(stateid_t *a, stateid_t *b)
|
||||||
struct nfsd4_blocked_lock {
|
struct nfsd4_blocked_lock {
|
||||||
struct list_head nbl_list;
|
struct list_head nbl_list;
|
||||||
struct list_head nbl_lru;
|
struct list_head nbl_lru;
|
||||||
unsigned long nbl_time;
|
time64_t nbl_time;
|
||||||
struct file_lock nbl_lock;
|
struct file_lock nbl_lock;
|
||||||
struct knfsd_fh nbl_fh;
|
struct knfsd_fh nbl_fh;
|
||||||
struct nfsd4_callback nbl_cb;
|
struct nfsd4_callback nbl_cb;
|
||||||
|
@ -618,14 +638,17 @@ struct nfsd4_copy;
|
||||||
|
|
||||||
extern __be32 nfs4_preprocess_stateid_op(struct svc_rqst *rqstp,
|
extern __be32 nfs4_preprocess_stateid_op(struct svc_rqst *rqstp,
|
||||||
struct nfsd4_compound_state *cstate, struct svc_fh *fhp,
|
struct nfsd4_compound_state *cstate, struct svc_fh *fhp,
|
||||||
stateid_t *stateid, int flags, struct nfsd_file **filp);
|
stateid_t *stateid, int flags, struct nfsd_file **filp,
|
||||||
|
struct nfs4_stid **cstid);
|
||||||
__be32 nfsd4_lookup_stateid(struct nfsd4_compound_state *cstate,
|
__be32 nfsd4_lookup_stateid(struct nfsd4_compound_state *cstate,
|
||||||
stateid_t *stateid, unsigned char typemask,
|
stateid_t *stateid, unsigned char typemask,
|
||||||
struct nfs4_stid **s, struct nfsd_net *nn);
|
struct nfs4_stid **s, struct nfsd_net *nn);
|
||||||
struct nfs4_stid *nfs4_alloc_stid(struct nfs4_client *cl, struct kmem_cache *slab,
|
struct nfs4_stid *nfs4_alloc_stid(struct nfs4_client *cl, struct kmem_cache *slab,
|
||||||
void (*sc_free)(struct nfs4_stid *));
|
void (*sc_free)(struct nfs4_stid *));
|
||||||
int nfs4_init_cp_state(struct nfsd_net *nn, struct nfsd4_copy *copy);
|
int nfs4_init_copy_state(struct nfsd_net *nn, struct nfsd4_copy *copy);
|
||||||
void nfs4_free_cp_state(struct nfsd4_copy *copy);
|
void nfs4_free_copy_state(struct nfsd4_copy *copy);
|
||||||
|
struct nfs4_cpntf_state *nfs4_alloc_init_cpntf_state(struct nfsd_net *nn,
|
||||||
|
struct nfs4_stid *p_stid);
|
||||||
void nfs4_unhash_stid(struct nfs4_stid *s);
|
void nfs4_unhash_stid(struct nfs4_stid *s);
|
||||||
void nfs4_put_stid(struct nfs4_stid *s);
|
void nfs4_put_stid(struct nfs4_stid *s);
|
||||||
void nfs4_inc_and_copy_stateid(stateid_t *dst, struct nfs4_stid *stid);
|
void nfs4_inc_and_copy_stateid(stateid_t *dst, struct nfs4_stid *stid);
|
||||||
|
@ -655,6 +678,11 @@ void put_nfs4_file(struct nfs4_file *fi);
|
||||||
extern void nfs4_put_copy(struct nfsd4_copy *copy);
|
extern void nfs4_put_copy(struct nfsd4_copy *copy);
|
||||||
extern struct nfsd4_copy *
|
extern struct nfsd4_copy *
|
||||||
find_async_copy(struct nfs4_client *clp, stateid_t *staetid);
|
find_async_copy(struct nfs4_client *clp, stateid_t *staetid);
|
||||||
|
extern void nfs4_put_cpntf_state(struct nfsd_net *nn,
|
||||||
|
struct nfs4_cpntf_state *cps);
|
||||||
|
extern __be32 manage_cpntf_state(struct nfsd_net *nn, stateid_t *st,
|
||||||
|
struct nfs4_client *clp,
|
||||||
|
struct nfs4_cpntf_state **cps);
|
||||||
static inline void get_nfs4_file(struct nfs4_file *fi)
|
static inline void get_nfs4_file(struct nfs4_file *fi)
|
||||||
{
|
{
|
||||||
refcount_inc(&fi->fi_ref);
|
refcount_inc(&fi->fi_ref);
|
||||||
|
|
|
@ -166,6 +166,12 @@ DEFINE_STATEID_EVENT(layout_recall_done);
|
||||||
DEFINE_STATEID_EVENT(layout_recall_fail);
|
DEFINE_STATEID_EVENT(layout_recall_fail);
|
||||||
DEFINE_STATEID_EVENT(layout_recall_release);
|
DEFINE_STATEID_EVENT(layout_recall_release);
|
||||||
|
|
||||||
|
TRACE_DEFINE_ENUM(NFSD_FILE_HASHED);
|
||||||
|
TRACE_DEFINE_ENUM(NFSD_FILE_PENDING);
|
||||||
|
TRACE_DEFINE_ENUM(NFSD_FILE_BREAK_READ);
|
||||||
|
TRACE_DEFINE_ENUM(NFSD_FILE_BREAK_WRITE);
|
||||||
|
TRACE_DEFINE_ENUM(NFSD_FILE_REFERENCED);
|
||||||
|
|
||||||
#define show_nf_flags(val) \
|
#define show_nf_flags(val) \
|
||||||
__print_flags(val, "|", \
|
__print_flags(val, "|", \
|
||||||
{ 1 << NFSD_FILE_HASHED, "HASHED" }, \
|
{ 1 << NFSD_FILE_HASHED, "HASHED" }, \
|
||||||
|
@ -195,7 +201,7 @@ DECLARE_EVENT_CLASS(nfsd_file_class,
|
||||||
TP_fast_assign(
|
TP_fast_assign(
|
||||||
__entry->nf_hashval = nf->nf_hashval;
|
__entry->nf_hashval = nf->nf_hashval;
|
||||||
__entry->nf_inode = nf->nf_inode;
|
__entry->nf_inode = nf->nf_inode;
|
||||||
__entry->nf_ref = atomic_read(&nf->nf_ref);
|
__entry->nf_ref = refcount_read(&nf->nf_ref);
|
||||||
__entry->nf_flags = nf->nf_flags;
|
__entry->nf_flags = nf->nf_flags;
|
||||||
__entry->nf_may = nf->nf_may;
|
__entry->nf_may = nf->nf_may;
|
||||||
__entry->nf_file = nf->nf_file;
|
__entry->nf_file = nf->nf_file;
|
||||||
|
@ -228,7 +234,7 @@ TRACE_EVENT(nfsd_file_acquire,
|
||||||
TP_ARGS(rqstp, hash, inode, may_flags, nf, status),
|
TP_ARGS(rqstp, hash, inode, may_flags, nf, status),
|
||||||
|
|
||||||
TP_STRUCT__entry(
|
TP_STRUCT__entry(
|
||||||
__field(__be32, xid)
|
__field(u32, xid)
|
||||||
__field(unsigned int, hash)
|
__field(unsigned int, hash)
|
||||||
__field(void *, inode)
|
__field(void *, inode)
|
||||||
__field(unsigned int, may_flags)
|
__field(unsigned int, may_flags)
|
||||||
|
@ -236,27 +242,27 @@ TRACE_EVENT(nfsd_file_acquire,
|
||||||
__field(unsigned long, nf_flags)
|
__field(unsigned long, nf_flags)
|
||||||
__field(unsigned char, nf_may)
|
__field(unsigned char, nf_may)
|
||||||
__field(struct file *, nf_file)
|
__field(struct file *, nf_file)
|
||||||
__field(__be32, status)
|
__field(u32, status)
|
||||||
),
|
),
|
||||||
|
|
||||||
TP_fast_assign(
|
TP_fast_assign(
|
||||||
__entry->xid = rqstp->rq_xid;
|
__entry->xid = be32_to_cpu(rqstp->rq_xid);
|
||||||
__entry->hash = hash;
|
__entry->hash = hash;
|
||||||
__entry->inode = inode;
|
__entry->inode = inode;
|
||||||
__entry->may_flags = may_flags;
|
__entry->may_flags = may_flags;
|
||||||
__entry->nf_ref = nf ? atomic_read(&nf->nf_ref) : 0;
|
__entry->nf_ref = nf ? refcount_read(&nf->nf_ref) : 0;
|
||||||
__entry->nf_flags = nf ? nf->nf_flags : 0;
|
__entry->nf_flags = nf ? nf->nf_flags : 0;
|
||||||
__entry->nf_may = nf ? nf->nf_may : 0;
|
__entry->nf_may = nf ? nf->nf_may : 0;
|
||||||
__entry->nf_file = nf ? nf->nf_file : NULL;
|
__entry->nf_file = nf ? nf->nf_file : NULL;
|
||||||
__entry->status = status;
|
__entry->status = be32_to_cpu(status);
|
||||||
),
|
),
|
||||||
|
|
||||||
TP_printk("xid=0x%x hash=0x%x inode=0x%p may_flags=%s ref=%d nf_flags=%s nf_may=%s nf_file=0x%p status=%u",
|
TP_printk("xid=0x%x hash=0x%x inode=0x%p may_flags=%s ref=%d nf_flags=%s nf_may=%s nf_file=0x%p status=%u",
|
||||||
be32_to_cpu(__entry->xid), __entry->hash, __entry->inode,
|
__entry->xid, __entry->hash, __entry->inode,
|
||||||
show_nf_may(__entry->may_flags), __entry->nf_ref,
|
show_nf_may(__entry->may_flags), __entry->nf_ref,
|
||||||
show_nf_flags(__entry->nf_flags),
|
show_nf_flags(__entry->nf_flags),
|
||||||
show_nf_may(__entry->nf_may), __entry->nf_file,
|
show_nf_may(__entry->nf_may), __entry->nf_file,
|
||||||
be32_to_cpu(__entry->status))
|
__entry->status)
|
||||||
);
|
);
|
||||||
|
|
||||||
DECLARE_EVENT_CLASS(nfsd_file_search_class,
|
DECLARE_EVENT_CLASS(nfsd_file_search_class,
|
||||||
|
|
109
fs/nfsd/vfs.c
109
fs/nfsd/vfs.c
|
@ -280,19 +280,25 @@ out:
|
||||||
* Commit metadata changes to stable storage.
|
* Commit metadata changes to stable storage.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
commit_metadata(struct svc_fh *fhp)
|
commit_inode_metadata(struct inode *inode)
|
||||||
{
|
{
|
||||||
struct inode *inode = d_inode(fhp->fh_dentry);
|
|
||||||
const struct export_operations *export_ops = inode->i_sb->s_export_op;
|
const struct export_operations *export_ops = inode->i_sb->s_export_op;
|
||||||
|
|
||||||
if (!EX_ISSYNC(fhp->fh_export))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (export_ops->commit_metadata)
|
if (export_ops->commit_metadata)
|
||||||
return export_ops->commit_metadata(inode);
|
return export_ops->commit_metadata(inode);
|
||||||
return sync_inode_metadata(inode, 1);
|
return sync_inode_metadata(inode, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
commit_metadata(struct svc_fh *fhp)
|
||||||
|
{
|
||||||
|
struct inode *inode = d_inode(fhp->fh_dentry);
|
||||||
|
|
||||||
|
if (!EX_ISSYNC(fhp->fh_export))
|
||||||
|
return 0;
|
||||||
|
return commit_inode_metadata(inode);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Go over the attributes and take care of the small differences between
|
* Go over the attributes and take care of the small differences between
|
||||||
* NFS semantics and what Linux expects.
|
* NFS semantics and what Linux expects.
|
||||||
|
@ -358,7 +364,7 @@ out_nfserrno:
|
||||||
*/
|
*/
|
||||||
__be32
|
__be32
|
||||||
nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
|
nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
|
||||||
int check_guard, time_t guardtime)
|
int check_guard, time64_t guardtime)
|
||||||
{
|
{
|
||||||
struct dentry *dentry;
|
struct dentry *dentry;
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
|
@ -524,23 +530,39 @@ __be32 nfsd4_set_nfs4_label(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
__be32 nfsd4_clone_file_range(struct file *src, u64 src_pos, struct file *dst,
|
__be32 nfsd4_clone_file_range(struct nfsd_file *nf_src, u64 src_pos,
|
||||||
u64 dst_pos, u64 count, bool sync)
|
struct nfsd_file *nf_dst, u64 dst_pos, u64 count, bool sync)
|
||||||
{
|
{
|
||||||
|
struct file *src = nf_src->nf_file;
|
||||||
|
struct file *dst = nf_dst->nf_file;
|
||||||
loff_t cloned;
|
loff_t cloned;
|
||||||
|
__be32 ret = 0;
|
||||||
|
|
||||||
|
down_write(&nf_dst->nf_rwsem);
|
||||||
cloned = vfs_clone_file_range(src, src_pos, dst, dst_pos, count, 0);
|
cloned = vfs_clone_file_range(src, src_pos, dst, dst_pos, count, 0);
|
||||||
if (cloned < 0)
|
if (cloned < 0) {
|
||||||
return nfserrno(cloned);
|
ret = nfserrno(cloned);
|
||||||
if (count && cloned != count)
|
goto out_err;
|
||||||
return nfserrno(-EINVAL);
|
}
|
||||||
|
if (count && cloned != count) {
|
||||||
|
ret = nfserrno(-EINVAL);
|
||||||
|
goto out_err;
|
||||||
|
}
|
||||||
if (sync) {
|
if (sync) {
|
||||||
loff_t dst_end = count ? dst_pos + count - 1 : LLONG_MAX;
|
loff_t dst_end = count ? dst_pos + count - 1 : LLONG_MAX;
|
||||||
int status = vfs_fsync_range(dst, dst_pos, dst_end, 0);
|
int status = vfs_fsync_range(dst, dst_pos, dst_end, 0);
|
||||||
if (status < 0)
|
|
||||||
return nfserrno(status);
|
if (!status)
|
||||||
|
status = commit_inode_metadata(file_inode(src));
|
||||||
|
if (status < 0) {
|
||||||
|
nfsd_reset_boot_verifier(net_generic(nf_dst->nf_net,
|
||||||
|
nfsd_net_id));
|
||||||
|
ret = nfserrno(status);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
out_err:
|
||||||
|
up_write(&nf_dst->nf_rwsem);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t nfsd_copy_file_range(struct file *src, u64 src_pos, struct file *dst,
|
ssize_t nfsd_copy_file_range(struct file *src, u64 src_pos, struct file *dst,
|
||||||
|
@ -938,10 +960,12 @@ static int wait_for_concurrent_writes(struct file *file)
|
||||||
}
|
}
|
||||||
|
|
||||||
__be32
|
__be32
|
||||||
nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
|
nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct nfsd_file *nf,
|
||||||
loff_t offset, struct kvec *vec, int vlen,
|
loff_t offset, struct kvec *vec, int vlen,
|
||||||
unsigned long *cnt, int stable)
|
unsigned long *cnt, int stable,
|
||||||
|
__be32 *verf)
|
||||||
{
|
{
|
||||||
|
struct file *file = nf->nf_file;
|
||||||
struct svc_export *exp;
|
struct svc_export *exp;
|
||||||
struct iov_iter iter;
|
struct iov_iter iter;
|
||||||
__be32 nfserr;
|
__be32 nfserr;
|
||||||
|
@ -972,9 +996,28 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
|
||||||
flags |= RWF_SYNC;
|
flags |= RWF_SYNC;
|
||||||
|
|
||||||
iov_iter_kvec(&iter, WRITE, vec, vlen, *cnt);
|
iov_iter_kvec(&iter, WRITE, vec, vlen, *cnt);
|
||||||
host_err = vfs_iter_write(file, &iter, &pos, flags);
|
if (flags & RWF_SYNC) {
|
||||||
if (host_err < 0)
|
down_write(&nf->nf_rwsem);
|
||||||
|
host_err = vfs_iter_write(file, &iter, &pos, flags);
|
||||||
|
if (host_err < 0)
|
||||||
|
nfsd_reset_boot_verifier(net_generic(SVC_NET(rqstp),
|
||||||
|
nfsd_net_id));
|
||||||
|
up_write(&nf->nf_rwsem);
|
||||||
|
} else {
|
||||||
|
down_read(&nf->nf_rwsem);
|
||||||
|
if (verf)
|
||||||
|
nfsd_copy_boot_verifier(verf,
|
||||||
|
net_generic(SVC_NET(rqstp),
|
||||||
|
nfsd_net_id));
|
||||||
|
host_err = vfs_iter_write(file, &iter, &pos, flags);
|
||||||
|
up_read(&nf->nf_rwsem);
|
||||||
|
}
|
||||||
|
if (host_err < 0) {
|
||||||
|
nfsd_reset_boot_verifier(net_generic(SVC_NET(rqstp),
|
||||||
|
nfsd_net_id));
|
||||||
goto out_nfserr;
|
goto out_nfserr;
|
||||||
|
}
|
||||||
|
*cnt = host_err;
|
||||||
nfsdstats.io_write += *cnt;
|
nfsdstats.io_write += *cnt;
|
||||||
fsnotify_modify(file);
|
fsnotify_modify(file);
|
||||||
|
|
||||||
|
@ -1036,7 +1079,8 @@ __be32 nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||||
*/
|
*/
|
||||||
__be32
|
__be32
|
||||||
nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
|
nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
|
||||||
struct kvec *vec, int vlen, unsigned long *cnt, int stable)
|
struct kvec *vec, int vlen, unsigned long *cnt, int stable,
|
||||||
|
__be32 *verf)
|
||||||
{
|
{
|
||||||
struct nfsd_file *nf;
|
struct nfsd_file *nf;
|
||||||
__be32 err;
|
__be32 err;
|
||||||
|
@ -1047,8 +1091,8 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
err = nfsd_vfs_write(rqstp, fhp, nf->nf_file, offset, vec,
|
err = nfsd_vfs_write(rqstp, fhp, nf, offset, vec,
|
||||||
vlen, cnt, stable);
|
vlen, cnt, stable, verf);
|
||||||
nfsd_file_put(nf);
|
nfsd_file_put(nf);
|
||||||
out:
|
out:
|
||||||
trace_nfsd_write_done(rqstp, fhp, offset, *cnt);
|
trace_nfsd_write_done(rqstp, fhp, offset, *cnt);
|
||||||
|
@ -1067,7 +1111,7 @@ out:
|
||||||
*/
|
*/
|
||||||
__be32
|
__be32
|
||||||
nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||||
loff_t offset, unsigned long count)
|
loff_t offset, unsigned long count, __be32 *verf)
|
||||||
{
|
{
|
||||||
struct nfsd_file *nf;
|
struct nfsd_file *nf;
|
||||||
loff_t end = LLONG_MAX;
|
loff_t end = LLONG_MAX;
|
||||||
|
@ -1086,10 +1130,14 @@ nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
if (EX_ISSYNC(fhp->fh_export)) {
|
if (EX_ISSYNC(fhp->fh_export)) {
|
||||||
int err2 = vfs_fsync_range(nf->nf_file, offset, end, 0);
|
int err2;
|
||||||
|
|
||||||
|
down_write(&nf->nf_rwsem);
|
||||||
|
err2 = vfs_fsync_range(nf->nf_file, offset, end, 0);
|
||||||
switch (err2) {
|
switch (err2) {
|
||||||
case 0:
|
case 0:
|
||||||
|
nfsd_copy_boot_verifier(verf, net_generic(nf->nf_net,
|
||||||
|
nfsd_net_id));
|
||||||
break;
|
break;
|
||||||
case -EINVAL:
|
case -EINVAL:
|
||||||
err = nfserr_notsupp;
|
err = nfserr_notsupp;
|
||||||
|
@ -1099,7 +1147,10 @@ nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||||
nfsd_reset_boot_verifier(net_generic(nf->nf_net,
|
nfsd_reset_boot_verifier(net_generic(nf->nf_net,
|
||||||
nfsd_net_id));
|
nfsd_net_id));
|
||||||
}
|
}
|
||||||
}
|
up_write(&nf->nf_rwsem);
|
||||||
|
} else
|
||||||
|
nfsd_copy_boot_verifier(verf, net_generic(nf->nf_net,
|
||||||
|
nfsd_net_id));
|
||||||
|
|
||||||
nfsd_file_put(nf);
|
nfsd_file_put(nf);
|
||||||
out:
|
out:
|
||||||
|
@ -1123,7 +1174,7 @@ nfsd_create_setattr(struct svc_rqst *rqstp, struct svc_fh *resfhp,
|
||||||
if (!uid_eq(current_fsuid(), GLOBAL_ROOT_UID))
|
if (!uid_eq(current_fsuid(), GLOBAL_ROOT_UID))
|
||||||
iap->ia_valid &= ~(ATTR_UID|ATTR_GID);
|
iap->ia_valid &= ~(ATTR_UID|ATTR_GID);
|
||||||
if (iap->ia_valid)
|
if (iap->ia_valid)
|
||||||
return nfsd_setattr(rqstp, resfhp, iap, 0, (time_t)0);
|
return nfsd_setattr(rqstp, resfhp, iap, 0, (time64_t)0);
|
||||||
/* Callers expect file metadata to be committed here */
|
/* Callers expect file metadata to be committed here */
|
||||||
return nfserrno(commit_metadata(resfhp));
|
return nfserrno(commit_metadata(resfhp));
|
||||||
}
|
}
|
||||||
|
@ -1386,7 +1437,7 @@ do_nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||||
&& d_inode(dchild)->i_atime.tv_sec == v_atime
|
&& d_inode(dchild)->i_atime.tv_sec == v_atime
|
||||||
&& d_inode(dchild)->i_size == 0 ) {
|
&& d_inode(dchild)->i_size == 0 ) {
|
||||||
if (created)
|
if (created)
|
||||||
*created = 1;
|
*created = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* fall through */
|
/* fall through */
|
||||||
|
@ -1395,7 +1446,7 @@ do_nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||||
&& d_inode(dchild)->i_atime.tv_sec == v_atime
|
&& d_inode(dchild)->i_atime.tv_sec == v_atime
|
||||||
&& d_inode(dchild)->i_size == 0 ) {
|
&& d_inode(dchild)->i_size == 0 ) {
|
||||||
if (created)
|
if (created)
|
||||||
*created = 1;
|
*created = true;
|
||||||
goto set_attr;
|
goto set_attr;
|
||||||
}
|
}
|
||||||
/* fall through */
|
/* fall through */
|
||||||
|
@ -1412,7 +1463,7 @@ do_nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||||
goto out_nfserr;
|
goto out_nfserr;
|
||||||
}
|
}
|
||||||
if (created)
|
if (created)
|
||||||
*created = 1;
|
*created = true;
|
||||||
|
|
||||||
nfsd_check_ignore_resizing(iap);
|
nfsd_check_ignore_resizing(iap);
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,8 @@
|
||||||
#define NFSD_MAY_CREATE (NFSD_MAY_EXEC|NFSD_MAY_WRITE)
|
#define NFSD_MAY_CREATE (NFSD_MAY_EXEC|NFSD_MAY_WRITE)
|
||||||
#define NFSD_MAY_REMOVE (NFSD_MAY_EXEC|NFSD_MAY_WRITE|NFSD_MAY_TRUNC)
|
#define NFSD_MAY_REMOVE (NFSD_MAY_EXEC|NFSD_MAY_WRITE|NFSD_MAY_TRUNC)
|
||||||
|
|
||||||
|
struct nfsd_file;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Callback function for readdir
|
* Callback function for readdir
|
||||||
*/
|
*/
|
||||||
|
@ -48,15 +50,16 @@ __be32 nfsd_lookup_dentry(struct svc_rqst *, struct svc_fh *,
|
||||||
const char *, unsigned int,
|
const char *, unsigned int,
|
||||||
struct svc_export **, struct dentry **);
|
struct svc_export **, struct dentry **);
|
||||||
__be32 nfsd_setattr(struct svc_rqst *, struct svc_fh *,
|
__be32 nfsd_setattr(struct svc_rqst *, struct svc_fh *,
|
||||||
struct iattr *, int, time_t);
|
struct iattr *, int, time64_t);
|
||||||
int nfsd_mountpoint(struct dentry *, struct svc_export *);
|
int nfsd_mountpoint(struct dentry *, struct svc_export *);
|
||||||
#ifdef CONFIG_NFSD_V4
|
#ifdef CONFIG_NFSD_V4
|
||||||
__be32 nfsd4_set_nfs4_label(struct svc_rqst *, struct svc_fh *,
|
__be32 nfsd4_set_nfs4_label(struct svc_rqst *, struct svc_fh *,
|
||||||
struct xdr_netobj *);
|
struct xdr_netobj *);
|
||||||
__be32 nfsd4_vfs_fallocate(struct svc_rqst *, struct svc_fh *,
|
__be32 nfsd4_vfs_fallocate(struct svc_rqst *, struct svc_fh *,
|
||||||
struct file *, loff_t, loff_t, int);
|
struct file *, loff_t, loff_t, int);
|
||||||
__be32 nfsd4_clone_file_range(struct file *, u64, struct file *,
|
__be32 nfsd4_clone_file_range(struct nfsd_file *nf_src, u64 src_pos,
|
||||||
u64, u64, bool);
|
struct nfsd_file *nf_dst, u64 dst_pos,
|
||||||
|
u64 count, bool sync);
|
||||||
#endif /* CONFIG_NFSD_V4 */
|
#endif /* CONFIG_NFSD_V4 */
|
||||||
__be32 nfsd_create_locked(struct svc_rqst *, struct svc_fh *,
|
__be32 nfsd_create_locked(struct svc_rqst *, struct svc_fh *,
|
||||||
char *name, int len, struct iattr *attrs,
|
char *name, int len, struct iattr *attrs,
|
||||||
|
@ -71,7 +74,7 @@ __be32 do_nfsd_create(struct svc_rqst *, struct svc_fh *,
|
||||||
struct svc_fh *res, int createmode,
|
struct svc_fh *res, int createmode,
|
||||||
u32 *verifier, bool *truncp, bool *created);
|
u32 *verifier, bool *truncp, bool *created);
|
||||||
__be32 nfsd_commit(struct svc_rqst *, struct svc_fh *,
|
__be32 nfsd_commit(struct svc_rqst *, struct svc_fh *,
|
||||||
loff_t, unsigned long);
|
loff_t, unsigned long, __be32 *verf);
|
||||||
#endif /* CONFIG_NFSD_V3 */
|
#endif /* CONFIG_NFSD_V3 */
|
||||||
int nfsd_open_break_lease(struct inode *, int);
|
int nfsd_open_break_lease(struct inode *, int);
|
||||||
__be32 nfsd_open(struct svc_rqst *, struct svc_fh *, umode_t,
|
__be32 nfsd_open(struct svc_rqst *, struct svc_fh *, umode_t,
|
||||||
|
@ -91,11 +94,12 @@ __be32 nfsd_read(struct svc_rqst *, struct svc_fh *,
|
||||||
loff_t, struct kvec *, int, unsigned long *,
|
loff_t, struct kvec *, int, unsigned long *,
|
||||||
u32 *eof);
|
u32 *eof);
|
||||||
__be32 nfsd_write(struct svc_rqst *, struct svc_fh *, loff_t,
|
__be32 nfsd_write(struct svc_rqst *, struct svc_fh *, loff_t,
|
||||||
struct kvec *, int, unsigned long *, int);
|
struct kvec *, int, unsigned long *,
|
||||||
|
int stable, __be32 *verf);
|
||||||
__be32 nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
__be32 nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||||
struct file *file, loff_t offset,
|
struct nfsd_file *nf, loff_t offset,
|
||||||
struct kvec *vec, int vlen, unsigned long *cnt,
|
struct kvec *vec, int vlen, unsigned long *cnt,
|
||||||
int stable);
|
int stable, __be32 *verf);
|
||||||
__be32 nfsd_readlink(struct svc_rqst *, struct svc_fh *,
|
__be32 nfsd_readlink(struct svc_rqst *, struct svc_fh *,
|
||||||
char *, int *);
|
char *, int *);
|
||||||
__be32 nfsd_symlink(struct svc_rqst *, struct svc_fh *,
|
__be32 nfsd_symlink(struct svc_rqst *, struct svc_fh *,
|
||||||
|
|
|
@ -14,7 +14,7 @@ struct nfsd3_sattrargs {
|
||||||
struct svc_fh fh;
|
struct svc_fh fh;
|
||||||
struct iattr attrs;
|
struct iattr attrs;
|
||||||
int check_guard;
|
int check_guard;
|
||||||
time_t guardtime;
|
time64_t guardtime;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct nfsd3_diropargs {
|
struct nfsd3_diropargs {
|
||||||
|
@ -159,6 +159,7 @@ struct nfsd3_writeres {
|
||||||
struct svc_fh fh;
|
struct svc_fh fh;
|
||||||
unsigned long count;
|
unsigned long count;
|
||||||
int committed;
|
int committed;
|
||||||
|
__be32 verf[2];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct nfsd3_renameres {
|
struct nfsd3_renameres {
|
||||||
|
@ -223,6 +224,7 @@ struct nfsd3_pathconfres {
|
||||||
struct nfsd3_commitres {
|
struct nfsd3_commitres {
|
||||||
__be32 status;
|
__be32 status;
|
||||||
struct svc_fh fh;
|
struct svc_fh fh;
|
||||||
|
__be32 verf[2];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct nfsd3_getaclres {
|
struct nfsd3_getaclres {
|
||||||
|
|
|
@ -46,9 +46,9 @@
|
||||||
#define CURRENT_STATE_ID_FLAG (1<<0)
|
#define CURRENT_STATE_ID_FLAG (1<<0)
|
||||||
#define SAVED_STATE_ID_FLAG (1<<1)
|
#define SAVED_STATE_ID_FLAG (1<<1)
|
||||||
|
|
||||||
#define SET_STATE_ID(c, f) ((c)->sid_flags |= (f))
|
#define SET_CSTATE_FLAG(c, f) ((c)->sid_flags |= (f))
|
||||||
#define HAS_STATE_ID(c, f) ((c)->sid_flags & (f))
|
#define HAS_CSTATE_FLAG(c, f) ((c)->sid_flags & (f))
|
||||||
#define CLEAR_STATE_ID(c, f) ((c)->sid_flags &= ~(f))
|
#define CLEAR_CSTATE_FLAG(c, f) ((c)->sid_flags &= ~(f))
|
||||||
|
|
||||||
struct nfsd4_compound_state {
|
struct nfsd4_compound_state {
|
||||||
struct svc_fh current_fh;
|
struct svc_fh current_fh;
|
||||||
|
@ -221,6 +221,7 @@ struct nfsd4_lookup {
|
||||||
struct nfsd4_putfh {
|
struct nfsd4_putfh {
|
||||||
u32 pf_fhlen; /* request */
|
u32 pf_fhlen; /* request */
|
||||||
char *pf_fhval; /* request */
|
char *pf_fhval; /* request */
|
||||||
|
bool no_verify; /* represents foreigh fh */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct nfsd4_open {
|
struct nfsd4_open {
|
||||||
|
@ -518,11 +519,13 @@ struct nfsd42_write_res {
|
||||||
|
|
||||||
struct nfsd4_copy {
|
struct nfsd4_copy {
|
||||||
/* request */
|
/* request */
|
||||||
stateid_t cp_src_stateid;
|
stateid_t cp_src_stateid;
|
||||||
stateid_t cp_dst_stateid;
|
stateid_t cp_dst_stateid;
|
||||||
u64 cp_src_pos;
|
u64 cp_src_pos;
|
||||||
u64 cp_dst_pos;
|
u64 cp_dst_pos;
|
||||||
u64 cp_count;
|
u64 cp_count;
|
||||||
|
struct nl4_server cp_src;
|
||||||
|
bool cp_intra;
|
||||||
|
|
||||||
/* both */
|
/* both */
|
||||||
bool cp_synchronous;
|
bool cp_synchronous;
|
||||||
|
@ -540,13 +543,18 @@ struct nfsd4_copy {
|
||||||
struct nfsd_file *nf_src;
|
struct nfsd_file *nf_src;
|
||||||
struct nfsd_file *nf_dst;
|
struct nfsd_file *nf_dst;
|
||||||
|
|
||||||
stateid_t cp_stateid;
|
copy_stateid_t cp_stateid;
|
||||||
|
|
||||||
struct list_head copies;
|
struct list_head copies;
|
||||||
struct task_struct *copy_task;
|
struct task_struct *copy_task;
|
||||||
refcount_t refcount;
|
refcount_t refcount;
|
||||||
bool stopped;
|
bool stopped;
|
||||||
|
|
||||||
|
struct vfsmount *ss_mnt;
|
||||||
|
struct nfs_fh c_fh;
|
||||||
|
nfs4_stateid stateid;
|
||||||
};
|
};
|
||||||
|
extern bool inter_copy_offload_enable;
|
||||||
|
|
||||||
struct nfsd4_seek {
|
struct nfsd4_seek {
|
||||||
/* request */
|
/* request */
|
||||||
|
@ -568,6 +576,18 @@ struct nfsd4_offload_status {
|
||||||
u32 status;
|
u32 status;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct nfsd4_copy_notify {
|
||||||
|
/* request */
|
||||||
|
stateid_t cpn_src_stateid;
|
||||||
|
struct nl4_server cpn_dst;
|
||||||
|
|
||||||
|
/* response */
|
||||||
|
stateid_t cpn_cnr_stateid;
|
||||||
|
u64 cpn_sec;
|
||||||
|
u32 cpn_nsec;
|
||||||
|
struct nl4_server cpn_src;
|
||||||
|
};
|
||||||
|
|
||||||
struct nfsd4_op {
|
struct nfsd4_op {
|
||||||
int opnum;
|
int opnum;
|
||||||
const struct nfsd4_operation * opdesc;
|
const struct nfsd4_operation * opdesc;
|
||||||
|
@ -627,6 +647,7 @@ struct nfsd4_op {
|
||||||
struct nfsd4_clone clone;
|
struct nfsd4_clone clone;
|
||||||
struct nfsd4_copy copy;
|
struct nfsd4_copy copy;
|
||||||
struct nfsd4_offload_status offload_status;
|
struct nfsd4_offload_status offload_status;
|
||||||
|
struct nfsd4_copy_notify copy_notify;
|
||||||
struct nfsd4_seek seek;
|
struct nfsd4_seek seek;
|
||||||
} u;
|
} u;
|
||||||
struct nfs4_replay * replay;
|
struct nfs4_replay * replay;
|
||||||
|
|
|
@ -1248,6 +1248,7 @@ static int gss_proxy_save_rsc(struct cache_detail *cd,
|
||||||
dprintk("RPC: No creds found!\n");
|
dprintk("RPC: No creds found!\n");
|
||||||
goto out;
|
goto out;
|
||||||
} else {
|
} else {
|
||||||
|
struct timespec64 boot;
|
||||||
|
|
||||||
/* steal creds */
|
/* steal creds */
|
||||||
rsci.cred = ud->creds;
|
rsci.cred = ud->creds;
|
||||||
|
@ -1268,6 +1269,9 @@ static int gss_proxy_save_rsc(struct cache_detail *cd,
|
||||||
&expiry, GFP_KERNEL);
|
&expiry, GFP_KERNEL);
|
||||||
if (status)
|
if (status)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
getboottime64(&boot);
|
||||||
|
expiry -= boot.tv_sec;
|
||||||
}
|
}
|
||||||
|
|
||||||
rsci.h.expiry_time = expiry;
|
rsci.h.expiry_time = expiry;
|
||||||
|
|
|
@ -77,6 +77,22 @@ static struct cache_head *sunrpc_cache_find_rcu(struct cache_detail *detail,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void sunrpc_begin_cache_remove_entry(struct cache_head *ch,
|
||||||
|
struct cache_detail *cd)
|
||||||
|
{
|
||||||
|
/* Must be called under cd->hash_lock */
|
||||||
|
hlist_del_init_rcu(&ch->cache_list);
|
||||||
|
set_bit(CACHE_CLEANED, &ch->flags);
|
||||||
|
cd->entries --;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sunrpc_end_cache_remove_entry(struct cache_head *ch,
|
||||||
|
struct cache_detail *cd)
|
||||||
|
{
|
||||||
|
cache_fresh_unlocked(ch, cd);
|
||||||
|
cache_put(ch, cd);
|
||||||
|
}
|
||||||
|
|
||||||
static struct cache_head *sunrpc_cache_add_entry(struct cache_detail *detail,
|
static struct cache_head *sunrpc_cache_add_entry(struct cache_detail *detail,
|
||||||
struct cache_head *key,
|
struct cache_head *key,
|
||||||
int hash)
|
int hash)
|
||||||
|
@ -100,8 +116,7 @@ static struct cache_head *sunrpc_cache_add_entry(struct cache_detail *detail,
|
||||||
hlist_for_each_entry_rcu(tmp, head, cache_list) {
|
hlist_for_each_entry_rcu(tmp, head, cache_list) {
|
||||||
if (detail->match(tmp, key)) {
|
if (detail->match(tmp, key)) {
|
||||||
if (cache_is_expired(detail, tmp)) {
|
if (cache_is_expired(detail, tmp)) {
|
||||||
hlist_del_init_rcu(&tmp->cache_list);
|
sunrpc_begin_cache_remove_entry(tmp, detail);
|
||||||
detail->entries --;
|
|
||||||
freeme = tmp;
|
freeme = tmp;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -117,10 +132,8 @@ static struct cache_head *sunrpc_cache_add_entry(struct cache_detail *detail,
|
||||||
cache_get(new);
|
cache_get(new);
|
||||||
spin_unlock(&detail->hash_lock);
|
spin_unlock(&detail->hash_lock);
|
||||||
|
|
||||||
if (freeme) {
|
if (freeme)
|
||||||
cache_fresh_unlocked(freeme, detail);
|
sunrpc_end_cache_remove_entry(freeme, detail);
|
||||||
cache_put(freeme, detail);
|
|
||||||
}
|
|
||||||
return new;
|
return new;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -454,8 +467,7 @@ static int cache_clean(void)
|
||||||
if (!cache_is_expired(current_detail, ch))
|
if (!cache_is_expired(current_detail, ch))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
hlist_del_init_rcu(&ch->cache_list);
|
sunrpc_begin_cache_remove_entry(ch, current_detail);
|
||||||
current_detail->entries--;
|
|
||||||
rv = 1;
|
rv = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -465,11 +477,8 @@ static int cache_clean(void)
|
||||||
if (!ch)
|
if (!ch)
|
||||||
current_index ++;
|
current_index ++;
|
||||||
spin_unlock(&cache_list_lock);
|
spin_unlock(&cache_list_lock);
|
||||||
if (ch) {
|
if (ch)
|
||||||
set_bit(CACHE_CLEANED, &ch->flags);
|
sunrpc_end_cache_remove_entry(ch, d);
|
||||||
cache_fresh_unlocked(ch, d);
|
|
||||||
cache_put(ch, d);
|
|
||||||
}
|
|
||||||
} else
|
} else
|
||||||
spin_unlock(&cache_list_lock);
|
spin_unlock(&cache_list_lock);
|
||||||
|
|
||||||
|
@ -525,13 +534,9 @@ void cache_purge(struct cache_detail *detail)
|
||||||
for (i = 0; i < detail->hash_size; i++) {
|
for (i = 0; i < detail->hash_size; i++) {
|
||||||
head = &detail->hash_table[i];
|
head = &detail->hash_table[i];
|
||||||
hlist_for_each_entry_safe(ch, tmp, head, cache_list) {
|
hlist_for_each_entry_safe(ch, tmp, head, cache_list) {
|
||||||
hlist_del_init_rcu(&ch->cache_list);
|
sunrpc_begin_cache_remove_entry(ch, detail);
|
||||||
detail->entries--;
|
|
||||||
|
|
||||||
set_bit(CACHE_CLEANED, &ch->flags);
|
|
||||||
spin_unlock(&detail->hash_lock);
|
spin_unlock(&detail->hash_lock);
|
||||||
cache_fresh_unlocked(ch, detail);
|
sunrpc_end_cache_remove_entry(ch, detail);
|
||||||
cache_put(ch, detail);
|
|
||||||
spin_lock(&detail->hash_lock);
|
spin_lock(&detail->hash_lock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1885,10 +1890,9 @@ void sunrpc_cache_unhash(struct cache_detail *cd, struct cache_head *h)
|
||||||
{
|
{
|
||||||
spin_lock(&cd->hash_lock);
|
spin_lock(&cd->hash_lock);
|
||||||
if (!hlist_unhashed(&h->cache_list)){
|
if (!hlist_unhashed(&h->cache_list)){
|
||||||
hlist_del_init_rcu(&h->cache_list);
|
sunrpc_begin_cache_remove_entry(h, cd);
|
||||||
cd->entries--;
|
|
||||||
spin_unlock(&cd->hash_lock);
|
spin_unlock(&cd->hash_lock);
|
||||||
cache_put(h, cd);
|
sunrpc_end_cache_remove_entry(h, cd);
|
||||||
} else
|
} else
|
||||||
spin_unlock(&cd->hash_lock);
|
spin_unlock(&cd->hash_lock);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue