xfs: remove subdirectories
Use the move from Linux 2.6 to Linux 3.x as an excuse to kill the annoying subdirectories in the XFS source code. Besides the large amount of file rename the only changes are to the Makefile, a few files including headers with the subdirectory prefix, and the binary sysctl compat code that includes a header under fs/xfs/ from kernel/. Signed-off-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Alex Elder <aelder@sgi.com>
This commit is contained in:
parent
06f8e2d675
commit
c59d87c460
55 changed files with 58 additions and 63 deletions
117
fs/xfs/Makefile
117
fs/xfs/Makefile
|
|
@ -16,44 +16,51 @@
|
|||
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
|
||||
ccflags-y := -I$(src) -I$(src)/linux-2.6 -I$(src)/quota -I$(src)/support
|
||||
ccflags-$(CONFIG_XFS_DEBUG) += -g
|
||||
|
||||
XFS_LINUX := linux-2.6
|
||||
|
||||
obj-$(CONFIG_XFS_FS) += xfs.o
|
||||
|
||||
xfs-y += linux-2.6/xfs_trace.o
|
||||
|
||||
xfs-$(CONFIG_XFS_QUOTA) += $(addprefix quota/, \
|
||||
xfs_dquot.o \
|
||||
xfs_dquot_item.o \
|
||||
xfs_trans_dquot.o \
|
||||
xfs_qm_syscalls.o \
|
||||
xfs_qm_bhv.o \
|
||||
xfs_qm.o)
|
||||
xfs-$(CONFIG_XFS_QUOTA) += linux-2.6/xfs_quotaops.o
|
||||
|
||||
ifeq ($(CONFIG_XFS_QUOTA),y)
|
||||
xfs-$(CONFIG_PROC_FS) += quota/xfs_qm_stats.o
|
||||
endif
|
||||
|
||||
xfs-$(CONFIG_XFS_RT) += xfs_rtalloc.o
|
||||
xfs-$(CONFIG_XFS_POSIX_ACL) += $(XFS_LINUX)/xfs_acl.o
|
||||
xfs-$(CONFIG_PROC_FS) += $(XFS_LINUX)/xfs_stats.o
|
||||
xfs-$(CONFIG_SYSCTL) += $(XFS_LINUX)/xfs_sysctl.o
|
||||
xfs-$(CONFIG_COMPAT) += $(XFS_LINUX)/xfs_ioctl32.o
|
||||
# this one should be compiled first, as the tracing macros can easily blow up
|
||||
xfs-y += xfs_trace.o
|
||||
|
||||
# highlevel code
|
||||
xfs-y += xfs_aops.o \
|
||||
xfs_bit.o \
|
||||
xfs_buf.o \
|
||||
xfs_dfrag.o \
|
||||
xfs_discard.o \
|
||||
xfs_error.o \
|
||||
xfs_export.o \
|
||||
xfs_file.o \
|
||||
xfs_filestream.o \
|
||||
xfs_fsops.o \
|
||||
xfs_fs_subr.o \
|
||||
xfs_globals.o \
|
||||
xfs_iget.o \
|
||||
xfs_ioctl.o \
|
||||
xfs_iomap.o \
|
||||
xfs_iops.o \
|
||||
xfs_itable.o \
|
||||
xfs_message.o \
|
||||
xfs_mru_cache.o \
|
||||
xfs_super.o \
|
||||
xfs_sync.o \
|
||||
xfs_xattr.o \
|
||||
xfs_rename.o \
|
||||
xfs_rw.o \
|
||||
xfs_utils.o \
|
||||
xfs_vnodeops.o \
|
||||
kmem.o \
|
||||
uuid.o
|
||||
|
||||
# code shared with libxfs
|
||||
xfs-y += xfs_alloc.o \
|
||||
xfs_alloc_btree.o \
|
||||
xfs_attr.o \
|
||||
xfs_attr_leaf.o \
|
||||
xfs_bit.o \
|
||||
xfs_bmap.o \
|
||||
xfs_bmap_btree.o \
|
||||
xfs_btree.o \
|
||||
xfs_buf_item.o \
|
||||
xfs_da_btree.o \
|
||||
xfs_dir2.o \
|
||||
xfs_dir2_block.o \
|
||||
|
|
@ -61,49 +68,37 @@ xfs-y += xfs_alloc.o \
|
|||
xfs_dir2_leaf.o \
|
||||
xfs_dir2_node.o \
|
||||
xfs_dir2_sf.o \
|
||||
xfs_error.o \
|
||||
xfs_extfree_item.o \
|
||||
xfs_filestream.o \
|
||||
xfs_fsops.o \
|
||||
xfs_ialloc.o \
|
||||
xfs_ialloc_btree.o \
|
||||
xfs_iget.o \
|
||||
xfs_inode.o \
|
||||
xfs_inode_item.o \
|
||||
xfs_iomap.o \
|
||||
xfs_itable.o \
|
||||
xfs_dfrag.o \
|
||||
xfs_log.o \
|
||||
xfs_log_cil.o \
|
||||
xfs_log_recover.o \
|
||||
xfs_mount.o \
|
||||
xfs_mru_cache.o \
|
||||
xfs_rename.o \
|
||||
xfs_trans.o \
|
||||
xfs_trans.o
|
||||
|
||||
# low-level transaction/log code
|
||||
xfs-y += xfs_log.o \
|
||||
xfs_log_cil.o \
|
||||
xfs_buf_item.o \
|
||||
xfs_extfree_item.o \
|
||||
xfs_inode_item.o \
|
||||
xfs_trans_ail.o \
|
||||
xfs_trans_buf.o \
|
||||
xfs_trans_extfree.o \
|
||||
xfs_trans_inode.o \
|
||||
xfs_utils.o \
|
||||
xfs_vnodeops.o \
|
||||
xfs_rw.o
|
||||
|
||||
# Objects in linux/
|
||||
xfs-y += $(addprefix $(XFS_LINUX)/, \
|
||||
kmem.o \
|
||||
xfs_aops.o \
|
||||
xfs_buf.o \
|
||||
xfs_discard.o \
|
||||
xfs_export.o \
|
||||
xfs_file.o \
|
||||
xfs_fs_subr.o \
|
||||
xfs_globals.o \
|
||||
xfs_ioctl.o \
|
||||
xfs_iops.o \
|
||||
xfs_message.o \
|
||||
xfs_super.o \
|
||||
xfs_sync.o \
|
||||
xfs_xattr.o)
|
||||
|
||||
# Objects in support/
|
||||
xfs-y += support/uuid.o
|
||||
# optional features
|
||||
xfs-$(CONFIG_XFS_QUOTA) += xfs_dquot.o \
|
||||
xfs_dquot_item.o \
|
||||
xfs_trans_dquot.o \
|
||||
xfs_qm_syscalls.o \
|
||||
xfs_qm_bhv.o \
|
||||
xfs_qm.o \
|
||||
xfs_quotaops.o
|
||||
ifeq ($(CONFIG_XFS_QUOTA),y)
|
||||
xfs-$(CONFIG_PROC_FS) += xfs_qm_stats.o
|
||||
endif
|
||||
xfs-$(CONFIG_XFS_RT) += xfs_rtalloc.o
|
||||
xfs-$(CONFIG_XFS_POSIX_ACL) += xfs_acl.o
|
||||
xfs-$(CONFIG_PROC_FS) += xfs_stats.o
|
||||
xfs-$(CONFIG_SYSCTL) += xfs_sysctl.o
|
||||
xfs-$(CONFIG_COMPAT) += xfs_ioctl32.o
|
||||
|
|
|
|||
132
fs/xfs/kmem.c
Normal file
132
fs/xfs/kmem.c
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* Copyright (c) 2000-2005 Silicon Graphics, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it would be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include <linux/mm.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/swap.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/backing-dev.h>
|
||||
#include "time.h"
|
||||
#include "kmem.h"
|
||||
#include "xfs_message.h"
|
||||
|
||||
/*
|
||||
* Greedy allocation. May fail and may return vmalloced memory.
|
||||
*
|
||||
* Must be freed using kmem_free_large.
|
||||
*/
|
||||
void *
|
||||
kmem_zalloc_greedy(size_t *size, size_t minsize, size_t maxsize)
|
||||
{
|
||||
void *ptr;
|
||||
size_t kmsize = maxsize;
|
||||
|
||||
while (!(ptr = kmem_zalloc_large(kmsize))) {
|
||||
if ((kmsize >>= 1) <= minsize)
|
||||
kmsize = minsize;
|
||||
}
|
||||
if (ptr)
|
||||
*size = kmsize;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void *
|
||||
kmem_alloc(size_t size, unsigned int __nocast flags)
|
||||
{
|
||||
int retries = 0;
|
||||
gfp_t lflags = kmem_flags_convert(flags);
|
||||
void *ptr;
|
||||
|
||||
do {
|
||||
ptr = kmalloc(size, lflags);
|
||||
if (ptr || (flags & (KM_MAYFAIL|KM_NOSLEEP)))
|
||||
return ptr;
|
||||
if (!(++retries % 100))
|
||||
xfs_err(NULL,
|
||||
"possible memory allocation deadlock in %s (mode:0x%x)",
|
||||
__func__, lflags);
|
||||
congestion_wait(BLK_RW_ASYNC, HZ/50);
|
||||
} while (1);
|
||||
}
|
||||
|
||||
void *
|
||||
kmem_zalloc(size_t size, unsigned int __nocast flags)
|
||||
{
|
||||
void *ptr;
|
||||
|
||||
ptr = kmem_alloc(size, flags);
|
||||
if (ptr)
|
||||
memset((char *)ptr, 0, (int)size);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void
|
||||
kmem_free(const void *ptr)
|
||||
{
|
||||
if (!is_vmalloc_addr(ptr)) {
|
||||
kfree(ptr);
|
||||
} else {
|
||||
vfree(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
void *
|
||||
kmem_realloc(const void *ptr, size_t newsize, size_t oldsize,
|
||||
unsigned int __nocast flags)
|
||||
{
|
||||
void *new;
|
||||
|
||||
new = kmem_alloc(newsize, flags);
|
||||
if (ptr) {
|
||||
if (new)
|
||||
memcpy(new, ptr,
|
||||
((oldsize < newsize) ? oldsize : newsize));
|
||||
kmem_free(ptr);
|
||||
}
|
||||
return new;
|
||||
}
|
||||
|
||||
void *
|
||||
kmem_zone_alloc(kmem_zone_t *zone, unsigned int __nocast flags)
|
||||
{
|
||||
int retries = 0;
|
||||
gfp_t lflags = kmem_flags_convert(flags);
|
||||
void *ptr;
|
||||
|
||||
do {
|
||||
ptr = kmem_cache_alloc(zone, lflags);
|
||||
if (ptr || (flags & (KM_MAYFAIL|KM_NOSLEEP)))
|
||||
return ptr;
|
||||
if (!(++retries % 100))
|
||||
xfs_err(NULL,
|
||||
"possible memory allocation deadlock in %s (mode:0x%x)",
|
||||
__func__, lflags);
|
||||
congestion_wait(BLK_RW_ASYNC, HZ/50);
|
||||
} while (1);
|
||||
}
|
||||
|
||||
void *
|
||||
kmem_zone_zalloc(kmem_zone_t *zone, unsigned int __nocast flags)
|
||||
{
|
||||
void *ptr;
|
||||
|
||||
ptr = kmem_zone_alloc(zone, flags);
|
||||
if (ptr)
|
||||
memset((char *)ptr, 0, kmem_cache_size(zone));
|
||||
return ptr;
|
||||
}
|
||||
124
fs/xfs/kmem.h
Normal file
124
fs/xfs/kmem.h
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* Copyright (c) 2000-2005 Silicon Graphics, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it would be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef __XFS_SUPPORT_KMEM_H__
|
||||
#define __XFS_SUPPORT_KMEM_H__
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
/*
|
||||
* General memory allocation interfaces
|
||||
*/
|
||||
|
||||
#define KM_SLEEP 0x0001u
|
||||
#define KM_NOSLEEP 0x0002u
|
||||
#define KM_NOFS 0x0004u
|
||||
#define KM_MAYFAIL 0x0008u
|
||||
|
||||
/*
|
||||
* We use a special process flag to avoid recursive callbacks into
|
||||
* the filesystem during transactions. We will also issue our own
|
||||
* warnings, so we explicitly skip any generic ones (silly of us).
|
||||
*/
|
||||
static inline gfp_t
|
||||
kmem_flags_convert(unsigned int __nocast flags)
|
||||
{
|
||||
gfp_t lflags;
|
||||
|
||||
BUG_ON(flags & ~(KM_SLEEP|KM_NOSLEEP|KM_NOFS|KM_MAYFAIL));
|
||||
|
||||
if (flags & KM_NOSLEEP) {
|
||||
lflags = GFP_ATOMIC | __GFP_NOWARN;
|
||||
} else {
|
||||
lflags = GFP_KERNEL | __GFP_NOWARN;
|
||||
if ((current->flags & PF_FSTRANS) || (flags & KM_NOFS))
|
||||
lflags &= ~__GFP_FS;
|
||||
}
|
||||
return lflags;
|
||||
}
|
||||
|
||||
extern void *kmem_alloc(size_t, unsigned int __nocast);
|
||||
extern void *kmem_zalloc(size_t, unsigned int __nocast);
|
||||
extern void *kmem_realloc(const void *, size_t, size_t, unsigned int __nocast);
|
||||
extern void kmem_free(const void *);
|
||||
|
||||
static inline void *kmem_zalloc_large(size_t size)
|
||||
{
|
||||
void *ptr;
|
||||
|
||||
ptr = vmalloc(size);
|
||||
if (ptr)
|
||||
memset(ptr, 0, size);
|
||||
return ptr;
|
||||
}
|
||||
static inline void kmem_free_large(void *ptr)
|
||||
{
|
||||
vfree(ptr);
|
||||
}
|
||||
|
||||
extern void *kmem_zalloc_greedy(size_t *, size_t, size_t);
|
||||
|
||||
/*
|
||||
* Zone interfaces
|
||||
*/
|
||||
|
||||
#define KM_ZONE_HWALIGN SLAB_HWCACHE_ALIGN
|
||||
#define KM_ZONE_RECLAIM SLAB_RECLAIM_ACCOUNT
|
||||
#define KM_ZONE_SPREAD SLAB_MEM_SPREAD
|
||||
|
||||
#define kmem_zone kmem_cache
|
||||
#define kmem_zone_t struct kmem_cache
|
||||
|
||||
static inline kmem_zone_t *
|
||||
kmem_zone_init(int size, char *zone_name)
|
||||
{
|
||||
return kmem_cache_create(zone_name, size, 0, 0, NULL);
|
||||
}
|
||||
|
||||
static inline kmem_zone_t *
|
||||
kmem_zone_init_flags(int size, char *zone_name, unsigned long flags,
|
||||
void (*construct)(void *))
|
||||
{
|
||||
return kmem_cache_create(zone_name, size, 0, flags, construct);
|
||||
}
|
||||
|
||||
static inline void
|
||||
kmem_zone_free(kmem_zone_t *zone, void *ptr)
|
||||
{
|
||||
kmem_cache_free(zone, ptr);
|
||||
}
|
||||
|
||||
static inline void
|
||||
kmem_zone_destroy(kmem_zone_t *zone)
|
||||
{
|
||||
if (zone)
|
||||
kmem_cache_destroy(zone);
|
||||
}
|
||||
|
||||
extern void *kmem_zone_alloc(kmem_zone_t *, unsigned int __nocast);
|
||||
extern void *kmem_zone_zalloc(kmem_zone_t *, unsigned int __nocast);
|
||||
|
||||
static inline int
|
||||
kmem_shake_allow(gfp_t gfp_mask)
|
||||
{
|
||||
return ((gfp_mask & __GFP_WAIT) && (gfp_mask & __GFP_FS));
|
||||
}
|
||||
|
||||
#endif /* __XFS_SUPPORT_KMEM_H__ */
|
||||
90
fs/xfs/mrlock.h
Normal file
90
fs/xfs/mrlock.h
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright (c) 2000-2006 Silicon Graphics, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it would be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef __XFS_SUPPORT_MRLOCK_H__
|
||||
#define __XFS_SUPPORT_MRLOCK_H__
|
||||
|
||||
#include <linux/rwsem.h>
|
||||
|
||||
typedef struct {
|
||||
struct rw_semaphore mr_lock;
|
||||
#ifdef DEBUG
|
||||
int mr_writer;
|
||||
#endif
|
||||
} mrlock_t;
|
||||
|
||||
#ifdef DEBUG
|
||||
#define mrinit(mrp, name) \
|
||||
do { (mrp)->mr_writer = 0; init_rwsem(&(mrp)->mr_lock); } while (0)
|
||||
#else
|
||||
#define mrinit(mrp, name) \
|
||||
do { init_rwsem(&(mrp)->mr_lock); } while (0)
|
||||
#endif
|
||||
|
||||
#define mrlock_init(mrp, t,n,s) mrinit(mrp, n)
|
||||
#define mrfree(mrp) do { } while (0)
|
||||
|
||||
static inline void mraccess_nested(mrlock_t *mrp, int subclass)
|
||||
{
|
||||
down_read_nested(&mrp->mr_lock, subclass);
|
||||
}
|
||||
|
||||
static inline void mrupdate_nested(mrlock_t *mrp, int subclass)
|
||||
{
|
||||
down_write_nested(&mrp->mr_lock, subclass);
|
||||
#ifdef DEBUG
|
||||
mrp->mr_writer = 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int mrtryaccess(mrlock_t *mrp)
|
||||
{
|
||||
return down_read_trylock(&mrp->mr_lock);
|
||||
}
|
||||
|
||||
static inline int mrtryupdate(mrlock_t *mrp)
|
||||
{
|
||||
if (!down_write_trylock(&mrp->mr_lock))
|
||||
return 0;
|
||||
#ifdef DEBUG
|
||||
mrp->mr_writer = 1;
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline void mrunlock_excl(mrlock_t *mrp)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
mrp->mr_writer = 0;
|
||||
#endif
|
||||
up_write(&mrp->mr_lock);
|
||||
}
|
||||
|
||||
static inline void mrunlock_shared(mrlock_t *mrp)
|
||||
{
|
||||
up_read(&mrp->mr_lock);
|
||||
}
|
||||
|
||||
static inline void mrdemote(mrlock_t *mrp)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
mrp->mr_writer = 0;
|
||||
#endif
|
||||
downgrade_write(&mrp->mr_lock);
|
||||
}
|
||||
|
||||
#endif /* __XFS_SUPPORT_MRLOCK_H__ */
|
||||
36
fs/xfs/time.h
Normal file
36
fs/xfs/time.h
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (c) 2000-2003,2005 Silicon Graphics, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it would be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef __XFS_SUPPORT_TIME_H__
|
||||
#define __XFS_SUPPORT_TIME_H__
|
||||
|
||||
#include <linux/sched.h>
|
||||
#include <linux/time.h>
|
||||
|
||||
typedef struct timespec timespec_t;
|
||||
|
||||
static inline void delay(long ticks)
|
||||
{
|
||||
schedule_timeout_uninterruptible(ticks);
|
||||
}
|
||||
|
||||
static inline void nanotime(struct timespec *tvp)
|
||||
{
|
||||
*tvp = CURRENT_TIME;
|
||||
}
|
||||
|
||||
#endif /* __XFS_SUPPORT_TIME_H__ */
|
||||
63
fs/xfs/uuid.c
Normal file
63
fs/xfs/uuid.c
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright (c) 2000-2003,2005 Silicon Graphics, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it would be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include <xfs.h>
|
||||
|
||||
/* IRIX interpretation of an uuid_t */
|
||||
typedef struct {
|
||||
__be32 uu_timelow;
|
||||
__be16 uu_timemid;
|
||||
__be16 uu_timehi;
|
||||
__be16 uu_clockseq;
|
||||
__be16 uu_node[3];
|
||||
} xfs_uu_t;
|
||||
|
||||
/*
|
||||
* uuid_getnodeuniq - obtain the node unique fields of a UUID.
|
||||
*
|
||||
* This is not in any way a standard or condoned UUID function;
|
||||
* it just something that's needed for user-level file handles.
|
||||
*/
|
||||
void
|
||||
uuid_getnodeuniq(uuid_t *uuid, int fsid [2])
|
||||
{
|
||||
xfs_uu_t *uup = (xfs_uu_t *)uuid;
|
||||
|
||||
fsid[0] = (be16_to_cpu(uup->uu_clockseq) << 16) |
|
||||
be16_to_cpu(uup->uu_timemid);
|
||||
fsid[1] = be32_to_cpu(uup->uu_timelow);
|
||||
}
|
||||
|
||||
int
|
||||
uuid_is_nil(uuid_t *uuid)
|
||||
{
|
||||
int i;
|
||||
char *cp = (char *)uuid;
|
||||
|
||||
if (uuid == NULL)
|
||||
return 0;
|
||||
/* implied check of version number here... */
|
||||
for (i = 0; i < sizeof *uuid; i++)
|
||||
if (*cp++) return 0; /* not nil */
|
||||
return 1; /* is nil */
|
||||
}
|
||||
|
||||
int
|
||||
uuid_equal(uuid_t *uuid1, uuid_t *uuid2)
|
||||
{
|
||||
return memcmp(uuid1, uuid2, sizeof(uuid_t)) ? 0 : 1;
|
||||
}
|
||||
29
fs/xfs/uuid.h
Normal file
29
fs/xfs/uuid.h
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (c) 2000-2003,2005 Silicon Graphics, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it would be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef __XFS_SUPPORT_UUID_H__
|
||||
#define __XFS_SUPPORT_UUID_H__
|
||||
|
||||
typedef struct {
|
||||
unsigned char __u_bits[16];
|
||||
} uuid_t;
|
||||
|
||||
extern int uuid_is_nil(uuid_t *uuid);
|
||||
extern int uuid_equal(uuid_t *uuid1, uuid_t *uuid2);
|
||||
extern void uuid_getnodeuniq(uuid_t *uuid, int fsid [2]);
|
||||
|
||||
#endif /* __XFS_SUPPORT_UUID_H__ */
|
||||
420
fs/xfs/xfs_acl.c
Normal file
420
fs/xfs/xfs_acl.c
Normal file
|
|
@ -0,0 +1,420 @@
|
|||
/*
|
||||
* Copyright (c) 2008, Christoph Hellwig
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it would be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include "xfs.h"
|
||||
#include "xfs_acl.h"
|
||||
#include "xfs_attr.h"
|
||||
#include "xfs_bmap_btree.h"
|
||||
#include "xfs_inode.h"
|
||||
#include "xfs_vnodeops.h"
|
||||
#include "xfs_trace.h"
|
||||
#include <linux/slab.h>
|
||||
#include <linux/xattr.h>
|
||||
#include <linux/posix_acl_xattr.h>
|
||||
|
||||
|
||||
/*
|
||||
* Locking scheme:
|
||||
* - all ACL updates are protected by inode->i_mutex, which is taken before
|
||||
* calling into this file.
|
||||
*/
|
||||
|
||||
STATIC struct posix_acl *
|
||||
xfs_acl_from_disk(struct xfs_acl *aclp)
|
||||
{
|
||||
struct posix_acl_entry *acl_e;
|
||||
struct posix_acl *acl;
|
||||
struct xfs_acl_entry *ace;
|
||||
int count, i;
|
||||
|
||||
count = be32_to_cpu(aclp->acl_cnt);
|
||||
|
||||
acl = posix_acl_alloc(count, GFP_KERNEL);
|
||||
if (!acl)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
acl_e = &acl->a_entries[i];
|
||||
ace = &aclp->acl_entry[i];
|
||||
|
||||
/*
|
||||
* The tag is 32 bits on disk and 16 bits in core.
|
||||
*
|
||||
* Because every access to it goes through the core
|
||||
* format first this is not a problem.
|
||||
*/
|
||||
acl_e->e_tag = be32_to_cpu(ace->ae_tag);
|
||||
acl_e->e_perm = be16_to_cpu(ace->ae_perm);
|
||||
|
||||
switch (acl_e->e_tag) {
|
||||
case ACL_USER:
|
||||
case ACL_GROUP:
|
||||
acl_e->e_id = be32_to_cpu(ace->ae_id);
|
||||
break;
|
||||
case ACL_USER_OBJ:
|
||||
case ACL_GROUP_OBJ:
|
||||
case ACL_MASK:
|
||||
case ACL_OTHER:
|
||||
acl_e->e_id = ACL_UNDEFINED_ID;
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
return acl;
|
||||
|
||||
fail:
|
||||
posix_acl_release(acl);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
STATIC void
|
||||
xfs_acl_to_disk(struct xfs_acl *aclp, const struct posix_acl *acl)
|
||||
{
|
||||
const struct posix_acl_entry *acl_e;
|
||||
struct xfs_acl_entry *ace;
|
||||
int i;
|
||||
|
||||
aclp->acl_cnt = cpu_to_be32(acl->a_count);
|
||||
for (i = 0; i < acl->a_count; i++) {
|
||||
ace = &aclp->acl_entry[i];
|
||||
acl_e = &acl->a_entries[i];
|
||||
|
||||
ace->ae_tag = cpu_to_be32(acl_e->e_tag);
|
||||
ace->ae_id = cpu_to_be32(acl_e->e_id);
|
||||
ace->ae_perm = cpu_to_be16(acl_e->e_perm);
|
||||
}
|
||||
}
|
||||
|
||||
struct posix_acl *
|
||||
xfs_get_acl(struct inode *inode, int type)
|
||||
{
|
||||
struct xfs_inode *ip = XFS_I(inode);
|
||||
struct posix_acl *acl;
|
||||
struct xfs_acl *xfs_acl;
|
||||
int len = sizeof(struct xfs_acl);
|
||||
unsigned char *ea_name;
|
||||
int error;
|
||||
|
||||
acl = get_cached_acl(inode, type);
|
||||
if (acl != ACL_NOT_CACHED)
|
||||
return acl;
|
||||
|
||||
trace_xfs_get_acl(ip);
|
||||
|
||||
switch (type) {
|
||||
case ACL_TYPE_ACCESS:
|
||||
ea_name = SGI_ACL_FILE;
|
||||
break;
|
||||
case ACL_TYPE_DEFAULT:
|
||||
ea_name = SGI_ACL_DEFAULT;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have a cached ACLs value just return it, not need to
|
||||
* go out to the disk.
|
||||
*/
|
||||
|
||||
xfs_acl = kzalloc(sizeof(struct xfs_acl), GFP_KERNEL);
|
||||
if (!xfs_acl)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
error = -xfs_attr_get(ip, ea_name, (unsigned char *)xfs_acl,
|
||||
&len, ATTR_ROOT);
|
||||
if (error) {
|
||||
/*
|
||||
* If the attribute doesn't exist make sure we have a negative
|
||||
* cache entry, for any other error assume it is transient and
|
||||
* leave the cache entry as ACL_NOT_CACHED.
|
||||
*/
|
||||
if (error == -ENOATTR) {
|
||||
acl = NULL;
|
||||
goto out_update_cache;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
acl = xfs_acl_from_disk(xfs_acl);
|
||||
if (IS_ERR(acl))
|
||||
goto out;
|
||||
|
||||
out_update_cache:
|
||||
set_cached_acl(inode, type, acl);
|
||||
out:
|
||||
kfree(xfs_acl);
|
||||
return acl;
|
||||
}
|
||||
|
||||
STATIC int
|
||||
xfs_set_acl(struct inode *inode, int type, struct posix_acl *acl)
|
||||
{
|
||||
struct xfs_inode *ip = XFS_I(inode);
|
||||
unsigned char *ea_name;
|
||||
int error;
|
||||
|
||||
if (S_ISLNK(inode->i_mode))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
switch (type) {
|
||||
case ACL_TYPE_ACCESS:
|
||||
ea_name = SGI_ACL_FILE;
|
||||
break;
|
||||
case ACL_TYPE_DEFAULT:
|
||||
if (!S_ISDIR(inode->i_mode))
|
||||
return acl ? -EACCES : 0;
|
||||
ea_name = SGI_ACL_DEFAULT;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (acl) {
|
||||
struct xfs_acl *xfs_acl;
|
||||
int len;
|
||||
|
||||
xfs_acl = kzalloc(sizeof(struct xfs_acl), GFP_KERNEL);
|
||||
if (!xfs_acl)
|
||||
return -ENOMEM;
|
||||
|
||||
xfs_acl_to_disk(xfs_acl, acl);
|
||||
len = sizeof(struct xfs_acl) -
|
||||
(sizeof(struct xfs_acl_entry) *
|
||||
(XFS_ACL_MAX_ENTRIES - acl->a_count));
|
||||
|
||||
error = -xfs_attr_set(ip, ea_name, (unsigned char *)xfs_acl,
|
||||
len, ATTR_ROOT);
|
||||
|
||||
kfree(xfs_acl);
|
||||
} else {
|
||||
/*
|
||||
* A NULL ACL argument means we want to remove the ACL.
|
||||
*/
|
||||
error = -xfs_attr_remove(ip, ea_name, ATTR_ROOT);
|
||||
|
||||
/*
|
||||
* If the attribute didn't exist to start with that's fine.
|
||||
*/
|
||||
if (error == -ENOATTR)
|
||||
error = 0;
|
||||
}
|
||||
|
||||
if (!error)
|
||||
set_cached_acl(inode, type, acl);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int
|
||||
xfs_set_mode(struct inode *inode, umode_t mode)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
if (mode != inode->i_mode) {
|
||||
struct iattr iattr;
|
||||
|
||||
iattr.ia_valid = ATTR_MODE | ATTR_CTIME;
|
||||
iattr.ia_mode = mode;
|
||||
iattr.ia_ctime = current_fs_time(inode->i_sb);
|
||||
|
||||
error = -xfs_setattr_nonsize(XFS_I(inode), &iattr, XFS_ATTR_NOACL);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int
|
||||
xfs_acl_exists(struct inode *inode, unsigned char *name)
|
||||
{
|
||||
int len = sizeof(struct xfs_acl);
|
||||
|
||||
return (xfs_attr_get(XFS_I(inode), name, NULL, &len,
|
||||
ATTR_ROOT|ATTR_KERNOVAL) == 0);
|
||||
}
|
||||
|
||||
int
|
||||
posix_acl_access_exists(struct inode *inode)
|
||||
{
|
||||
return xfs_acl_exists(inode, SGI_ACL_FILE);
|
||||
}
|
||||
|
||||
int
|
||||
posix_acl_default_exists(struct inode *inode)
|
||||
{
|
||||
if (!S_ISDIR(inode->i_mode))
|
||||
return 0;
|
||||
return xfs_acl_exists(inode, SGI_ACL_DEFAULT);
|
||||
}
|
||||
|
||||
/*
|
||||
* No need for i_mutex because the inode is not yet exposed to the VFS.
|
||||
*/
|
||||
int
|
||||
xfs_inherit_acl(struct inode *inode, struct posix_acl *acl)
|
||||
{
|
||||
umode_t mode = inode->i_mode;
|
||||
int error = 0, inherit = 0;
|
||||
|
||||
if (S_ISDIR(inode->i_mode)) {
|
||||
error = xfs_set_acl(inode, ACL_TYPE_DEFAULT, acl);
|
||||
if (error)
|
||||
goto out;
|
||||
}
|
||||
|
||||
error = posix_acl_create(&acl, GFP_KERNEL, &mode);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
/*
|
||||
* If posix_acl_create returns a positive value we need to
|
||||
* inherit a permission that can't be represented using the Unix
|
||||
* mode bits and we actually need to set an ACL.
|
||||
*/
|
||||
if (error > 0)
|
||||
inherit = 1;
|
||||
|
||||
error = xfs_set_mode(inode, mode);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
if (inherit)
|
||||
error = xfs_set_acl(inode, ACL_TYPE_ACCESS, acl);
|
||||
|
||||
out:
|
||||
posix_acl_release(acl);
|
||||
return error;
|
||||
}
|
||||
|
||||
int
|
||||
xfs_acl_chmod(struct inode *inode)
|
||||
{
|
||||
struct posix_acl *acl;
|
||||
int error;
|
||||
|
||||
if (S_ISLNK(inode->i_mode))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
acl = xfs_get_acl(inode, ACL_TYPE_ACCESS);
|
||||
if (IS_ERR(acl) || !acl)
|
||||
return PTR_ERR(acl);
|
||||
|
||||
error = posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = xfs_set_acl(inode, ACL_TYPE_ACCESS, acl);
|
||||
posix_acl_release(acl);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int
|
||||
xfs_xattr_acl_get(struct dentry *dentry, const char *name,
|
||||
void *value, size_t size, int type)
|
||||
{
|
||||
struct posix_acl *acl;
|
||||
int error;
|
||||
|
||||
acl = xfs_get_acl(dentry->d_inode, type);
|
||||
if (IS_ERR(acl))
|
||||
return PTR_ERR(acl);
|
||||
if (acl == NULL)
|
||||
return -ENODATA;
|
||||
|
||||
error = posix_acl_to_xattr(acl, value, size);
|
||||
posix_acl_release(acl);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int
|
||||
xfs_xattr_acl_set(struct dentry *dentry, const char *name,
|
||||
const void *value, size_t size, int flags, int type)
|
||||
{
|
||||
struct inode *inode = dentry->d_inode;
|
||||
struct posix_acl *acl = NULL;
|
||||
int error = 0;
|
||||
|
||||
if (flags & XATTR_CREATE)
|
||||
return -EINVAL;
|
||||
if (type == ACL_TYPE_DEFAULT && !S_ISDIR(inode->i_mode))
|
||||
return value ? -EACCES : 0;
|
||||
if ((current_fsuid() != inode->i_uid) && !capable(CAP_FOWNER))
|
||||
return -EPERM;
|
||||
|
||||
if (!value)
|
||||
goto set_acl;
|
||||
|
||||
acl = posix_acl_from_xattr(value, size);
|
||||
if (!acl) {
|
||||
/*
|
||||
* acl_set_file(3) may request that we set default ACLs with
|
||||
* zero length -- defend (gracefully) against that here.
|
||||
*/
|
||||
goto out;
|
||||
}
|
||||
if (IS_ERR(acl)) {
|
||||
error = PTR_ERR(acl);
|
||||
goto out;
|
||||
}
|
||||
|
||||
error = posix_acl_valid(acl);
|
||||
if (error)
|
||||
goto out_release;
|
||||
|
||||
error = -EINVAL;
|
||||
if (acl->a_count > XFS_ACL_MAX_ENTRIES)
|
||||
goto out_release;
|
||||
|
||||
if (type == ACL_TYPE_ACCESS) {
|
||||
umode_t mode = inode->i_mode;
|
||||
error = posix_acl_equiv_mode(acl, &mode);
|
||||
|
||||
if (error <= 0) {
|
||||
posix_acl_release(acl);
|
||||
acl = NULL;
|
||||
|
||||
if (error < 0)
|
||||
return error;
|
||||
}
|
||||
|
||||
error = xfs_set_mode(inode, mode);
|
||||
if (error)
|
||||
goto out_release;
|
||||
}
|
||||
|
||||
set_acl:
|
||||
error = xfs_set_acl(inode, type, acl);
|
||||
out_release:
|
||||
posix_acl_release(acl);
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
||||
const struct xattr_handler xfs_xattr_acl_access_handler = {
|
||||
.prefix = POSIX_ACL_XATTR_ACCESS,
|
||||
.flags = ACL_TYPE_ACCESS,
|
||||
.get = xfs_xattr_acl_get,
|
||||
.set = xfs_xattr_acl_set,
|
||||
};
|
||||
|
||||
const struct xattr_handler xfs_xattr_acl_default_handler = {
|
||||
.prefix = POSIX_ACL_XATTR_DEFAULT,
|
||||
.flags = ACL_TYPE_DEFAULT,
|
||||
.get = xfs_xattr_acl_get,
|
||||
.set = xfs_xattr_acl_set,
|
||||
};
|
||||
1499
fs/xfs/xfs_aops.c
Normal file
1499
fs/xfs/xfs_aops.c
Normal file
File diff suppressed because it is too large
Load diff
68
fs/xfs/xfs_aops.h
Normal file
68
fs/xfs/xfs_aops.h
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright (c) 2005-2006 Silicon Graphics, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it would be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef __XFS_AOPS_H__
|
||||
#define __XFS_AOPS_H__
|
||||
|
||||
extern struct workqueue_struct *xfsdatad_workqueue;
|
||||
extern struct workqueue_struct *xfsconvertd_workqueue;
|
||||
extern mempool_t *xfs_ioend_pool;
|
||||
|
||||
/*
|
||||
* Types of I/O for bmap clustering and I/O completion tracking.
|
||||
*/
|
||||
enum {
|
||||
IO_DIRECT = 0, /* special case for direct I/O ioends */
|
||||
IO_DELALLOC, /* mapping covers delalloc region */
|
||||
IO_UNWRITTEN, /* mapping covers allocated but uninitialized data */
|
||||
IO_OVERWRITE, /* mapping covers already allocated extent */
|
||||
};
|
||||
|
||||
#define XFS_IO_TYPES \
|
||||
{ 0, "" }, \
|
||||
{ IO_DELALLOC, "delalloc" }, \
|
||||
{ IO_UNWRITTEN, "unwritten" }, \
|
||||
{ IO_OVERWRITE, "overwrite" }
|
||||
|
||||
/*
|
||||
* xfs_ioend struct manages large extent writes for XFS.
|
||||
* It can manage several multi-page bio's at once.
|
||||
*/
|
||||
typedef struct xfs_ioend {
|
||||
struct xfs_ioend *io_list; /* next ioend in chain */
|
||||
unsigned int io_type; /* delalloc / unwritten */
|
||||
int io_error; /* I/O error code */
|
||||
atomic_t io_remaining; /* hold count */
|
||||
struct inode *io_inode; /* file being written to */
|
||||
struct buffer_head *io_buffer_head;/* buffer linked list head */
|
||||
struct buffer_head *io_buffer_tail;/* buffer linked list tail */
|
||||
size_t io_size; /* size of the extent */
|
||||
xfs_off_t io_offset; /* offset in the file */
|
||||
struct work_struct io_work; /* xfsdatad work queue */
|
||||
struct kiocb *io_iocb;
|
||||
int io_result;
|
||||
} xfs_ioend_t;
|
||||
|
||||
extern const struct address_space_operations xfs_address_space_operations;
|
||||
extern int xfs_get_blocks(struct inode *, sector_t, struct buffer_head *, int);
|
||||
|
||||
extern void xfs_ioend_init(void);
|
||||
extern void xfs_ioend_wait(struct xfs_inode *);
|
||||
|
||||
extern void xfs_count_page_state(struct page *, int *, int *);
|
||||
|
||||
#endif /* __XFS_AOPS_H__ */
|
||||
1876
fs/xfs/xfs_buf.c
Normal file
1876
fs/xfs/xfs_buf.c
Normal file
File diff suppressed because it is too large
Load diff
326
fs/xfs/xfs_buf.h
Normal file
326
fs/xfs/xfs_buf.h
Normal file
|
|
@ -0,0 +1,326 @@
|
|||
/*
|
||||
* Copyright (c) 2000-2005 Silicon Graphics, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it would be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef __XFS_BUF_H__
|
||||
#define __XFS_BUF_H__
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <asm/system.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/uio.h>
|
||||
|
||||
/*
|
||||
* Base types
|
||||
*/
|
||||
|
||||
#define XFS_BUF_DADDR_NULL ((xfs_daddr_t) (-1LL))
|
||||
|
||||
#define xfs_buf_ctob(pp) ((pp) * PAGE_CACHE_SIZE)
|
||||
#define xfs_buf_btoc(dd) (((dd) + PAGE_CACHE_SIZE-1) >> PAGE_CACHE_SHIFT)
|
||||
#define xfs_buf_btoct(dd) ((dd) >> PAGE_CACHE_SHIFT)
|
||||
#define xfs_buf_poff(aa) ((aa) & ~PAGE_CACHE_MASK)
|
||||
|
||||
typedef enum {
|
||||
XBRW_READ = 1, /* transfer into target memory */
|
||||
XBRW_WRITE = 2, /* transfer from target memory */
|
||||
XBRW_ZERO = 3, /* Zero target memory */
|
||||
} xfs_buf_rw_t;
|
||||
|
||||
#define XBF_READ (1 << 0) /* buffer intended for reading from device */
|
||||
#define XBF_WRITE (1 << 1) /* buffer intended for writing to device */
|
||||
#define XBF_READ_AHEAD (1 << 2) /* asynchronous read-ahead */
|
||||
#define XBF_MAPPED (1 << 3) /* buffer mapped (b_addr valid) */
|
||||
#define XBF_ASYNC (1 << 4) /* initiator will not wait for completion */
|
||||
#define XBF_DONE (1 << 5) /* all pages in the buffer uptodate */
|
||||
#define XBF_DELWRI (1 << 6) /* buffer has dirty pages */
|
||||
#define XBF_STALE (1 << 7) /* buffer has been staled, do not find it */
|
||||
|
||||
/* I/O hints for the BIO layer */
|
||||
#define XBF_SYNCIO (1 << 10)/* treat this buffer as synchronous I/O */
|
||||
#define XBF_FUA (1 << 11)/* force cache write through mode */
|
||||
#define XBF_FLUSH (1 << 12)/* flush the disk cache before a write */
|
||||
|
||||
/* flags used only as arguments to access routines */
|
||||
#define XBF_LOCK (1 << 15)/* lock requested */
|
||||
#define XBF_TRYLOCK (1 << 16)/* lock requested, but do not wait */
|
||||
#define XBF_DONT_BLOCK (1 << 17)/* do not block in current thread */
|
||||
|
||||
/* flags used only internally */
|
||||
#define _XBF_PAGES (1 << 20)/* backed by refcounted pages */
|
||||
#define _XBF_KMEM (1 << 21)/* backed by heap memory */
|
||||
#define _XBF_DELWRI_Q (1 << 22)/* buffer on delwri queue */
|
||||
|
||||
typedef unsigned int xfs_buf_flags_t;
|
||||
|
||||
#define XFS_BUF_FLAGS \
|
||||
{ XBF_READ, "READ" }, \
|
||||
{ XBF_WRITE, "WRITE" }, \
|
||||
{ XBF_READ_AHEAD, "READ_AHEAD" }, \
|
||||
{ XBF_MAPPED, "MAPPED" }, \
|
||||
{ XBF_ASYNC, "ASYNC" }, \
|
||||
{ XBF_DONE, "DONE" }, \
|
||||
{ XBF_DELWRI, "DELWRI" }, \
|
||||
{ XBF_STALE, "STALE" }, \
|
||||
{ XBF_SYNCIO, "SYNCIO" }, \
|
||||
{ XBF_FUA, "FUA" }, \
|
||||
{ XBF_FLUSH, "FLUSH" }, \
|
||||
{ XBF_LOCK, "LOCK" }, /* should never be set */\
|
||||
{ XBF_TRYLOCK, "TRYLOCK" }, /* ditto */\
|
||||
{ XBF_DONT_BLOCK, "DONT_BLOCK" }, /* ditto */\
|
||||
{ _XBF_PAGES, "PAGES" }, \
|
||||
{ _XBF_KMEM, "KMEM" }, \
|
||||
{ _XBF_DELWRI_Q, "DELWRI_Q" }
|
||||
|
||||
typedef enum {
|
||||
XBT_FORCE_SLEEP = 0,
|
||||
XBT_FORCE_FLUSH = 1,
|
||||
} xfs_buftarg_flags_t;
|
||||
|
||||
typedef struct xfs_buftarg {
|
||||
dev_t bt_dev;
|
||||
struct block_device *bt_bdev;
|
||||
struct backing_dev_info *bt_bdi;
|
||||
struct xfs_mount *bt_mount;
|
||||
unsigned int bt_bsize;
|
||||
unsigned int bt_sshift;
|
||||
size_t bt_smask;
|
||||
|
||||
/* per device delwri queue */
|
||||
struct task_struct *bt_task;
|
||||
struct list_head bt_delwrite_queue;
|
||||
spinlock_t bt_delwrite_lock;
|
||||
unsigned long bt_flags;
|
||||
|
||||
/* LRU control structures */
|
||||
struct shrinker bt_shrinker;
|
||||
struct list_head bt_lru;
|
||||
spinlock_t bt_lru_lock;
|
||||
unsigned int bt_lru_nr;
|
||||
} xfs_buftarg_t;
|
||||
|
||||
struct xfs_buf;
|
||||
typedef void (*xfs_buf_iodone_t)(struct xfs_buf *);
|
||||
|
||||
#define XB_PAGES 2
|
||||
|
||||
typedef struct xfs_buf {
|
||||
/*
|
||||
* first cacheline holds all the fields needed for an uncontended cache
|
||||
* hit to be fully processed. The semaphore straddles the cacheline
|
||||
* boundary, but the counter and lock sits on the first cacheline,
|
||||
* which is the only bit that is touched if we hit the semaphore
|
||||
* fast-path on locking.
|
||||
*/
|
||||
struct rb_node b_rbnode; /* rbtree node */
|
||||
xfs_off_t b_file_offset; /* offset in file */
|
||||
size_t b_buffer_length;/* size of buffer in bytes */
|
||||
atomic_t b_hold; /* reference count */
|
||||
atomic_t b_lru_ref; /* lru reclaim ref count */
|
||||
xfs_buf_flags_t b_flags; /* status flags */
|
||||
struct semaphore b_sema; /* semaphore for lockables */
|
||||
|
||||
struct list_head b_lru; /* lru list */
|
||||
wait_queue_head_t b_waiters; /* unpin waiters */
|
||||
struct list_head b_list;
|
||||
struct xfs_perag *b_pag; /* contains rbtree root */
|
||||
xfs_buftarg_t *b_target; /* buffer target (device) */
|
||||
xfs_daddr_t b_bn; /* block number for I/O */
|
||||
size_t b_count_desired;/* desired transfer size */
|
||||
void *b_addr; /* virtual address of buffer */
|
||||
struct work_struct b_iodone_work;
|
||||
xfs_buf_iodone_t b_iodone; /* I/O completion function */
|
||||
struct completion b_iowait; /* queue for I/O waiters */
|
||||
void *b_fspriv;
|
||||
struct xfs_trans *b_transp;
|
||||
struct page **b_pages; /* array of page pointers */
|
||||
struct page *b_page_array[XB_PAGES]; /* inline pages */
|
||||
unsigned long b_queuetime; /* time buffer was queued */
|
||||
atomic_t b_pin_count; /* pin count */
|
||||
atomic_t b_io_remaining; /* #outstanding I/O requests */
|
||||
unsigned int b_page_count; /* size of page array */
|
||||
unsigned int b_offset; /* page offset in first page */
|
||||
unsigned short b_error; /* error code on I/O */
|
||||
#ifdef XFS_BUF_LOCK_TRACKING
|
||||
int b_last_holder;
|
||||
#endif
|
||||
} xfs_buf_t;
|
||||
|
||||
|
||||
/* Finding and Reading Buffers */
|
||||
extern xfs_buf_t *_xfs_buf_find(xfs_buftarg_t *, xfs_off_t, size_t,
|
||||
xfs_buf_flags_t, xfs_buf_t *);
|
||||
#define xfs_incore(buftarg,blkno,len,lockit) \
|
||||
_xfs_buf_find(buftarg, blkno ,len, lockit, NULL)
|
||||
|
||||
extern xfs_buf_t *xfs_buf_get(xfs_buftarg_t *, xfs_off_t, size_t,
|
||||
xfs_buf_flags_t);
|
||||
extern xfs_buf_t *xfs_buf_read(xfs_buftarg_t *, xfs_off_t, size_t,
|
||||
xfs_buf_flags_t);
|
||||
|
||||
extern xfs_buf_t *xfs_buf_get_empty(size_t, xfs_buftarg_t *);
|
||||
extern void xfs_buf_set_empty(struct xfs_buf *bp, size_t len);
|
||||
extern xfs_buf_t *xfs_buf_get_uncached(struct xfs_buftarg *, size_t, int);
|
||||
extern int xfs_buf_associate_memory(xfs_buf_t *, void *, size_t);
|
||||
extern void xfs_buf_hold(xfs_buf_t *);
|
||||
extern void xfs_buf_readahead(xfs_buftarg_t *, xfs_off_t, size_t);
|
||||
struct xfs_buf *xfs_buf_read_uncached(struct xfs_mount *mp,
|
||||
struct xfs_buftarg *target,
|
||||
xfs_daddr_t daddr, size_t length, int flags);
|
||||
|
||||
/* Releasing Buffers */
|
||||
extern void xfs_buf_free(xfs_buf_t *);
|
||||
extern void xfs_buf_rele(xfs_buf_t *);
|
||||
|
||||
/* Locking and Unlocking Buffers */
|
||||
extern int xfs_buf_trylock(xfs_buf_t *);
|
||||
extern void xfs_buf_lock(xfs_buf_t *);
|
||||
extern void xfs_buf_unlock(xfs_buf_t *);
|
||||
#define xfs_buf_islocked(bp) \
|
||||
((bp)->b_sema.count <= 0)
|
||||
|
||||
/* Buffer Read and Write Routines */
|
||||
extern int xfs_bwrite(struct xfs_mount *mp, struct xfs_buf *bp);
|
||||
extern void xfs_bdwrite(void *mp, xfs_buf_t *bp);
|
||||
|
||||
extern void xfsbdstrat(struct xfs_mount *, struct xfs_buf *);
|
||||
extern int xfs_bdstrat_cb(struct xfs_buf *);
|
||||
|
||||
extern void xfs_buf_ioend(xfs_buf_t *, int);
|
||||
extern void xfs_buf_ioerror(xfs_buf_t *, int);
|
||||
extern int xfs_buf_iorequest(xfs_buf_t *);
|
||||
extern int xfs_buf_iowait(xfs_buf_t *);
|
||||
extern void xfs_buf_iomove(xfs_buf_t *, size_t, size_t, void *,
|
||||
xfs_buf_rw_t);
|
||||
#define xfs_buf_zero(bp, off, len) \
|
||||
xfs_buf_iomove((bp), (off), (len), NULL, XBRW_ZERO)
|
||||
|
||||
static inline int xfs_buf_geterror(xfs_buf_t *bp)
|
||||
{
|
||||
return bp ? bp->b_error : ENOMEM;
|
||||
}
|
||||
|
||||
/* Buffer Utility Routines */
|
||||
extern xfs_caddr_t xfs_buf_offset(xfs_buf_t *, size_t);
|
||||
|
||||
/* Delayed Write Buffer Routines */
|
||||
extern void xfs_buf_delwri_dequeue(xfs_buf_t *);
|
||||
extern void xfs_buf_delwri_promote(xfs_buf_t *);
|
||||
|
||||
/* Buffer Daemon Setup Routines */
|
||||
extern int xfs_buf_init(void);
|
||||
extern void xfs_buf_terminate(void);
|
||||
|
||||
static inline const char *
|
||||
xfs_buf_target_name(struct xfs_buftarg *target)
|
||||
{
|
||||
static char __b[BDEVNAME_SIZE];
|
||||
|
||||
return bdevname(target->bt_bdev, __b);
|
||||
}
|
||||
|
||||
|
||||
#define XFS_BUF_ZEROFLAGS(bp) \
|
||||
((bp)->b_flags &= ~(XBF_READ|XBF_WRITE|XBF_ASYNC|XBF_DELWRI| \
|
||||
XBF_SYNCIO|XBF_FUA|XBF_FLUSH))
|
||||
|
||||
void xfs_buf_stale(struct xfs_buf *bp);
|
||||
#define XFS_BUF_STALE(bp) xfs_buf_stale(bp);
|
||||
#define XFS_BUF_UNSTALE(bp) ((bp)->b_flags &= ~XBF_STALE)
|
||||
#define XFS_BUF_ISSTALE(bp) ((bp)->b_flags & XBF_STALE)
|
||||
#define XFS_BUF_SUPER_STALE(bp) do { \
|
||||
XFS_BUF_STALE(bp); \
|
||||
xfs_buf_delwri_dequeue(bp); \
|
||||
XFS_BUF_DONE(bp); \
|
||||
} while (0)
|
||||
|
||||
#define XFS_BUF_DELAYWRITE(bp) ((bp)->b_flags |= XBF_DELWRI)
|
||||
#define XFS_BUF_UNDELAYWRITE(bp) xfs_buf_delwri_dequeue(bp)
|
||||
#define XFS_BUF_ISDELAYWRITE(bp) ((bp)->b_flags & XBF_DELWRI)
|
||||
|
||||
#define XFS_BUF_DONE(bp) ((bp)->b_flags |= XBF_DONE)
|
||||
#define XFS_BUF_UNDONE(bp) ((bp)->b_flags &= ~XBF_DONE)
|
||||
#define XFS_BUF_ISDONE(bp) ((bp)->b_flags & XBF_DONE)
|
||||
|
||||
#define XFS_BUF_ASYNC(bp) ((bp)->b_flags |= XBF_ASYNC)
|
||||
#define XFS_BUF_UNASYNC(bp) ((bp)->b_flags &= ~XBF_ASYNC)
|
||||
#define XFS_BUF_ISASYNC(bp) ((bp)->b_flags & XBF_ASYNC)
|
||||
|
||||
#define XFS_BUF_READ(bp) ((bp)->b_flags |= XBF_READ)
|
||||
#define XFS_BUF_UNREAD(bp) ((bp)->b_flags &= ~XBF_READ)
|
||||
#define XFS_BUF_ISREAD(bp) ((bp)->b_flags & XBF_READ)
|
||||
|
||||
#define XFS_BUF_WRITE(bp) ((bp)->b_flags |= XBF_WRITE)
|
||||
#define XFS_BUF_UNWRITE(bp) ((bp)->b_flags &= ~XBF_WRITE)
|
||||
#define XFS_BUF_ISWRITE(bp) ((bp)->b_flags & XBF_WRITE)
|
||||
|
||||
#define XFS_BUF_ADDR(bp) ((bp)->b_bn)
|
||||
#define XFS_BUF_SET_ADDR(bp, bno) ((bp)->b_bn = (xfs_daddr_t)(bno))
|
||||
#define XFS_BUF_OFFSET(bp) ((bp)->b_file_offset)
|
||||
#define XFS_BUF_SET_OFFSET(bp, off) ((bp)->b_file_offset = (off))
|
||||
#define XFS_BUF_COUNT(bp) ((bp)->b_count_desired)
|
||||
#define XFS_BUF_SET_COUNT(bp, cnt) ((bp)->b_count_desired = (cnt))
|
||||
#define XFS_BUF_SIZE(bp) ((bp)->b_buffer_length)
|
||||
#define XFS_BUF_SET_SIZE(bp, cnt) ((bp)->b_buffer_length = (cnt))
|
||||
|
||||
static inline void
|
||||
xfs_buf_set_ref(
|
||||
struct xfs_buf *bp,
|
||||
int lru_ref)
|
||||
{
|
||||
atomic_set(&bp->b_lru_ref, lru_ref);
|
||||
}
|
||||
#define XFS_BUF_SET_VTYPE_REF(bp, type, ref) xfs_buf_set_ref(bp, ref)
|
||||
#define XFS_BUF_SET_VTYPE(bp, type) do { } while (0)
|
||||
|
||||
static inline int xfs_buf_ispinned(struct xfs_buf *bp)
|
||||
{
|
||||
return atomic_read(&bp->b_pin_count);
|
||||
}
|
||||
|
||||
#define XFS_BUF_FINISH_IOWAIT(bp) complete(&bp->b_iowait);
|
||||
|
||||
static inline void xfs_buf_relse(xfs_buf_t *bp)
|
||||
{
|
||||
xfs_buf_unlock(bp);
|
||||
xfs_buf_rele(bp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handling of buftargs.
|
||||
*/
|
||||
extern xfs_buftarg_t *xfs_alloc_buftarg(struct xfs_mount *,
|
||||
struct block_device *, int, const char *);
|
||||
extern void xfs_free_buftarg(struct xfs_mount *, struct xfs_buftarg *);
|
||||
extern void xfs_wait_buftarg(xfs_buftarg_t *);
|
||||
extern int xfs_setsize_buftarg(xfs_buftarg_t *, unsigned int, unsigned int);
|
||||
extern int xfs_flush_buftarg(xfs_buftarg_t *, int);
|
||||
|
||||
#ifdef CONFIG_KDB_MODULES
|
||||
extern struct list_head *xfs_get_buftarg_list(void);
|
||||
#endif
|
||||
|
||||
#define xfs_getsize_buftarg(buftarg) block_size((buftarg)->bt_bdev)
|
||||
#define xfs_readonly_buftarg(buftarg) bdev_read_only((buftarg)->bt_bdev)
|
||||
|
||||
#define xfs_binval(buftarg) xfs_flush_buftarg(buftarg, 1)
|
||||
#define XFS_bflush(buftarg) xfs_flush_buftarg(buftarg, 1)
|
||||
|
||||
#endif /* __XFS_BUF_H__ */
|
||||
222
fs/xfs/xfs_discard.c
Normal file
222
fs/xfs/xfs_discard.c
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
/*
|
||||
* Copyright (C) 2010 Red Hat, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it would be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include "xfs.h"
|
||||
#include "xfs_sb.h"
|
||||
#include "xfs_inum.h"
|
||||
#include "xfs_log.h"
|
||||
#include "xfs_ag.h"
|
||||
#include "xfs_mount.h"
|
||||
#include "xfs_quota.h"
|
||||
#include "xfs_trans.h"
|
||||
#include "xfs_alloc_btree.h"
|
||||
#include "xfs_bmap_btree.h"
|
||||
#include "xfs_ialloc_btree.h"
|
||||
#include "xfs_btree.h"
|
||||
#include "xfs_inode.h"
|
||||
#include "xfs_alloc.h"
|
||||
#include "xfs_error.h"
|
||||
#include "xfs_discard.h"
|
||||
#include "xfs_trace.h"
|
||||
|
||||
STATIC int
|
||||
xfs_trim_extents(
|
||||
struct xfs_mount *mp,
|
||||
xfs_agnumber_t agno,
|
||||
xfs_fsblock_t start,
|
||||
xfs_fsblock_t len,
|
||||
xfs_fsblock_t minlen,
|
||||
__uint64_t *blocks_trimmed)
|
||||
{
|
||||
struct block_device *bdev = mp->m_ddev_targp->bt_bdev;
|
||||
struct xfs_btree_cur *cur;
|
||||
struct xfs_buf *agbp;
|
||||
struct xfs_perag *pag;
|
||||
int error;
|
||||
int i;
|
||||
|
||||
pag = xfs_perag_get(mp, agno);
|
||||
|
||||
error = xfs_alloc_read_agf(mp, NULL, agno, 0, &agbp);
|
||||
if (error || !agbp)
|
||||
goto out_put_perag;
|
||||
|
||||
cur = xfs_allocbt_init_cursor(mp, NULL, agbp, agno, XFS_BTNUM_CNT);
|
||||
|
||||
/*
|
||||
* Force out the log. This means any transactions that might have freed
|
||||
* space before we took the AGF buffer lock are now on disk, and the
|
||||
* volatile disk cache is flushed.
|
||||
*/
|
||||
xfs_log_force(mp, XFS_LOG_SYNC);
|
||||
|
||||
/*
|
||||
* Look up the longest btree in the AGF and start with it.
|
||||
*/
|
||||
error = xfs_alloc_lookup_le(cur, 0,
|
||||
XFS_BUF_TO_AGF(agbp)->agf_longest, &i);
|
||||
if (error)
|
||||
goto out_del_cursor;
|
||||
|
||||
/*
|
||||
* Loop until we are done with all extents that are large
|
||||
* enough to be worth discarding.
|
||||
*/
|
||||
while (i) {
|
||||
xfs_agblock_t fbno;
|
||||
xfs_extlen_t flen;
|
||||
|
||||
error = xfs_alloc_get_rec(cur, &fbno, &flen, &i);
|
||||
if (error)
|
||||
goto out_del_cursor;
|
||||
XFS_WANT_CORRUPTED_GOTO(i == 1, out_del_cursor);
|
||||
ASSERT(flen <= XFS_BUF_TO_AGF(agbp)->agf_longest);
|
||||
|
||||
/*
|
||||
* Too small? Give up.
|
||||
*/
|
||||
if (flen < minlen) {
|
||||
trace_xfs_discard_toosmall(mp, agno, fbno, flen);
|
||||
goto out_del_cursor;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the extent is entirely outside of the range we are
|
||||
* supposed to discard skip it. Do not bother to trim
|
||||
* down partially overlapping ranges for now.
|
||||
*/
|
||||
if (XFS_AGB_TO_FSB(mp, agno, fbno) + flen < start ||
|
||||
XFS_AGB_TO_FSB(mp, agno, fbno) >= start + len) {
|
||||
trace_xfs_discard_exclude(mp, agno, fbno, flen);
|
||||
goto next_extent;
|
||||
}
|
||||
|
||||
/*
|
||||
* If any blocks in the range are still busy, skip the
|
||||
* discard and try again the next time.
|
||||
*/
|
||||
if (xfs_alloc_busy_search(mp, agno, fbno, flen)) {
|
||||
trace_xfs_discard_busy(mp, agno, fbno, flen);
|
||||
goto next_extent;
|
||||
}
|
||||
|
||||
trace_xfs_discard_extent(mp, agno, fbno, flen);
|
||||
error = -blkdev_issue_discard(bdev,
|
||||
XFS_AGB_TO_DADDR(mp, agno, fbno),
|
||||
XFS_FSB_TO_BB(mp, flen),
|
||||
GFP_NOFS, 0);
|
||||
if (error)
|
||||
goto out_del_cursor;
|
||||
*blocks_trimmed += flen;
|
||||
|
||||
next_extent:
|
||||
error = xfs_btree_decrement(cur, 0, &i);
|
||||
if (error)
|
||||
goto out_del_cursor;
|
||||
}
|
||||
|
||||
out_del_cursor:
|
||||
xfs_btree_del_cursor(cur, error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
|
||||
xfs_buf_relse(agbp);
|
||||
out_put_perag:
|
||||
xfs_perag_put(pag);
|
||||
return error;
|
||||
}
|
||||
|
||||
int
|
||||
xfs_ioc_trim(
|
||||
struct xfs_mount *mp,
|
||||
struct fstrim_range __user *urange)
|
||||
{
|
||||
struct request_queue *q = mp->m_ddev_targp->bt_bdev->bd_disk->queue;
|
||||
unsigned int granularity = q->limits.discard_granularity;
|
||||
struct fstrim_range range;
|
||||
xfs_fsblock_t start, len, minlen;
|
||||
xfs_agnumber_t start_agno, end_agno, agno;
|
||||
__uint64_t blocks_trimmed = 0;
|
||||
int error, last_error = 0;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -XFS_ERROR(EPERM);
|
||||
if (!blk_queue_discard(q))
|
||||
return -XFS_ERROR(EOPNOTSUPP);
|
||||
if (copy_from_user(&range, urange, sizeof(range)))
|
||||
return -XFS_ERROR(EFAULT);
|
||||
|
||||
/*
|
||||
* Truncating down the len isn't actually quite correct, but using
|
||||
* XFS_B_TO_FSB would mean we trivially get overflows for values
|
||||
* of ULLONG_MAX or slightly lower. And ULLONG_MAX is the default
|
||||
* used by the fstrim application. In the end it really doesn't
|
||||
* matter as trimming blocks is an advisory interface.
|
||||
*/
|
||||
start = XFS_B_TO_FSBT(mp, range.start);
|
||||
len = XFS_B_TO_FSBT(mp, range.len);
|
||||
minlen = XFS_B_TO_FSB(mp, max_t(u64, granularity, range.minlen));
|
||||
|
||||
start_agno = XFS_FSB_TO_AGNO(mp, start);
|
||||
if (start_agno >= mp->m_sb.sb_agcount)
|
||||
return -XFS_ERROR(EINVAL);
|
||||
|
||||
end_agno = XFS_FSB_TO_AGNO(mp, start + len);
|
||||
if (end_agno >= mp->m_sb.sb_agcount)
|
||||
end_agno = mp->m_sb.sb_agcount - 1;
|
||||
|
||||
for (agno = start_agno; agno <= end_agno; agno++) {
|
||||
error = -xfs_trim_extents(mp, agno, start, len, minlen,
|
||||
&blocks_trimmed);
|
||||
if (error)
|
||||
last_error = error;
|
||||
}
|
||||
|
||||
if (last_error)
|
||||
return last_error;
|
||||
|
||||
range.len = XFS_FSB_TO_B(mp, blocks_trimmed);
|
||||
if (copy_to_user(urange, &range, sizeof(range)))
|
||||
return -XFS_ERROR(EFAULT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
xfs_discard_extents(
|
||||
struct xfs_mount *mp,
|
||||
struct list_head *list)
|
||||
{
|
||||
struct xfs_busy_extent *busyp;
|
||||
int error = 0;
|
||||
|
||||
list_for_each_entry(busyp, list, list) {
|
||||
trace_xfs_discard_extent(mp, busyp->agno, busyp->bno,
|
||||
busyp->length);
|
||||
|
||||
error = -blkdev_issue_discard(mp->m_ddev_targp->bt_bdev,
|
||||
XFS_AGB_TO_DADDR(mp, busyp->agno, busyp->bno),
|
||||
XFS_FSB_TO_BB(mp, busyp->length),
|
||||
GFP_NOFS, 0);
|
||||
if (error && error != EOPNOTSUPP) {
|
||||
xfs_info(mp,
|
||||
"discard failed for extent [0x%llu,%u], error %d",
|
||||
(unsigned long long)busyp->bno,
|
||||
busyp->length,
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
10
fs/xfs/xfs_discard.h
Normal file
10
fs/xfs/xfs_discard.h
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef XFS_DISCARD_H
|
||||
#define XFS_DISCARD_H 1
|
||||
|
||||
struct fstrim_range;
|
||||
struct list_head;
|
||||
|
||||
extern int xfs_ioc_trim(struct xfs_mount *, struct fstrim_range __user *);
|
||||
extern int xfs_discard_extents(struct xfs_mount *, struct list_head *);
|
||||
|
||||
#endif /* XFS_DISCARD_H */
|
||||
1454
fs/xfs/xfs_dquot.c
Normal file
1454
fs/xfs/xfs_dquot.c
Normal file
File diff suppressed because it is too large
Load diff
137
fs/xfs/xfs_dquot.h
Normal file
137
fs/xfs/xfs_dquot.h
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* Copyright (c) 2000-2005 Silicon Graphics, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it would be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef __XFS_DQUOT_H__
|
||||
#define __XFS_DQUOT_H__
|
||||
|
||||
/*
|
||||
* Dquots are structures that hold quota information about a user or a group,
|
||||
* much like inodes are for files. In fact, dquots share many characteristics
|
||||
* with inodes. However, dquots can also be a centralized resource, relative
|
||||
* to a collection of inodes. In this respect, dquots share some characteristics
|
||||
* of the superblock.
|
||||
* XFS dquots exploit both those in its algorithms. They make every attempt
|
||||
* to not be a bottleneck when quotas are on and have minimal impact, if any,
|
||||
* when quotas are off.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The hash chain headers (hash buckets)
|
||||
*/
|
||||
typedef struct xfs_dqhash {
|
||||
struct list_head qh_list;
|
||||
struct mutex qh_lock;
|
||||
uint qh_version; /* ever increasing version */
|
||||
uint qh_nelems; /* number of dquots on the list */
|
||||
} xfs_dqhash_t;
|
||||
|
||||
struct xfs_mount;
|
||||
struct xfs_trans;
|
||||
|
||||
/*
|
||||
* The incore dquot structure
|
||||
*/
|
||||
typedef struct xfs_dquot {
|
||||
uint dq_flags; /* various flags (XFS_DQ_*) */
|
||||
struct list_head q_freelist; /* global free list of dquots */
|
||||
struct list_head q_mplist; /* mount's list of dquots */
|
||||
struct list_head q_hashlist; /* gloabl hash list of dquots */
|
||||
xfs_dqhash_t *q_hash; /* the hashchain header */
|
||||
struct xfs_mount*q_mount; /* filesystem this relates to */
|
||||
struct xfs_trans*q_transp; /* trans this belongs to currently */
|
||||
uint q_nrefs; /* # active refs from inodes */
|
||||
xfs_daddr_t q_blkno; /* blkno of dquot buffer */
|
||||
int q_bufoffset; /* off of dq in buffer (# dquots) */
|
||||
xfs_fileoff_t q_fileoffset; /* offset in quotas file */
|
||||
|
||||
struct xfs_dquot*q_gdquot; /* group dquot, hint only */
|
||||
xfs_disk_dquot_t q_core; /* actual usage & quotas */
|
||||
xfs_dq_logitem_t q_logitem; /* dquot log item */
|
||||
xfs_qcnt_t q_res_bcount; /* total regular nblks used+reserved */
|
||||
xfs_qcnt_t q_res_icount; /* total inos allocd+reserved */
|
||||
xfs_qcnt_t q_res_rtbcount;/* total realtime blks used+reserved */
|
||||
struct mutex q_qlock; /* quota lock */
|
||||
struct completion q_flush; /* flush completion queue */
|
||||
atomic_t q_pincount; /* dquot pin count */
|
||||
wait_queue_head_t q_pinwait; /* dquot pinning wait queue */
|
||||
} xfs_dquot_t;
|
||||
|
||||
/*
|
||||
* Lock hierarchy for q_qlock:
|
||||
* XFS_QLOCK_NORMAL is the implicit default,
|
||||
* XFS_QLOCK_NESTED is the dquot with the higher id in xfs_dqlock2
|
||||
*/
|
||||
enum {
|
||||
XFS_QLOCK_NORMAL = 0,
|
||||
XFS_QLOCK_NESTED,
|
||||
};
|
||||
|
||||
#define XFS_DQHOLD(dqp) ((dqp)->q_nrefs++)
|
||||
|
||||
/*
|
||||
* Manage the q_flush completion queue embedded in the dquot. This completion
|
||||
* queue synchronizes processes attempting to flush the in-core dquot back to
|
||||
* disk.
|
||||
*/
|
||||
static inline void xfs_dqflock(xfs_dquot_t *dqp)
|
||||
{
|
||||
wait_for_completion(&dqp->q_flush);
|
||||
}
|
||||
|
||||
static inline int xfs_dqflock_nowait(xfs_dquot_t *dqp)
|
||||
{
|
||||
return try_wait_for_completion(&dqp->q_flush);
|
||||
}
|
||||
|
||||
static inline void xfs_dqfunlock(xfs_dquot_t *dqp)
|
||||
{
|
||||
complete(&dqp->q_flush);
|
||||
}
|
||||
|
||||
#define XFS_DQ_IS_LOCKED(dqp) (mutex_is_locked(&((dqp)->q_qlock)))
|
||||
#define XFS_DQ_IS_DIRTY(dqp) ((dqp)->dq_flags & XFS_DQ_DIRTY)
|
||||
#define XFS_QM_ISUDQ(dqp) ((dqp)->dq_flags & XFS_DQ_USER)
|
||||
#define XFS_QM_ISPDQ(dqp) ((dqp)->dq_flags & XFS_DQ_PROJ)
|
||||
#define XFS_QM_ISGDQ(dqp) ((dqp)->dq_flags & XFS_DQ_GROUP)
|
||||
#define XFS_DQ_TO_QINF(dqp) ((dqp)->q_mount->m_quotainfo)
|
||||
#define XFS_DQ_TO_QIP(dqp) (XFS_QM_ISUDQ(dqp) ? \
|
||||
XFS_DQ_TO_QINF(dqp)->qi_uquotaip : \
|
||||
XFS_DQ_TO_QINF(dqp)->qi_gquotaip)
|
||||
|
||||
#define XFS_IS_THIS_QUOTA_OFF(d) (! (XFS_QM_ISUDQ(d) ? \
|
||||
(XFS_IS_UQUOTA_ON((d)->q_mount)) : \
|
||||
(XFS_IS_OQUOTA_ON((d)->q_mount))))
|
||||
|
||||
extern void xfs_qm_dqdestroy(xfs_dquot_t *);
|
||||
extern int xfs_qm_dqflush(xfs_dquot_t *, uint);
|
||||
extern int xfs_qm_dqpurge(xfs_dquot_t *);
|
||||
extern void xfs_qm_dqunpin_wait(xfs_dquot_t *);
|
||||
extern int xfs_qm_dqlock_nowait(xfs_dquot_t *);
|
||||
extern void xfs_qm_dqflock_pushbuf_wait(xfs_dquot_t *dqp);
|
||||
extern void xfs_qm_adjust_dqtimers(xfs_mount_t *,
|
||||
xfs_disk_dquot_t *);
|
||||
extern void xfs_qm_adjust_dqlimits(xfs_mount_t *,
|
||||
xfs_disk_dquot_t *);
|
||||
extern int xfs_qm_dqget(xfs_mount_t *, xfs_inode_t *,
|
||||
xfs_dqid_t, uint, uint, xfs_dquot_t **);
|
||||
extern void xfs_qm_dqput(xfs_dquot_t *);
|
||||
extern void xfs_dqlock(xfs_dquot_t *);
|
||||
extern void xfs_dqlock2(xfs_dquot_t *, xfs_dquot_t *);
|
||||
extern void xfs_dqunlock(xfs_dquot_t *);
|
||||
extern void xfs_dqunlock_nonotify(xfs_dquot_t *);
|
||||
|
||||
#endif /* __XFS_DQUOT_H__ */
|
||||
529
fs/xfs/xfs_dquot_item.c
Normal file
529
fs/xfs/xfs_dquot_item.c
Normal file
|
|
@ -0,0 +1,529 @@
|
|||
/*
|
||||
* Copyright (c) 2000-2003 Silicon Graphics, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it would be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include "xfs.h"
|
||||
#include "xfs_fs.h"
|
||||
#include "xfs_bit.h"
|
||||
#include "xfs_log.h"
|
||||
#include "xfs_inum.h"
|
||||
#include "xfs_trans.h"
|
||||
#include "xfs_sb.h"
|
||||
#include "xfs_ag.h"
|
||||
#include "xfs_alloc.h"
|
||||
#include "xfs_quota.h"
|
||||
#include "xfs_mount.h"
|
||||
#include "xfs_bmap_btree.h"
|
||||
#include "xfs_inode.h"
|
||||
#include "xfs_bmap.h"
|
||||
#include "xfs_rtalloc.h"
|
||||
#include "xfs_error.h"
|
||||
#include "xfs_itable.h"
|
||||
#include "xfs_attr.h"
|
||||
#include "xfs_buf_item.h"
|
||||
#include "xfs_trans_priv.h"
|
||||
#include "xfs_qm.h"
|
||||
|
||||
static inline struct xfs_dq_logitem *DQUOT_ITEM(struct xfs_log_item *lip)
|
||||
{
|
||||
return container_of(lip, struct xfs_dq_logitem, qli_item);
|
||||
}
|
||||
|
||||
/*
|
||||
* returns the number of iovecs needed to log the given dquot item.
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_qm_dquot_logitem_size(
|
||||
struct xfs_log_item *lip)
|
||||
{
|
||||
/*
|
||||
* we need only two iovecs, one for the format, one for the real thing
|
||||
*/
|
||||
return 2;
|
||||
}
|
||||
|
||||
/*
|
||||
* fills in the vector of log iovecs for the given dquot log item.
|
||||
*/
|
||||
STATIC void
|
||||
xfs_qm_dquot_logitem_format(
|
||||
struct xfs_log_item *lip,
|
||||
struct xfs_log_iovec *logvec)
|
||||
{
|
||||
struct xfs_dq_logitem *qlip = DQUOT_ITEM(lip);
|
||||
|
||||
logvec->i_addr = &qlip->qli_format;
|
||||
logvec->i_len = sizeof(xfs_dq_logformat_t);
|
||||
logvec->i_type = XLOG_REG_TYPE_QFORMAT;
|
||||
logvec++;
|
||||
logvec->i_addr = &qlip->qli_dquot->q_core;
|
||||
logvec->i_len = sizeof(xfs_disk_dquot_t);
|
||||
logvec->i_type = XLOG_REG_TYPE_DQUOT;
|
||||
|
||||
ASSERT(2 == lip->li_desc->lid_size);
|
||||
qlip->qli_format.qlf_size = 2;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Increment the pin count of the given dquot.
|
||||
*/
|
||||
STATIC void
|
||||
xfs_qm_dquot_logitem_pin(
|
||||
struct xfs_log_item *lip)
|
||||
{
|
||||
struct xfs_dquot *dqp = DQUOT_ITEM(lip)->qli_dquot;
|
||||
|
||||
ASSERT(XFS_DQ_IS_LOCKED(dqp));
|
||||
atomic_inc(&dqp->q_pincount);
|
||||
}
|
||||
|
||||
/*
|
||||
* Decrement the pin count of the given dquot, and wake up
|
||||
* anyone in xfs_dqwait_unpin() if the count goes to 0. The
|
||||
* dquot must have been previously pinned with a call to
|
||||
* xfs_qm_dquot_logitem_pin().
|
||||
*/
|
||||
STATIC void
|
||||
xfs_qm_dquot_logitem_unpin(
|
||||
struct xfs_log_item *lip,
|
||||
int remove)
|
||||
{
|
||||
struct xfs_dquot *dqp = DQUOT_ITEM(lip)->qli_dquot;
|
||||
|
||||
ASSERT(atomic_read(&dqp->q_pincount) > 0);
|
||||
if (atomic_dec_and_test(&dqp->q_pincount))
|
||||
wake_up(&dqp->q_pinwait);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given the logitem, this writes the corresponding dquot entry to disk
|
||||
* asynchronously. This is called with the dquot entry securely locked;
|
||||
* we simply get xfs_qm_dqflush() to do the work, and unlock the dquot
|
||||
* at the end.
|
||||
*/
|
||||
STATIC void
|
||||
xfs_qm_dquot_logitem_push(
|
||||
struct xfs_log_item *lip)
|
||||
{
|
||||
struct xfs_dquot *dqp = DQUOT_ITEM(lip)->qli_dquot;
|
||||
int error;
|
||||
|
||||
ASSERT(XFS_DQ_IS_LOCKED(dqp));
|
||||
ASSERT(!completion_done(&dqp->q_flush));
|
||||
|
||||
/*
|
||||
* Since we were able to lock the dquot's flush lock and
|
||||
* we found it on the AIL, the dquot must be dirty. This
|
||||
* is because the dquot is removed from the AIL while still
|
||||
* holding the flush lock in xfs_dqflush_done(). Thus, if
|
||||
* we found it in the AIL and were able to obtain the flush
|
||||
* lock without sleeping, then there must not have been
|
||||
* anyone in the process of flushing the dquot.
|
||||
*/
|
||||
error = xfs_qm_dqflush(dqp, 0);
|
||||
if (error)
|
||||
xfs_warn(dqp->q_mount, "%s: push error %d on dqp %p",
|
||||
__func__, error, dqp);
|
||||
xfs_dqunlock(dqp);
|
||||
}
|
||||
|
||||
STATIC xfs_lsn_t
|
||||
xfs_qm_dquot_logitem_committed(
|
||||
struct xfs_log_item *lip,
|
||||
xfs_lsn_t lsn)
|
||||
{
|
||||
/*
|
||||
* We always re-log the entire dquot when it becomes dirty,
|
||||
* so, the latest copy _is_ the only one that matters.
|
||||
*/
|
||||
return lsn;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called to wait for the given dquot to be unpinned.
|
||||
* Most of these pin/unpin routines are plagiarized from inode code.
|
||||
*/
|
||||
void
|
||||
xfs_qm_dqunpin_wait(
|
||||
struct xfs_dquot *dqp)
|
||||
{
|
||||
ASSERT(XFS_DQ_IS_LOCKED(dqp));
|
||||
if (atomic_read(&dqp->q_pincount) == 0)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Give the log a push so we don't wait here too long.
|
||||
*/
|
||||
xfs_log_force(dqp->q_mount, 0);
|
||||
wait_event(dqp->q_pinwait, (atomic_read(&dqp->q_pincount) == 0));
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called when IOP_TRYLOCK returns XFS_ITEM_PUSHBUF to indicate that
|
||||
* the dquot is locked by us, but the flush lock isn't. So, here we are
|
||||
* going to see if the relevant dquot buffer is incore, waiting on DELWRI.
|
||||
* If so, we want to push it out to help us take this item off the AIL as soon
|
||||
* as possible.
|
||||
*
|
||||
* We must not be holding the AIL lock at this point. Calling incore() to
|
||||
* search the buffer cache can be a time consuming thing, and AIL lock is a
|
||||
* spinlock.
|
||||
*/
|
||||
STATIC void
|
||||
xfs_qm_dquot_logitem_pushbuf(
|
||||
struct xfs_log_item *lip)
|
||||
{
|
||||
struct xfs_dq_logitem *qlip = DQUOT_ITEM(lip);
|
||||
struct xfs_dquot *dqp = qlip->qli_dquot;
|
||||
struct xfs_buf *bp;
|
||||
|
||||
ASSERT(XFS_DQ_IS_LOCKED(dqp));
|
||||
|
||||
/*
|
||||
* If flushlock isn't locked anymore, chances are that the
|
||||
* inode flush completed and the inode was taken off the AIL.
|
||||
* So, just get out.
|
||||
*/
|
||||
if (completion_done(&dqp->q_flush) ||
|
||||
!(lip->li_flags & XFS_LI_IN_AIL)) {
|
||||
xfs_dqunlock(dqp);
|
||||
return;
|
||||
}
|
||||
|
||||
bp = xfs_incore(dqp->q_mount->m_ddev_targp, qlip->qli_format.qlf_blkno,
|
||||
dqp->q_mount->m_quotainfo->qi_dqchunklen, XBF_TRYLOCK);
|
||||
xfs_dqunlock(dqp);
|
||||
if (!bp)
|
||||
return;
|
||||
if (XFS_BUF_ISDELAYWRITE(bp))
|
||||
xfs_buf_delwri_promote(bp);
|
||||
xfs_buf_relse(bp);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called to attempt to lock the dquot associated with this
|
||||
* dquot log item. Don't sleep on the dquot lock or the flush lock.
|
||||
* If the flush lock is already held, indicating that the dquot has
|
||||
* been or is in the process of being flushed, then see if we can
|
||||
* find the dquot's buffer in the buffer cache without sleeping. If
|
||||
* we can and it is marked delayed write, then we want to send it out.
|
||||
* We delay doing so until the push routine, though, to avoid sleeping
|
||||
* in any device strategy routines.
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_qm_dquot_logitem_trylock(
|
||||
struct xfs_log_item *lip)
|
||||
{
|
||||
struct xfs_dquot *dqp = DQUOT_ITEM(lip)->qli_dquot;
|
||||
|
||||
if (atomic_read(&dqp->q_pincount) > 0)
|
||||
return XFS_ITEM_PINNED;
|
||||
|
||||
if (!xfs_qm_dqlock_nowait(dqp))
|
||||
return XFS_ITEM_LOCKED;
|
||||
|
||||
if (!xfs_dqflock_nowait(dqp)) {
|
||||
/*
|
||||
* dquot has already been flushed to the backing buffer,
|
||||
* leave it locked, pushbuf routine will unlock it.
|
||||
*/
|
||||
return XFS_ITEM_PUSHBUF;
|
||||
}
|
||||
|
||||
ASSERT(lip->li_flags & XFS_LI_IN_AIL);
|
||||
return XFS_ITEM_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unlock the dquot associated with the log item.
|
||||
* Clear the fields of the dquot and dquot log item that
|
||||
* are specific to the current transaction. If the
|
||||
* hold flags is set, do not unlock the dquot.
|
||||
*/
|
||||
STATIC void
|
||||
xfs_qm_dquot_logitem_unlock(
|
||||
struct xfs_log_item *lip)
|
||||
{
|
||||
struct xfs_dquot *dqp = DQUOT_ITEM(lip)->qli_dquot;
|
||||
|
||||
ASSERT(XFS_DQ_IS_LOCKED(dqp));
|
||||
|
||||
/*
|
||||
* Clear the transaction pointer in the dquot
|
||||
*/
|
||||
dqp->q_transp = NULL;
|
||||
|
||||
/*
|
||||
* dquots are never 'held' from getting unlocked at the end of
|
||||
* a transaction. Their locking and unlocking is hidden inside the
|
||||
* transaction layer, within trans_commit. Hence, no LI_HOLD flag
|
||||
* for the logitem.
|
||||
*/
|
||||
xfs_dqunlock(dqp);
|
||||
}
|
||||
|
||||
/*
|
||||
* this needs to stamp an lsn into the dquot, I think.
|
||||
* rpc's that look at user dquot's would then have to
|
||||
* push on the dependency recorded in the dquot
|
||||
*/
|
||||
STATIC void
|
||||
xfs_qm_dquot_logitem_committing(
|
||||
struct xfs_log_item *lip,
|
||||
xfs_lsn_t lsn)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the ops vector for dquots
|
||||
*/
|
||||
static struct xfs_item_ops xfs_dquot_item_ops = {
|
||||
.iop_size = xfs_qm_dquot_logitem_size,
|
||||
.iop_format = xfs_qm_dquot_logitem_format,
|
||||
.iop_pin = xfs_qm_dquot_logitem_pin,
|
||||
.iop_unpin = xfs_qm_dquot_logitem_unpin,
|
||||
.iop_trylock = xfs_qm_dquot_logitem_trylock,
|
||||
.iop_unlock = xfs_qm_dquot_logitem_unlock,
|
||||
.iop_committed = xfs_qm_dquot_logitem_committed,
|
||||
.iop_push = xfs_qm_dquot_logitem_push,
|
||||
.iop_pushbuf = xfs_qm_dquot_logitem_pushbuf,
|
||||
.iop_committing = xfs_qm_dquot_logitem_committing
|
||||
};
|
||||
|
||||
/*
|
||||
* Initialize the dquot log item for a newly allocated dquot.
|
||||
* The dquot isn't locked at this point, but it isn't on any of the lists
|
||||
* either, so we don't care.
|
||||
*/
|
||||
void
|
||||
xfs_qm_dquot_logitem_init(
|
||||
struct xfs_dquot *dqp)
|
||||
{
|
||||
struct xfs_dq_logitem *lp = &dqp->q_logitem;
|
||||
|
||||
xfs_log_item_init(dqp->q_mount, &lp->qli_item, XFS_LI_DQUOT,
|
||||
&xfs_dquot_item_ops);
|
||||
lp->qli_dquot = dqp;
|
||||
lp->qli_format.qlf_type = XFS_LI_DQUOT;
|
||||
lp->qli_format.qlf_id = be32_to_cpu(dqp->q_core.d_id);
|
||||
lp->qli_format.qlf_blkno = dqp->q_blkno;
|
||||
lp->qli_format.qlf_len = 1;
|
||||
/*
|
||||
* This is just the offset of this dquot within its buffer
|
||||
* (which is currently 1 FSB and probably won't change).
|
||||
* Hence 32 bits for this offset should be just fine.
|
||||
* Alternatively, we can store (bufoffset / sizeof(xfs_dqblk_t))
|
||||
* here, and recompute it at recovery time.
|
||||
*/
|
||||
lp->qli_format.qlf_boffset = (__uint32_t)dqp->q_bufoffset;
|
||||
}
|
||||
|
||||
/*------------------ QUOTAOFF LOG ITEMS -------------------*/
|
||||
|
||||
static inline struct xfs_qoff_logitem *QOFF_ITEM(struct xfs_log_item *lip)
|
||||
{
|
||||
return container_of(lip, struct xfs_qoff_logitem, qql_item);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This returns the number of iovecs needed to log the given quotaoff item.
|
||||
* We only need 1 iovec for an quotaoff item. It just logs the
|
||||
* quotaoff_log_format structure.
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_qm_qoff_logitem_size(
|
||||
struct xfs_log_item *lip)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called to fill in the vector of log iovecs for the
|
||||
* given quotaoff log item. We use only 1 iovec, and we point that
|
||||
* at the quotaoff_log_format structure embedded in the quotaoff item.
|
||||
* It is at this point that we assert that all of the extent
|
||||
* slots in the quotaoff item have been filled.
|
||||
*/
|
||||
STATIC void
|
||||
xfs_qm_qoff_logitem_format(
|
||||
struct xfs_log_item *lip,
|
||||
struct xfs_log_iovec *log_vector)
|
||||
{
|
||||
struct xfs_qoff_logitem *qflip = QOFF_ITEM(lip);
|
||||
|
||||
ASSERT(qflip->qql_format.qf_type == XFS_LI_QUOTAOFF);
|
||||
|
||||
log_vector->i_addr = &qflip->qql_format;
|
||||
log_vector->i_len = sizeof(xfs_qoff_logitem_t);
|
||||
log_vector->i_type = XLOG_REG_TYPE_QUOTAOFF;
|
||||
qflip->qql_format.qf_size = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pinning has no meaning for an quotaoff item, so just return.
|
||||
*/
|
||||
STATIC void
|
||||
xfs_qm_qoff_logitem_pin(
|
||||
struct xfs_log_item *lip)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Since pinning has no meaning for an quotaoff item, unpinning does
|
||||
* not either.
|
||||
*/
|
||||
STATIC void
|
||||
xfs_qm_qoff_logitem_unpin(
|
||||
struct xfs_log_item *lip,
|
||||
int remove)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Quotaoff items have no locking, so just return success.
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_qm_qoff_logitem_trylock(
|
||||
struct xfs_log_item *lip)
|
||||
{
|
||||
return XFS_ITEM_LOCKED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Quotaoff items have no locking or pushing, so return failure
|
||||
* so that the caller doesn't bother with us.
|
||||
*/
|
||||
STATIC void
|
||||
xfs_qm_qoff_logitem_unlock(
|
||||
struct xfs_log_item *lip)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* The quotaoff-start-item is logged only once and cannot be moved in the log,
|
||||
* so simply return the lsn at which it's been logged.
|
||||
*/
|
||||
STATIC xfs_lsn_t
|
||||
xfs_qm_qoff_logitem_committed(
|
||||
struct xfs_log_item *lip,
|
||||
xfs_lsn_t lsn)
|
||||
{
|
||||
return lsn;
|
||||
}
|
||||
|
||||
/*
|
||||
* There isn't much you can do to push on an quotaoff item. It is simply
|
||||
* stuck waiting for the log to be flushed to disk.
|
||||
*/
|
||||
STATIC void
|
||||
xfs_qm_qoff_logitem_push(
|
||||
struct xfs_log_item *lip)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
STATIC xfs_lsn_t
|
||||
xfs_qm_qoffend_logitem_committed(
|
||||
struct xfs_log_item *lip,
|
||||
xfs_lsn_t lsn)
|
||||
{
|
||||
struct xfs_qoff_logitem *qfe = QOFF_ITEM(lip);
|
||||
struct xfs_qoff_logitem *qfs = qfe->qql_start_lip;
|
||||
struct xfs_ail *ailp = qfs->qql_item.li_ailp;
|
||||
|
||||
/*
|
||||
* Delete the qoff-start logitem from the AIL.
|
||||
* xfs_trans_ail_delete() drops the AIL lock.
|
||||
*/
|
||||
spin_lock(&ailp->xa_lock);
|
||||
xfs_trans_ail_delete(ailp, (xfs_log_item_t *)qfs);
|
||||
|
||||
kmem_free(qfs);
|
||||
kmem_free(qfe);
|
||||
return (xfs_lsn_t)-1;
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX rcc - don't know quite what to do with this. I think we can
|
||||
* just ignore it. The only time that isn't the case is if we allow
|
||||
* the client to somehow see that quotas have been turned off in which
|
||||
* we can't allow that to get back until the quotaoff hits the disk.
|
||||
* So how would that happen? Also, do we need different routines for
|
||||
* quotaoff start and quotaoff end? I suspect the answer is yes but
|
||||
* to be sure, I need to look at the recovery code and see how quota off
|
||||
* recovery is handled (do we roll forward or back or do something else).
|
||||
* If we roll forwards or backwards, then we need two separate routines,
|
||||
* one that does nothing and one that stamps in the lsn that matters
|
||||
* (truly makes the quotaoff irrevocable). If we do something else,
|
||||
* then maybe we don't need two.
|
||||
*/
|
||||
STATIC void
|
||||
xfs_qm_qoff_logitem_committing(
|
||||
struct xfs_log_item *lip,
|
||||
xfs_lsn_t commit_lsn)
|
||||
{
|
||||
}
|
||||
|
||||
static struct xfs_item_ops xfs_qm_qoffend_logitem_ops = {
|
||||
.iop_size = xfs_qm_qoff_logitem_size,
|
||||
.iop_format = xfs_qm_qoff_logitem_format,
|
||||
.iop_pin = xfs_qm_qoff_logitem_pin,
|
||||
.iop_unpin = xfs_qm_qoff_logitem_unpin,
|
||||
.iop_trylock = xfs_qm_qoff_logitem_trylock,
|
||||
.iop_unlock = xfs_qm_qoff_logitem_unlock,
|
||||
.iop_committed = xfs_qm_qoffend_logitem_committed,
|
||||
.iop_push = xfs_qm_qoff_logitem_push,
|
||||
.iop_committing = xfs_qm_qoff_logitem_committing
|
||||
};
|
||||
|
||||
/*
|
||||
* This is the ops vector shared by all quotaoff-start log items.
|
||||
*/
|
||||
static struct xfs_item_ops xfs_qm_qoff_logitem_ops = {
|
||||
.iop_size = xfs_qm_qoff_logitem_size,
|
||||
.iop_format = xfs_qm_qoff_logitem_format,
|
||||
.iop_pin = xfs_qm_qoff_logitem_pin,
|
||||
.iop_unpin = xfs_qm_qoff_logitem_unpin,
|
||||
.iop_trylock = xfs_qm_qoff_logitem_trylock,
|
||||
.iop_unlock = xfs_qm_qoff_logitem_unlock,
|
||||
.iop_committed = xfs_qm_qoff_logitem_committed,
|
||||
.iop_push = xfs_qm_qoff_logitem_push,
|
||||
.iop_committing = xfs_qm_qoff_logitem_committing
|
||||
};
|
||||
|
||||
/*
|
||||
* Allocate and initialize an quotaoff item of the correct quota type(s).
|
||||
*/
|
||||
struct xfs_qoff_logitem *
|
||||
xfs_qm_qoff_logitem_init(
|
||||
struct xfs_mount *mp,
|
||||
struct xfs_qoff_logitem *start,
|
||||
uint flags)
|
||||
{
|
||||
struct xfs_qoff_logitem *qf;
|
||||
|
||||
qf = kmem_zalloc(sizeof(struct xfs_qoff_logitem), KM_SLEEP);
|
||||
|
||||
xfs_log_item_init(mp, &qf->qql_item, XFS_LI_QUOTAOFF, start ?
|
||||
&xfs_qm_qoffend_logitem_ops : &xfs_qm_qoff_logitem_ops);
|
||||
qf->qql_item.li_mountp = mp;
|
||||
qf->qql_format.qf_type = XFS_LI_QUOTAOFF;
|
||||
qf->qql_format.qf_flags = flags;
|
||||
qf->qql_start_lip = start;
|
||||
return qf;
|
||||
}
|
||||
48
fs/xfs/xfs_dquot_item.h
Normal file
48
fs/xfs/xfs_dquot_item.h
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (c) 2000-2003 Silicon Graphics, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it would be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef __XFS_DQUOT_ITEM_H__
|
||||
#define __XFS_DQUOT_ITEM_H__
|
||||
|
||||
struct xfs_dquot;
|
||||
struct xfs_trans;
|
||||
struct xfs_mount;
|
||||
struct xfs_qoff_logitem;
|
||||
|
||||
typedef struct xfs_dq_logitem {
|
||||
xfs_log_item_t qli_item; /* common portion */
|
||||
struct xfs_dquot *qli_dquot; /* dquot ptr */
|
||||
xfs_lsn_t qli_flush_lsn; /* lsn at last flush */
|
||||
xfs_dq_logformat_t qli_format; /* logged structure */
|
||||
} xfs_dq_logitem_t;
|
||||
|
||||
typedef struct xfs_qoff_logitem {
|
||||
xfs_log_item_t qql_item; /* common portion */
|
||||
struct xfs_qoff_logitem *qql_start_lip; /* qoff-start logitem, if any */
|
||||
xfs_qoff_logformat_t qql_format; /* logged structure */
|
||||
} xfs_qoff_logitem_t;
|
||||
|
||||
|
||||
extern void xfs_qm_dquot_logitem_init(struct xfs_dquot *);
|
||||
extern xfs_qoff_logitem_t *xfs_qm_qoff_logitem_init(struct xfs_mount *,
|
||||
struct xfs_qoff_logitem *, uint);
|
||||
extern xfs_qoff_logitem_t *xfs_trans_get_qoff_item(struct xfs_trans *,
|
||||
struct xfs_qoff_logitem *, uint);
|
||||
extern void xfs_trans_log_quotaoff_item(struct xfs_trans *,
|
||||
struct xfs_qoff_logitem *);
|
||||
|
||||
#endif /* __XFS_DQUOT_ITEM_H__ */
|
||||
250
fs/xfs/xfs_export.c
Normal file
250
fs/xfs/xfs_export.c
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
/*
|
||||
* Copyright (c) 2004-2005 Silicon Graphics, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it would be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include "xfs.h"
|
||||
#include "xfs_types.h"
|
||||
#include "xfs_inum.h"
|
||||
#include "xfs_log.h"
|
||||
#include "xfs_trans.h"
|
||||
#include "xfs_sb.h"
|
||||
#include "xfs_ag.h"
|
||||
#include "xfs_dir2.h"
|
||||
#include "xfs_mount.h"
|
||||
#include "xfs_export.h"
|
||||
#include "xfs_vnodeops.h"
|
||||
#include "xfs_bmap_btree.h"
|
||||
#include "xfs_inode.h"
|
||||
#include "xfs_inode_item.h"
|
||||
#include "xfs_trace.h"
|
||||
|
||||
/*
|
||||
* Note that we only accept fileids which are long enough rather than allow
|
||||
* the parent generation number to default to zero. XFS considers zero a
|
||||
* valid generation number not an invalid/wildcard value.
|
||||
*/
|
||||
static int xfs_fileid_length(int fileid_type)
|
||||
{
|
||||
switch (fileid_type) {
|
||||
case FILEID_INO32_GEN:
|
||||
return 2;
|
||||
case FILEID_INO32_GEN_PARENT:
|
||||
return 4;
|
||||
case FILEID_INO32_GEN | XFS_FILEID_TYPE_64FLAG:
|
||||
return 3;
|
||||
case FILEID_INO32_GEN_PARENT | XFS_FILEID_TYPE_64FLAG:
|
||||
return 6;
|
||||
}
|
||||
return 255; /* invalid */
|
||||
}
|
||||
|
||||
STATIC int
|
||||
xfs_fs_encode_fh(
|
||||
struct dentry *dentry,
|
||||
__u32 *fh,
|
||||
int *max_len,
|
||||
int connectable)
|
||||
{
|
||||
struct fid *fid = (struct fid *)fh;
|
||||
struct xfs_fid64 *fid64 = (struct xfs_fid64 *)fh;
|
||||
struct inode *inode = dentry->d_inode;
|
||||
int fileid_type;
|
||||
int len;
|
||||
|
||||
/* Directories don't need their parent encoded, they have ".." */
|
||||
if (S_ISDIR(inode->i_mode) || !connectable)
|
||||
fileid_type = FILEID_INO32_GEN;
|
||||
else
|
||||
fileid_type = FILEID_INO32_GEN_PARENT;
|
||||
|
||||
/*
|
||||
* If the the filesystem may contain 64bit inode numbers, we need
|
||||
* to use larger file handles that can represent them.
|
||||
*
|
||||
* While we only allocate inodes that do not fit into 32 bits any
|
||||
* large enough filesystem may contain them, thus the slightly
|
||||
* confusing looking conditional below.
|
||||
*/
|
||||
if (!(XFS_M(inode->i_sb)->m_flags & XFS_MOUNT_SMALL_INUMS) ||
|
||||
(XFS_M(inode->i_sb)->m_flags & XFS_MOUNT_32BITINODES))
|
||||
fileid_type |= XFS_FILEID_TYPE_64FLAG;
|
||||
|
||||
/*
|
||||
* Only encode if there is enough space given. In practice
|
||||
* this means we can't export a filesystem with 64bit inodes
|
||||
* over NFSv2 with the subtree_check export option; the other
|
||||
* seven combinations work. The real answer is "don't use v2".
|
||||
*/
|
||||
len = xfs_fileid_length(fileid_type);
|
||||
if (*max_len < len) {
|
||||
*max_len = len;
|
||||
return 255;
|
||||
}
|
||||
*max_len = len;
|
||||
|
||||
switch (fileid_type) {
|
||||
case FILEID_INO32_GEN_PARENT:
|
||||
spin_lock(&dentry->d_lock);
|
||||
fid->i32.parent_ino = dentry->d_parent->d_inode->i_ino;
|
||||
fid->i32.parent_gen = dentry->d_parent->d_inode->i_generation;
|
||||
spin_unlock(&dentry->d_lock);
|
||||
/*FALLTHRU*/
|
||||
case FILEID_INO32_GEN:
|
||||
fid->i32.ino = inode->i_ino;
|
||||
fid->i32.gen = inode->i_generation;
|
||||
break;
|
||||
case FILEID_INO32_GEN_PARENT | XFS_FILEID_TYPE_64FLAG:
|
||||
spin_lock(&dentry->d_lock);
|
||||
fid64->parent_ino = dentry->d_parent->d_inode->i_ino;
|
||||
fid64->parent_gen = dentry->d_parent->d_inode->i_generation;
|
||||
spin_unlock(&dentry->d_lock);
|
||||
/*FALLTHRU*/
|
||||
case FILEID_INO32_GEN | XFS_FILEID_TYPE_64FLAG:
|
||||
fid64->ino = inode->i_ino;
|
||||
fid64->gen = inode->i_generation;
|
||||
break;
|
||||
}
|
||||
|
||||
return fileid_type;
|
||||
}
|
||||
|
||||
STATIC struct inode *
|
||||
xfs_nfs_get_inode(
|
||||
struct super_block *sb,
|
||||
u64 ino,
|
||||
u32 generation)
|
||||
{
|
||||
xfs_mount_t *mp = XFS_M(sb);
|
||||
xfs_inode_t *ip;
|
||||
int error;
|
||||
|
||||
/*
|
||||
* NFS can sometimes send requests for ino 0. Fail them gracefully.
|
||||
*/
|
||||
if (ino == 0)
|
||||
return ERR_PTR(-ESTALE);
|
||||
|
||||
/*
|
||||
* The XFS_IGET_UNTRUSTED means that an invalid inode number is just
|
||||
* fine and not an indication of a corrupted filesystem as clients can
|
||||
* send invalid file handles and we have to handle it gracefully..
|
||||
*/
|
||||
error = xfs_iget(mp, NULL, ino, XFS_IGET_UNTRUSTED, 0, &ip);
|
||||
if (error) {
|
||||
/*
|
||||
* EINVAL means the inode cluster doesn't exist anymore.
|
||||
* This implies the filehandle is stale, so we should
|
||||
* translate it here.
|
||||
* We don't use ESTALE directly down the chain to not
|
||||
* confuse applications using bulkstat that expect EINVAL.
|
||||
*/
|
||||
if (error == EINVAL || error == ENOENT)
|
||||
error = ESTALE;
|
||||
return ERR_PTR(-error);
|
||||
}
|
||||
|
||||
if (ip->i_d.di_gen != generation) {
|
||||
IRELE(ip);
|
||||
return ERR_PTR(-ESTALE);
|
||||
}
|
||||
|
||||
return VFS_I(ip);
|
||||
}
|
||||
|
||||
STATIC struct dentry *
|
||||
xfs_fs_fh_to_dentry(struct super_block *sb, struct fid *fid,
|
||||
int fh_len, int fileid_type)
|
||||
{
|
||||
struct xfs_fid64 *fid64 = (struct xfs_fid64 *)fid;
|
||||
struct inode *inode = NULL;
|
||||
|
||||
if (fh_len < xfs_fileid_length(fileid_type))
|
||||
return NULL;
|
||||
|
||||
switch (fileid_type) {
|
||||
case FILEID_INO32_GEN_PARENT:
|
||||
case FILEID_INO32_GEN:
|
||||
inode = xfs_nfs_get_inode(sb, fid->i32.ino, fid->i32.gen);
|
||||
break;
|
||||
case FILEID_INO32_GEN_PARENT | XFS_FILEID_TYPE_64FLAG:
|
||||
case FILEID_INO32_GEN | XFS_FILEID_TYPE_64FLAG:
|
||||
inode = xfs_nfs_get_inode(sb, fid64->ino, fid64->gen);
|
||||
break;
|
||||
}
|
||||
|
||||
return d_obtain_alias(inode);
|
||||
}
|
||||
|
||||
STATIC struct dentry *
|
||||
xfs_fs_fh_to_parent(struct super_block *sb, struct fid *fid,
|
||||
int fh_len, int fileid_type)
|
||||
{
|
||||
struct xfs_fid64 *fid64 = (struct xfs_fid64 *)fid;
|
||||
struct inode *inode = NULL;
|
||||
|
||||
switch (fileid_type) {
|
||||
case FILEID_INO32_GEN_PARENT:
|
||||
inode = xfs_nfs_get_inode(sb, fid->i32.parent_ino,
|
||||
fid->i32.parent_gen);
|
||||
break;
|
||||
case FILEID_INO32_GEN_PARENT | XFS_FILEID_TYPE_64FLAG:
|
||||
inode = xfs_nfs_get_inode(sb, fid64->parent_ino,
|
||||
fid64->parent_gen);
|
||||
break;
|
||||
}
|
||||
|
||||
return d_obtain_alias(inode);
|
||||
}
|
||||
|
||||
STATIC struct dentry *
|
||||
xfs_fs_get_parent(
|
||||
struct dentry *child)
|
||||
{
|
||||
int error;
|
||||
struct xfs_inode *cip;
|
||||
|
||||
error = xfs_lookup(XFS_I(child->d_inode), &xfs_name_dotdot, &cip, NULL);
|
||||
if (unlikely(error))
|
||||
return ERR_PTR(-error);
|
||||
|
||||
return d_obtain_alias(VFS_I(cip));
|
||||
}
|
||||
|
||||
STATIC int
|
||||
xfs_fs_nfs_commit_metadata(
|
||||
struct inode *inode)
|
||||
{
|
||||
struct xfs_inode *ip = XFS_I(inode);
|
||||
struct xfs_mount *mp = ip->i_mount;
|
||||
int error = 0;
|
||||
|
||||
xfs_ilock(ip, XFS_ILOCK_SHARED);
|
||||
if (xfs_ipincount(ip)) {
|
||||
error = _xfs_log_force_lsn(mp, ip->i_itemp->ili_last_lsn,
|
||||
XFS_LOG_SYNC, NULL);
|
||||
}
|
||||
xfs_iunlock(ip, XFS_ILOCK_SHARED);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
const struct export_operations xfs_export_operations = {
|
||||
.encode_fh = xfs_fs_encode_fh,
|
||||
.fh_to_dentry = xfs_fs_fh_to_dentry,
|
||||
.fh_to_parent = xfs_fs_fh_to_parent,
|
||||
.get_parent = xfs_fs_get_parent,
|
||||
.commit_metadata = xfs_fs_nfs_commit_metadata,
|
||||
};
|
||||
72
fs/xfs/xfs_export.h
Normal file
72
fs/xfs/xfs_export.h
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright (c) 2005 Silicon Graphics, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it would be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef __XFS_EXPORT_H__
|
||||
#define __XFS_EXPORT_H__
|
||||
|
||||
/*
|
||||
* Common defines for code related to exporting XFS filesystems over NFS.
|
||||
*
|
||||
* The NFS fileid goes out on the wire as an array of
|
||||
* 32bit unsigned ints in host order. There are 5 possible
|
||||
* formats.
|
||||
*
|
||||
* (1) fileid_type=0x00
|
||||
* (no fileid data; handled by the generic code)
|
||||
*
|
||||
* (2) fileid_type=0x01
|
||||
* inode-num
|
||||
* generation
|
||||
*
|
||||
* (3) fileid_type=0x02
|
||||
* inode-num
|
||||
* generation
|
||||
* parent-inode-num
|
||||
* parent-generation
|
||||
*
|
||||
* (4) fileid_type=0x81
|
||||
* inode-num-lo32
|
||||
* inode-num-hi32
|
||||
* generation
|
||||
*
|
||||
* (5) fileid_type=0x82
|
||||
* inode-num-lo32
|
||||
* inode-num-hi32
|
||||
* generation
|
||||
* parent-inode-num-lo32
|
||||
* parent-inode-num-hi32
|
||||
* parent-generation
|
||||
*
|
||||
* Note, the NFS filehandle also includes an fsid portion which
|
||||
* may have an inode number in it. That number is hardcoded to
|
||||
* 32bits and there is no way for XFS to intercept it. In
|
||||
* practice this means when exporting an XFS filesystem with 64bit
|
||||
* inodes you should either export the mountpoint (rather than
|
||||
* a subdirectory) or use the "fsid" export option.
|
||||
*/
|
||||
|
||||
struct xfs_fid64 {
|
||||
u64 ino;
|
||||
u32 gen;
|
||||
u64 parent_ino;
|
||||
u32 parent_gen;
|
||||
} __attribute__((packed));
|
||||
|
||||
/* This flag goes on the wire. Don't play with it. */
|
||||
#define XFS_FILEID_TYPE_64FLAG 0x80 /* NFS fileid has 64bit inodes */
|
||||
|
||||
#endif /* __XFS_EXPORT_H__ */
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue