|
|
| version 1.387, 2006/11/30 14:40:05 | version 1.388, 2006/12/04 02:51:48 |
|---|---|
| Line 52 STATIC void xfs_unmountfs_wait(xfs_mount | Line 52 STATIC void xfs_unmountfs_wait(xfs_mount |
| #ifdef HAVE_PERCPU_SB | #ifdef HAVE_PERCPU_SB |
| STATIC void xfs_icsb_destroy_counters(xfs_mount_t *); | STATIC void xfs_icsb_destroy_counters(xfs_mount_t *); |
| STATIC void xfs_icsb_balance_counter(xfs_mount_t *, xfs_sb_field_t, int); | STATIC void xfs_icsb_balance_counter(xfs_mount_t *, xfs_sb_field_t, int, |
| int); | |
| STATIC void xfs_icsb_sync_counters(xfs_mount_t *); | STATIC void xfs_icsb_sync_counters(xfs_mount_t *); |
| STATIC int xfs_icsb_modify_counters(xfs_mount_t *, xfs_sb_field_t, | STATIC int xfs_icsb_modify_counters(xfs_mount_t *, xfs_sb_field_t, |
| int, int); | int, int); |
| STATIC int xfs_icsb_modify_counters_locked(xfs_mount_t *, xfs_sb_field_t, | |
| int, int); | |
| STATIC int xfs_icsb_disable_counter(xfs_mount_t *, xfs_sb_field_t); | STATIC int xfs_icsb_disable_counter(xfs_mount_t *, xfs_sb_field_t); |
| #else | #else |
| #define xfs_icsb_destroy_counters(mp) do { } while (0) | #define xfs_icsb_destroy_counters(mp) do { } while (0) |
| #define xfs_icsb_balance_counter(mp, a, b) do { } while (0) | #define xfs_icsb_balance_counter(mp, a, b, c) do { } while (0) |
| #define xfs_icsb_sync_counters(mp) do { } while (0) | #define xfs_icsb_sync_counters(mp) do { } while (0) |
| #define xfs_icsb_modify_counters(mp, a, b, c) do { } while (0) | #define xfs_icsb_modify_counters(mp, a, b, c) do { } while (0) |
| #define xfs_icsb_modify_counters_locked(mp, a, b, c) do { } while (0) | |
| #endif | #endif |
| Line 545 xfs_readsb(xfs_mount_t *mp, int flags) | Line 543 xfs_readsb(xfs_mount_t *mp, int flags) |
| ASSERT(XFS_BUF_VALUSEMA(bp) <= 0); | ASSERT(XFS_BUF_VALUSEMA(bp) <= 0); |
| } | } |
| xfs_icsb_balance_counter(mp, XFS_SBS_ICOUNT, 0); | mutex_lock(&mp->m_icsb_mutex); |
| xfs_icsb_balance_counter(mp, XFS_SBS_IFREE, 0); | xfs_icsb_balance_counter(mp, XFS_SBS_ICOUNT, 0, 0); |
| xfs_icsb_balance_counter(mp, XFS_SBS_FDBLOCKS, 0); | xfs_icsb_balance_counter(mp, XFS_SBS_IFREE, 0, 0); |
| xfs_icsb_balance_counter(mp, XFS_SBS_FDBLOCKS, 0, 0); | |
| mutex_unlock(&mp->m_icsb_mutex); | |
| mp->m_sb_bp = bp; | mp->m_sb_bp = bp; |
| xfs_buf_relse(bp); | xfs_buf_relse(bp); |
| Line 1485 xfs_mod_incore_sb_batch(xfs_mount_t *mp, | Line 1485 xfs_mod_incore_sb_batch(xfs_mount_t *mp, |
| case XFS_SBS_IFREE: | case XFS_SBS_IFREE: |
| case XFS_SBS_FDBLOCKS: | case XFS_SBS_FDBLOCKS: |
| if (!(mp->m_flags & XFS_MOUNT_NO_PERCPU_SB)) { | if (!(mp->m_flags & XFS_MOUNT_NO_PERCPU_SB)) { |
| status = xfs_icsb_modify_counters_locked(mp, | XFS_SB_UNLOCK(mp, s); |
| status = xfs_icsb_modify_counters(mp, | |
| msbp->msb_field, | msbp->msb_field, |
| msbp->msb_delta, rsvd); | msbp->msb_delta, rsvd); |
| s = XFS_SB_LOCK(mp); | |
| break; | break; |
| } | } |
| /* FALLTHROUGH */ | /* FALLTHROUGH */ |
| Line 1521 xfs_mod_incore_sb_batch(xfs_mount_t *mp, | Line 1523 xfs_mod_incore_sb_batch(xfs_mount_t *mp, |
| case XFS_SBS_IFREE: | case XFS_SBS_IFREE: |
| case XFS_SBS_FDBLOCKS: | case XFS_SBS_FDBLOCKS: |
| if (!(mp->m_flags & XFS_MOUNT_NO_PERCPU_SB)) { | if (!(mp->m_flags & XFS_MOUNT_NO_PERCPU_SB)) { |
| status = | XFS_SB_UNLOCK(mp, s); |
| xfs_icsb_modify_counters_locked(mp, | status = xfs_icsb_modify_counters(mp, |
| msbp->msb_field, | msbp->msb_field, |
| -(msbp->msb_delta), | -(msbp->msb_delta), |
| rsvd); | rsvd); |
| s = XFS_SB_LOCK(mp); | |
| break; | break; |
| } | } |
| /* FALLTHROUGH */ | /* FALLTHROUGH */ |
| Line 1733 xfs_icsb_cpu_notify( | Line 1736 xfs_icsb_cpu_notify( |
| memset(cntp, 0, sizeof(xfs_icsb_cnts_t)); | memset(cntp, 0, sizeof(xfs_icsb_cnts_t)); |
| break; | break; |
| case CPU_ONLINE: | case CPU_ONLINE: |
| xfs_icsb_balance_counter(mp, XFS_SBS_ICOUNT, 0); | mutex_lock(&mp->m_icsb_mutex); |
| xfs_icsb_balance_counter(mp, XFS_SBS_IFREE, 0); | xfs_icsb_balance_counter(mp, XFS_SBS_ICOUNT, 0, 0); |
| xfs_icsb_balance_counter(mp, XFS_SBS_FDBLOCKS, 0); | xfs_icsb_balance_counter(mp, XFS_SBS_IFREE, 0, 0); |
| xfs_icsb_balance_counter(mp, XFS_SBS_FDBLOCKS, 0, 0); | |
| mutex_unlock(&mp->m_icsb_mutex); | |
| break; | break; |
| case CPU_DEAD: | case CPU_DEAD: |
| /* Disable all the counters, then fold the dead cpu's | /* Disable all the counters, then fold the dead cpu's |
| * count into the total on the global superblock and | * count into the total on the global superblock and |
| * re-enable the counters. */ | * re-enable the counters. */ |
| mutex_lock(&mp->m_icsb_mutex); | |
| s = XFS_SB_LOCK(mp); | s = XFS_SB_LOCK(mp); |
| xfs_icsb_disable_counter(mp, XFS_SBS_ICOUNT); | xfs_icsb_disable_counter(mp, XFS_SBS_ICOUNT); |
| xfs_icsb_disable_counter(mp, XFS_SBS_IFREE); | xfs_icsb_disable_counter(mp, XFS_SBS_IFREE); |
| Line 1752 xfs_icsb_cpu_notify( | Line 1758 xfs_icsb_cpu_notify( |
| memset(cntp, 0, sizeof(xfs_icsb_cnts_t)); | memset(cntp, 0, sizeof(xfs_icsb_cnts_t)); |
| xfs_icsb_balance_counter(mp, XFS_SBS_ICOUNT, XFS_ICSB_SB_LOCKED); | xfs_icsb_balance_counter(mp, XFS_SBS_ICOUNT, |
| xfs_icsb_balance_counter(mp, XFS_SBS_IFREE, XFS_ICSB_SB_LOCKED); | XFS_ICSB_SB_LOCKED, 0); |
| xfs_icsb_balance_counter(mp, XFS_SBS_FDBLOCKS, XFS_ICSB_SB_LOCKED); | xfs_icsb_balance_counter(mp, XFS_SBS_IFREE, |
| XFS_ICSB_SB_LOCKED, 0); | |
| xfs_icsb_balance_counter(mp, XFS_SBS_FDBLOCKS, | |
| XFS_ICSB_SB_LOCKED, 0); | |
| XFS_SB_UNLOCK(mp, s); | XFS_SB_UNLOCK(mp, s); |
| mutex_unlock(&mp->m_icsb_mutex); | |
| break; | break; |
| } | } |
| Line 1784 xfs_icsb_init_counters( | Line 1794 xfs_icsb_init_counters( |
| cntp = (xfs_icsb_cnts_t *)per_cpu_ptr(mp->m_sb_cnts, i); | cntp = (xfs_icsb_cnts_t *)per_cpu_ptr(mp->m_sb_cnts, i); |
| memset(cntp, 0, sizeof(xfs_icsb_cnts_t)); | memset(cntp, 0, sizeof(xfs_icsb_cnts_t)); |
| } | } |
| mutex_init(&mp->m_icsb_mutex); | |
| /* | /* |
| * start with all counters disabled so that the | * start with all counters disabled so that the |
| * initial balance kicks us off correctly | * initial balance kicks us off correctly |
| Line 1888 xfs_icsb_disable_counter( | Line 1901 xfs_icsb_disable_counter( |
| ASSERT((field >= XFS_SBS_ICOUNT) && (field <= XFS_SBS_FDBLOCKS)); | ASSERT((field >= XFS_SBS_ICOUNT) && (field <= XFS_SBS_FDBLOCKS)); |
| /* | |
| * If we are already disabled, then there is nothing to do | |
| * here. We check before locking all the counters to avoid | |
| * the expensive lock operation when being called in the | |
| * slow path and the counter is already disabled. This is | |
| * safe because the only time we set or clear this state is under | |
| * the m_icsb_mutex. | |
| */ | |
| if (xfs_icsb_counter_disabled(mp, field)) | |
| return 0; | |
| xfs_icsb_lock_all_counters(mp); | xfs_icsb_lock_all_counters(mp); |
| if (!test_and_set_bit(field, &mp->m_icsb_counters)) { | if (!test_and_set_bit(field, &mp->m_icsb_counters)) { |
| /* drain back to superblock */ | /* drain back to superblock */ |
| Line 1997 xfs_icsb_sync_counters_lazy( | Line 2021 xfs_icsb_sync_counters_lazy( |
| /* | /* |
| * Balance and enable/disable counters as necessary. | * Balance and enable/disable counters as necessary. |
| * | * |
| * Thresholds for re-enabling counters are somewhat magic. | * Thresholds for re-enabling counters are somewhat magic. inode counts are |
| * inode counts are chosen to be the same number as single | * chosen to be the same number as single on disk allocation chunk per CPU, and |
| * on disk allocation chunk per CPU, and free blocks is | * free blocks is something far enough zero that we aren't going thrash when we |
| * something far enough zero that we aren't going thrash | * get near ENOSPC. We also need to supply a minimum we require per cpu to |
| * when we get near ENOSPC. | * prevent looping endlessly when xfs_alloc_space asks for more than will |
| * be distributed to a single CPU but each CPU has enough blocks to be | |
| * reenabled. | |
| * | |
| * Note that we can be called when counters are already disabled. | |
| * xfs_icsb_disable_counter() optimises the counter locking in this case to | |
| * prevent locking every per-cpu counter needlessly. | |
| */ | */ |
| #define XFS_ICSB_INO_CNTR_REENABLE 64 | |
| #define XFS_ICSB_INO_CNTR_REENABLE (uint64_t)64 | |
| #define XFS_ICSB_FDBLK_CNTR_REENABLE(mp) \ | #define XFS_ICSB_FDBLK_CNTR_REENABLE(mp) \ |
| (512 + XFS_ALLOC_SET_ASIDE(mp)) | (uint64_t)(512 + XFS_ALLOC_SET_ASIDE(mp)) |
| STATIC void | STATIC void |
| xfs_icsb_balance_counter( | xfs_icsb_balance_counter( |
| xfs_mount_t *mp, | xfs_mount_t *mp, |
| xfs_sb_field_t field, | xfs_sb_field_t field, |
| int flags) | int flags, |
| int min_per_cpu) | |
| { | { |
| uint64_t count, resid; | uint64_t count, resid; |
| int weight = num_online_cpus(); | int weight = num_online_cpus(); |
| int s; | int s; |
| uint64_t min = (uint64_t)min_per_cpu; | |
| if (!(flags & XFS_ICSB_SB_LOCKED)) | if (!(flags & XFS_ICSB_SB_LOCKED)) |
| s = XFS_SB_LOCK(mp); | s = XFS_SB_LOCK(mp); |
| Line 2027 xfs_icsb_balance_counter( | Line 2060 xfs_icsb_balance_counter( |
| case XFS_SBS_ICOUNT: | case XFS_SBS_ICOUNT: |
| count = mp->m_sb.sb_icount; | count = mp->m_sb.sb_icount; |
| resid = do_div(count, weight); | resid = do_div(count, weight); |
| if (count < XFS_ICSB_INO_CNTR_REENABLE) | if (count < max(min, XFS_ICSB_INO_CNTR_REENABLE)) |
| goto out; | goto out; |
| break; | break; |
| case XFS_SBS_IFREE: | case XFS_SBS_IFREE: |
| count = mp->m_sb.sb_ifree; | count = mp->m_sb.sb_ifree; |
| resid = do_div(count, weight); | resid = do_div(count, weight); |
| if (count < XFS_ICSB_INO_CNTR_REENABLE) | if (count < max(min, XFS_ICSB_INO_CNTR_REENABLE)) |
| goto out; | goto out; |
| break; | break; |
| case XFS_SBS_FDBLOCKS: | case XFS_SBS_FDBLOCKS: |
| count = mp->m_sb.sb_fdblocks; | count = mp->m_sb.sb_fdblocks; |
| resid = do_div(count, weight); | resid = do_div(count, weight); |
| if (count < XFS_ICSB_FDBLK_CNTR_REENABLE(mp)) | if (count < max(min, XFS_ICSB_FDBLK_CNTR_REENABLE(mp))) |
| goto out; | goto out; |
| break; | break; |
| default: | default: |
| Line 2054 out: | Line 2087 out: |
| XFS_SB_UNLOCK(mp, s); | XFS_SB_UNLOCK(mp, s); |
| } | } |
| STATIC int | int |
| xfs_icsb_modify_counters_int( | xfs_icsb_modify_counters( |
| xfs_mount_t *mp, | xfs_mount_t *mp, |
| xfs_sb_field_t field, | xfs_sb_field_t field, |
| int delta, | int delta, |
| int rsvd, | int rsvd) |
| int flags) | |
| { | { |
| xfs_icsb_cnts_t *icsbp; | xfs_icsb_cnts_t *icsbp; |
| long long lcounter; /* long counter for 64 bit fields */ | long long lcounter; /* long counter for 64 bit fields */ |
| int cpu, s, locked = 0; | int cpu, ret = 0, s; |
| int ret = 0, balance_done = 0; | |
| might_sleep(); | |
| again: | again: |
| cpu = get_cpu(); | cpu = get_cpu(); |
| icsbp = (xfs_icsb_cnts_t *)per_cpu_ptr(mp->m_sb_cnts, cpu), | icsbp = (xfs_icsb_cnts_t *)per_cpu_ptr(mp->m_sb_cnts, cpu); |
| xfs_icsb_lock_cntr(icsbp); | |
| /* | |
| * if the counter is disabled, go to slow path | |
| */ | |
| if (unlikely(xfs_icsb_counter_disabled(mp, field))) | if (unlikely(xfs_icsb_counter_disabled(mp, field))) |
| goto slow_path; | goto slow_path; |
| xfs_icsb_lock_cntr(icsbp); | |
| if (unlikely(xfs_icsb_counter_disabled(mp, field))) { | |
| xfs_icsb_unlock_cntr(icsbp); | |
| goto slow_path; | |
| } | |
| switch (field) { | switch (field) { |
| case XFS_SBS_ICOUNT: | case XFS_SBS_ICOUNT: |
| lcounter = icsbp->icsb_icount; | lcounter = icsbp->icsb_icount; |
| lcounter += delta; | lcounter += delta; |
| if (unlikely(lcounter < 0)) | if (unlikely(lcounter < 0)) |
| goto slow_path; | goto balance_counter; |
| icsbp->icsb_icount = lcounter; | icsbp->icsb_icount = lcounter; |
| break; | break; |
| Line 2087 again: | Line 2127 again: |
| lcounter = icsbp->icsb_ifree; | lcounter = icsbp->icsb_ifree; |
| lcounter += delta; | lcounter += delta; |
| if (unlikely(lcounter < 0)) | if (unlikely(lcounter < 0)) |
| goto slow_path; | goto balance_counter; |
| icsbp->icsb_ifree = lcounter; | icsbp->icsb_ifree = lcounter; |
| break; | break; |
| Line 2097 again: | Line 2137 again: |
| lcounter = icsbp->icsb_fdblocks - XFS_ALLOC_SET_ASIDE(mp); | lcounter = icsbp->icsb_fdblocks - XFS_ALLOC_SET_ASIDE(mp); |
| lcounter += delta; | lcounter += delta; |
| if (unlikely(lcounter < 0)) | if (unlikely(lcounter < 0)) |
| goto slow_path; | goto balance_counter; |
| icsbp->icsb_fdblocks = lcounter + XFS_ALLOC_SET_ASIDE(mp); | icsbp->icsb_fdblocks = lcounter + XFS_ALLOC_SET_ASIDE(mp); |
| break; | break; |
| default: | default: |
| Line 2106 again: | Line 2146 again: |
| } | } |
| xfs_icsb_unlock_cntr(icsbp); | xfs_icsb_unlock_cntr(icsbp); |
| put_cpu(); | put_cpu(); |
| if (locked) | |
| XFS_SB_UNLOCK(mp, s); | |
| return 0; | return 0; |
| /* | |
| * The slow path needs to be run with the SBLOCK | |
| * held so that we prevent other threads from | |
| * attempting to run this path at the same time. | |
| * this provides exclusion for the balancing code, | |
| * and exclusive fallback if the balance does not | |
| * provide enough resources to continue in an unlocked | |
| * manner. | |
| */ | |
| slow_path: | slow_path: |
| xfs_icsb_unlock_cntr(icsbp); | |
| put_cpu(); | put_cpu(); |
| /* need to hold superblock incase we need | /* |
| * to disable a counter */ | * serialise with a mutex so we don't burn lots of cpu on |
| if (!(flags & XFS_ICSB_SB_LOCKED)) { | * the superblock lock. We still need to hold the superblock |
| s = XFS_SB_LOCK(mp); | * lock, however, when we modify the global structures. |
| locked = 1; | */ |
| flags |= XFS_ICSB_SB_LOCKED; | mutex_lock(&mp->m_icsb_mutex); |
| } | |
| if (!balance_done) { | /* |
| xfs_icsb_balance_counter(mp, field, flags); | * Now running atomically. |
| balance_done = 1; | * |
| * If the counter is enabled, someone has beaten us to rebalancing. | |
| * Drop the lock and try again in the fast path.... | |
| */ | |
| if (!(xfs_icsb_counter_disabled(mp, field))) { | |
| mutex_unlock(&mp->m_icsb_mutex); | |
| goto again; | goto again; |
| } else { | |
| /* | |
| * we might not have enough on this local | |
| * cpu to allocate for a bulk request. | |
| * We need to drain this field from all CPUs | |
| * and disable the counter fastpath | |
| */ | |
| xfs_icsb_disable_counter(mp, field); | |
| } | } |
| /* | |
| * The counter is currently disabled. Because we are | |
| * running atomically here, we know a rebalance cannot | |
| * be in progress. Hence we can go straight to operating | |
| * on the global superblock. We do not call xfs_mod_incore_sb() | |
| * here even though we need to get the SB_LOCK. Doing so | |
| * will cause us to re-enter this function and deadlock. | |
| * Hence we get the SB_LOCK ourselves and then call | |
| * xfs_mod_incore_sb_unlocked() as the unlocked path operates | |
| * directly on the global counters. | |
| */ | |
| s = XFS_SB_LOCK(mp); | |
| ret = xfs_mod_incore_sb_unlocked(mp, field, delta, rsvd); | ret = xfs_mod_incore_sb_unlocked(mp, field, delta, rsvd); |
| XFS_SB_UNLOCK(mp, s); | |
| if (locked) | /* |
| XFS_SB_UNLOCK(mp, s); | * Now that we've modified the global superblock, we |
| * may be able to re-enable the distributed counters | |
| * (e.g. lots of space just got freed). After that | |
| * we are done. | |
| */ | |
| if (ret != ENOSPC) | |
| xfs_icsb_balance_counter(mp, field, 0, 0); | |
| mutex_unlock(&mp->m_icsb_mutex); | |
| return ret; | return ret; |
| } | |
| STATIC int | balance_counter: |
| xfs_icsb_modify_counters( | xfs_icsb_unlock_cntr(icsbp); |
| xfs_mount_t *mp, | put_cpu(); |
| xfs_sb_field_t field, | |
| int delta, | |
| int rsvd) | |
| { | |
| return xfs_icsb_modify_counters_int(mp, field, delta, rsvd, 0); | |
| } | |
| /* | /* |
| * Called when superblock is already locked | * We may have multiple threads here if multiple per-cpu |
| */ | * counters run dry at the same time. This will mean we can |
| STATIC int | * do more balances than strictly necessary but it is not |
| xfs_icsb_modify_counters_locked( | * the common slowpath case. |
| xfs_mount_t *mp, | */ |
| xfs_sb_field_t field, | mutex_lock(&mp->m_icsb_mutex); |
| int delta, | |
| int rsvd) | /* |
| { | * running atomically. |
| return xfs_icsb_modify_counters_int(mp, field, delta, | * |
| rsvd, XFS_ICSB_SB_LOCKED); | * This will leave the counter in the correct state for future |
| * accesses. After the rebalance, we simply try again and our retry | |
| * will either succeed through the fast path or slow path without | |
| * another balance operation being required. | |
| */ | |
| xfs_icsb_balance_counter(mp, field, 0, delta); | |
| mutex_unlock(&mp->m_icsb_mutex); | |
| goto again; | |
| } | } |
| #endif | #endif |