diff options
author | Brian Behlendorf <[email protected]> | 2011-10-24 16:55:20 -0700 |
---|---|---|
committer | Brian Behlendorf <[email protected]> | 2011-11-28 15:45:51 -0800 |
commit | 82a37189aac955c81a59a5ecc3400475adb56355 (patch) | |
tree | f67d24ae075a3e97bcc3000c7da2e1abd9c6258f /module/zfs/sa.c | |
parent | e89236fd28cbe8585146b8b5d0ced1f8d4aa8431 (diff) |
Implement SA based xattrs
The current ZFS implementation stores xattrs on disk using a hidden
directory. In this directory a file name represents the xattr name
and the file contexts are the xattr binary data. This approach is
very flexible and allows for arbitrarily large xattrs. However,
it also suffers from a significant performance penalty. Accessing
a single xattr can requires up to three disk seeks.
1) Lookup the dnode object.
2) Lookup the dnodes's xattr directory object.
3) Lookup the xattr object in the directory.
To avoid this performance penalty Linux filesystems such as ext3
and xfs try to store the xattr as part of the inode on disk. When
the xattr is to large to store in the inode then a single external
block is allocated for them. In practice most xattrs are small
and this approach works well.
The addition of System Attributes (SA) to zfs provides us a clean
way to make this optimization. When the dataset property 'xattr=sa'
is set then xattrs will be preferentially stored as System Attributes.
This allows tiny xattrs (~100 bytes) to be stored with the dnode and
up to 64k of xattrs to be stored in the spill block. If additional
xattr space is required, which is unlikely under Linux, they will be
stored using the traditional directory approach.
This optimization results in roughly a 3x performance improvement
when accessing xattrs which brings zfs roughly to parity with ext4
and xfs (see table below). When multiple xattrs are stored per-file
the performance improvements are even greater because all of the
xattrs stored in the spill block will be cached.
However, by default SA based xattrs are disabled in the Linux port
to maximize compatibility with other implementations. If you do
enable SA based xattrs then they will not be visible on platforms
which do not support this feature.
----------------------------------------------------------------------
Time in seconds to get/set one xattr of N bytes on 100,000 files
------+--------------------------------+------------------------------
| setxattr | getxattr
bytes | ext4 xfs zfs-dir zfs-sa | ext4 xfs zfs-dir zfs-sa
------+--------------------------------+------------------------------
1 | 2.33 31.88 21.50 4.57 | 2.35 2.64 6.29 2.43
32 | 2.79 30.68 21.98 4.60 | 2.44 2.59 6.78 2.48
256 | 3.25 31.99 21.36 5.92 | 2.32 2.71 6.22 3.14
1024 | 3.30 32.61 22.83 8.45 | 2.40 2.79 6.24 3.27
4096 | 3.57 317.46 22.52 10.73 | 2.78 28.62 6.90 3.94
16384 | n/a 2342.39 34.30 19.20 | n/a 45.44 145.90 7.55
65536 | n/a 2941.39 128.15 131.32* | n/a 141.92 256.85 262.12*
Legend:
* ext4 - Stock RHEL6.1 ext4 mounted with '-o user_xattr'.
* xfs - Stock RHEL6.1 xfs mounted with default options.
* zfs-dir - Directory based xattrs only.
* zfs-sa - Prefer SAs but spill in to directories as needed, a
trailing * indicates overflow in to directories occured.
NOTE: Ext4 supports 4096 bytes of xattr name/value pairs per file.
NOTE: XFS and ZFS have no limit on xattr name/value pairs per file.
NOTE: Linux limits individual name/value pairs to 65536 bytes.
NOTE: All setattr/getattr's were done after dropping the cache.
NOTE: All tests were run against a single hard drive.
Signed-off-by: Brian Behlendorf <[email protected]>
Issue #443
Diffstat (limited to 'module/zfs/sa.c')
-rw-r--r-- | module/zfs/sa.c | 28 |
1 files changed, 24 insertions, 4 deletions
diff --git a/module/zfs/sa.c b/module/zfs/sa.c index 4278ed7e4..bcef7d1fb 100644 --- a/module/zfs/sa.c +++ b/module/zfs/sa.c @@ -201,6 +201,7 @@ sa_attr_type_t sa_dummy_zpl_layout[] = { 0 }; static int sa_legacy_attr_count = 16; static kmem_cache_t *sa_cache = NULL; +static kmem_cache_t *spill_cache = NULL; /*ARGSUSED*/ static int @@ -232,6 +233,8 @@ sa_cache_init(void) sa_cache = kmem_cache_create("sa_cache", sizeof (sa_handle_t), 0, sa_cache_constructor, sa_cache_destructor, NULL, NULL, NULL, 0); + spill_cache = kmem_cache_create("spill_cache", + SPA_MAXBLOCKSIZE, 0, NULL, NULL, NULL, NULL, NULL, 0); } void @@ -239,6 +242,21 @@ sa_cache_fini(void) { if (sa_cache) kmem_cache_destroy(sa_cache); + + if (spill_cache) + kmem_cache_destroy(spill_cache); +} + +void * +sa_spill_alloc(int flags) +{ + return kmem_cache_alloc(spill_cache, flags); +} + +void +sa_spill_free(void *obj) +{ + kmem_cache_free(spill_cache, obj); } static int @@ -1618,7 +1636,7 @@ sa_modify_attrs(sa_handle_t *hdl, sa_attr_type_t newattr, sa_bulk_attr_t *attr_desc; void *old_data[2]; int bonus_attr_count = 0; - int bonus_data_size = 0, spill_data_size = 0; + int bonus_data_size = 0; int spill_attr_count = 0; int error; uint16_t length; @@ -1648,8 +1666,8 @@ sa_modify_attrs(sa_handle_t *hdl, sa_attr_type_t newattr, /* Bring spill buffer online if it isn't currently */ if ((error = sa_get_spill(hdl)) == 0) { - spill_data_size = hdl->sa_spill->db_size; - old_data[1] = kmem_alloc(spill_data_size, KM_SLEEP); + ASSERT3U(hdl->sa_spill->db_size, <=, SPA_MAXBLOCKSIZE); + old_data[1] = sa_spill_alloc(KM_SLEEP); bcopy(hdl->sa_spill->db_data, old_data[1], hdl->sa_spill->db_size); spill_attr_count = @@ -1729,7 +1747,7 @@ sa_modify_attrs(sa_handle_t *hdl, sa_attr_type_t newattr, if (old_data[0]) kmem_free(old_data[0], bonus_data_size); if (old_data[1]) - kmem_free(old_data[1], spill_data_size); + sa_spill_free(old_data[1]); kmem_free(attr_desc, sizeof (sa_bulk_attr_t) * attr_count); return (error); @@ -1998,6 +2016,8 @@ EXPORT_SYMBOL(sa_replace_all_by_template_locked); EXPORT_SYMBOL(sa_enabled); EXPORT_SYMBOL(sa_cache_init); EXPORT_SYMBOL(sa_cache_fini); +EXPORT_SYMBOL(sa_spill_alloc); +EXPORT_SYMBOL(sa_spill_free); EXPORT_SYMBOL(sa_set_sa_object); EXPORT_SYMBOL(sa_hdrsize); EXPORT_SYMBOL(sa_handle_lock); |