History

xfs: add inode to zone caching for data placement

Placing data from the same file in the same zone is a great heuristic for reducing write amplification and we do this already - but only for sequential writes. To support placing data in the same way for random writes, reuse the xfs mru cache to map inodes to open zones on first write. If a mapping is present, use the open zone for data placement for this file until the zone is full.

Hans Holmberg committed 1 year ago

Commit f3e2e531 parent 70b95cb2 files · +110 −0Browse files

Unified · Split

2 files
  1. fs/xfs/xfs_mount.h +1 −0
  2. fs/xfs/xfs_zone_alloc.c +109 −0
fs/xfs/xfs_mount.h+1−0
@@ -236,6 +236,7 @@ typedef struct xfs_mount { · 235 unmodified lines
236 bool m_update_sb; /* sb needs update in mount */236 bool m_update_sb; /* sb needs update in mount */
237 unsigned int m_max_open_zones;237 unsigned int m_max_open_zones;
238 unsigned int m_zonegc_low_space;238 unsigned int m_zonegc_low_space;
239 struct xfs_mru_cache *m_zone_cache; /* Inode to open zone cache */
239 240
240 /* max_atomic_write mount option value */241 /* max_atomic_write mount option value */
241 unsigned long long m_awu_max_bytes;242 unsigned long long m_awu_max_bytes;
fs/xfs/xfs_zone_alloc.c+109−0
@@ -24,6 +24,7 @@ · 23 unmodified lines
24#include "xfs_zone_priv.h"24#include "xfs_zone_priv.h"
25#include "xfs_zones.h"25#include "xfs_zones.h"
26#include "xfs_trace.h"26#include "xfs_trace.h"
27#include "xfs_mru_cache.h"
27 28
28void29void
29xfs_open_zone_put(30xfs_open_zone_put(
@@ -796,6 +797,100 @@ xfs_submit_zoned_bio( · 766 unmodified lines
796 submit_bio(&ioend->io_bio);797 submit_bio(&ioend->io_bio);
797}798}
798 799
800/*
801 * Cache the last zone written to for an inode so that it is considered first
802 * for subsequent writes.
803 */
804struct xfs_zone_cache_item {
805 struct xfs_mru_cache_elem mru;
806 struct xfs_open_zone *oz;
807};
808
809static inline struct xfs_zone_cache_item *
810xfs_zone_cache_item(struct xfs_mru_cache_elem *mru)
811{
812 return container_of(mru, struct xfs_zone_cache_item, mru);
813}
814
815static void
816xfs_zone_cache_free_func(
817 void *data,
818 struct xfs_mru_cache_elem *mru)
819{
820 struct xfs_zone_cache_item *item = xfs_zone_cache_item(mru);
821
822 xfs_open_zone_put(item->oz);
823 kfree(item);
824}
825
826/*
827 * Check if we have a cached last open zone available for the inode and
828 * if yes return a reference to it.
829 */
830static struct xfs_open_zone *
831xfs_cached_zone(
832 struct xfs_mount *mp,
833 struct xfs_inode *ip)
834{
835 struct xfs_mru_cache_elem *mru;
836 struct xfs_open_zone *oz;
837
838 mru = xfs_mru_cache_lookup(mp->m_zone_cache, ip->i_ino);
839 if (!mru)
840 return NULL;
841 oz = xfs_zone_cache_item(mru)->oz;
842 if (oz) {
843 /*
844 * GC only steals open zones at mount time, so no GC zones
845 * should end up in the cache.
846 */
847 ASSERT(!oz->oz_is_gc);
848 ASSERT(atomic_read(&oz->oz_ref) > 0);
849 atomic_inc(&oz->oz_ref);
850 }
851 xfs_mru_cache_done(mp->m_zone_cache);
852 return oz;
853}
854
855/*
856 * Update the last used zone cache for a given inode.
857 *
858 * The caller must have a reference on the open zone.
859 */
860static void
861xfs_zone_cache_create_association(
862 struct xfs_inode *ip,
863 struct xfs_open_zone *oz)
864{
865 struct xfs_mount *mp = ip->i_mount;
866 struct xfs_zone_cache_item *item = NULL;
867 struct xfs_mru_cache_elem *mru;
868
869 ASSERT(atomic_read(&oz->oz_ref) > 0);
870 atomic_inc(&oz->oz_ref);
871
872 mru = xfs_mru_cache_lookup(mp->m_zone_cache, ip->i_ino);
873 if (mru) {
874 /*
875 * If we have an association already, update it to point to the
876 * new zone.
877 */
878 item = xfs_zone_cache_item(mru);
879 xfs_open_zone_put(item->oz);
880 item->oz = oz;
881 xfs_mru_cache_done(mp->m_zone_cache);
882 return;
883 }
884
885 item = kmalloc(sizeof(*item), GFP_KERNEL);
886 if (!item) {
887 xfs_open_zone_put(oz);
888 return;
889 }
890 item->oz = oz;
891 xfs_mru_cache_insert(mp->m_zone_cache, ip->i_ino, &item->mru);
892}
893
799void894void
800xfs_zone_alloc_and_submit(895xfs_zone_alloc_and_submit(
801 struct iomap_ioend *ioend,896 struct iomap_ioend *ioend,
@@ -819,11 +914,16 @@ xfs_zone_alloc_and_submit( · 17 unmodified lines
819 */914 */
820 if (!*oz && ioend->io_offset)915 if (!*oz && ioend->io_offset)
821 *oz = xfs_last_used_zone(ioend);916 *oz = xfs_last_used_zone(ioend);
917 if (!*oz)
918 *oz = xfs_cached_zone(mp, ip);
919
822 if (!*oz) {920 if (!*oz) {
823select_zone:921select_zone:
824 *oz = xfs_select_zone(mp, write_hint, pack_tight);922 *oz = xfs_select_zone(mp, write_hint, pack_tight);
825 if (!*oz)923 if (!*oz)
826 goto out_error;924 goto out_error;
925
926 xfs_zone_cache_create_association(ip, *oz);
827 }927 }
828 928
829 alloc_len = xfs_zone_alloc_blocks(*oz, XFS_B_TO_FSB(mp, ioend->io_size),929 alloc_len = xfs_zone_alloc_blocks(*oz, XFS_B_TO_FSB(mp, ioend->io_size),
@@ -1211,6 +1311,14 @@ xfs_mount_zones( · 381 unmodified lines
1211 error = xfs_zone_gc_mount(mp);1311 error = xfs_zone_gc_mount(mp);
1212 if (error)1312 if (error)
1213 goto out_free_zone_info;1313 goto out_free_zone_info;
1314
1315 /*
1316 * Set up a mru cache to track inode to open zone for data placement
1317 * purposes. The magic values for group count and life time is the
1318 * same as the defaults for file streams, which seems sane enough.
1319 */
1320 xfs_mru_cache_create(&mp->m_zone_cache, mp,
1321 5000, 10, xfs_zone_cache_free_func);
1214 return 0;1322 return 0;
1215 1323
1216out_free_zone_info:1324out_free_zone_info:
@@ -1224,4 +1332,5 @@ xfs_unmount_zones( · 7 unmodified lines
1224{1332{
1225 xfs_zone_gc_unmount(mp);1333 xfs_zone_gc_unmount(mp);
1226 xfs_free_zone_info(mp->m_zone_info);1334 xfs_free_zone_info(mp->m_zone_info);
1335 xfs_mru_cache_destroy(mp->m_zone_cache);
1227}1336}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t