linux/fs/xfs/xfs_trans_extfree.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2000,2005 Silicon Graphics, Inc.
   3 * All Rights Reserved.
   4 *
   5 * This program is free software; you can redistribute it and/or
   6 * modify it under the terms of the GNU General Public License as
   7 * published by the Free Software Foundation.
   8 *
   9 * This program is distributed in the hope that it would be useful,
  10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12 * GNU General Public License for more details.
  13 *
  14 * You should have received a copy of the GNU General Public License
  15 * along with this program; if not, write the Free Software Foundation,
  16 * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  17 */
  18#include "xfs.h"
  19#include "xfs_fs.h"
  20#include "xfs_shared.h"
  21#include "xfs_format.h"
  22#include "xfs_log_format.h"
  23#include "xfs_trans_resv.h"
  24#include "xfs_bit.h"
  25#include "xfs_mount.h"
  26#include "xfs_defer.h"
  27#include "xfs_trans.h"
  28#include "xfs_trans_priv.h"
  29#include "xfs_extfree_item.h"
  30#include "xfs_alloc.h"
  31#include "xfs_bmap.h"
  32#include "xfs_trace.h"
  33
  34/*
  35 * This routine is called to allocate an "extent free done"
  36 * log item that will hold nextents worth of extents.  The
  37 * caller must use all nextents extents, because we are not
  38 * flexible about this at all.
  39 */
  40struct xfs_efd_log_item *
  41xfs_trans_get_efd(struct xfs_trans              *tp,
  42                  struct xfs_efi_log_item       *efip,
  43                  uint                          nextents)
  44{
  45        struct xfs_efd_log_item                 *efdp;
  46
  47        ASSERT(tp != NULL);
  48        ASSERT(nextents > 0);
  49
  50        efdp = xfs_efd_init(tp->t_mountp, efip, nextents);
  51        ASSERT(efdp != NULL);
  52
  53        /*
  54         * Get a log_item_desc to point at the new item.
  55         */
  56        xfs_trans_add_item(tp, &efdp->efd_item);
  57        return efdp;
  58}
  59
  60/*
  61 * Free an extent and log it to the EFD. Note that the transaction is marked
  62 * dirty regardless of whether the extent free succeeds or fails to support the
  63 * EFI/EFD lifecycle rules.
  64 */
  65int
  66xfs_trans_free_extent(
  67        struct xfs_trans        *tp,
  68        struct xfs_efd_log_item *efdp,
  69        xfs_fsblock_t           start_block,
  70        xfs_extlen_t            ext_len,
  71        struct xfs_owner_info   *oinfo)
  72{
  73        struct xfs_mount        *mp = tp->t_mountp;
  74        uint                    next_extent;
  75        xfs_agnumber_t          agno = XFS_FSB_TO_AGNO(mp, start_block);
  76        xfs_agblock_t           agbno = XFS_FSB_TO_AGBNO(mp, start_block);
  77        struct xfs_extent       *extp;
  78        int                     error;
  79
  80        trace_xfs_bmap_free_deferred(tp->t_mountp, agno, 0, agbno, ext_len);
  81
  82        error = xfs_free_extent(tp, start_block, ext_len, oinfo,
  83                        XFS_AG_RESV_NONE);
  84
  85        /*
  86         * Mark the transaction dirty, even on error. This ensures the
  87         * transaction is aborted, which:
  88         *
  89         * 1.) releases the EFI and frees the EFD
  90         * 2.) shuts down the filesystem
  91         */
  92        tp->t_flags |= XFS_TRANS_DIRTY;
  93        efdp->efd_item.li_desc->lid_flags |= XFS_LID_DIRTY;
  94
  95        next_extent = efdp->efd_next_extent;
  96        ASSERT(next_extent < efdp->efd_format.efd_nextents);
  97        extp = &(efdp->efd_format.efd_extents[next_extent]);
  98        extp->ext_start = start_block;
  99        extp->ext_len = ext_len;
 100        efdp->efd_next_extent++;
 101
 102        return error;
 103}
 104
 105/* Sort bmap items by AG. */
 106static int
 107xfs_extent_free_diff_items(
 108        void                            *priv,
 109        struct list_head                *a,
 110        struct list_head                *b)
 111{
 112        struct xfs_mount                *mp = priv;
 113        struct xfs_extent_free_item     *ra;
 114        struct xfs_extent_free_item     *rb;
 115
 116        ra = container_of(a, struct xfs_extent_free_item, xefi_list);
 117        rb = container_of(b, struct xfs_extent_free_item, xefi_list);
 118        return  XFS_FSB_TO_AGNO(mp, ra->xefi_startblock) -
 119                XFS_FSB_TO_AGNO(mp, rb->xefi_startblock);
 120}
 121
 122/* Get an EFI. */
 123STATIC void *
 124xfs_extent_free_create_intent(
 125        struct xfs_trans                *tp,
 126        unsigned int                    count)
 127{
 128        struct xfs_efi_log_item         *efip;
 129
 130        ASSERT(tp != NULL);
 131        ASSERT(count > 0);
 132
 133        efip = xfs_efi_init(tp->t_mountp, count);
 134        ASSERT(efip != NULL);
 135
 136        /*
 137         * Get a log_item_desc to point at the new item.
 138         */
 139        xfs_trans_add_item(tp, &efip->efi_item);
 140        return efip;
 141}
 142
 143/* Log a free extent to the intent item. */
 144STATIC void
 145xfs_extent_free_log_item(
 146        struct xfs_trans                *tp,
 147        void                            *intent,
 148        struct list_head                *item)
 149{
 150        struct xfs_efi_log_item         *efip = intent;
 151        struct xfs_extent_free_item     *free;
 152        uint                            next_extent;
 153        struct xfs_extent               *extp;
 154
 155        free = container_of(item, struct xfs_extent_free_item, xefi_list);
 156
 157        tp->t_flags |= XFS_TRANS_DIRTY;
 158        efip->efi_item.li_desc->lid_flags |= XFS_LID_DIRTY;
 159
 160        /*
 161         * atomic_inc_return gives us the value after the increment;
 162         * we want to use it as an array index so we need to subtract 1 from
 163         * it.
 164         */
 165        next_extent = atomic_inc_return(&efip->efi_next_extent) - 1;
 166        ASSERT(next_extent < efip->efi_format.efi_nextents);
 167        extp = &efip->efi_format.efi_extents[next_extent];
 168        extp->ext_start = free->xefi_startblock;
 169        extp->ext_len = free->xefi_blockcount;
 170}
 171
 172/* Get an EFD so we can process all the free extents. */
 173STATIC void *
 174xfs_extent_free_create_done(
 175        struct xfs_trans                *tp,
 176        void                            *intent,
 177        unsigned int                    count)
 178{
 179        return xfs_trans_get_efd(tp, intent, count);
 180}
 181
 182/* Process a free extent. */
 183STATIC int
 184xfs_extent_free_finish_item(
 185        struct xfs_trans                *tp,
 186        struct xfs_defer_ops            *dop,
 187        struct list_head                *item,
 188        void                            *done_item,
 189        void                            **state)
 190{
 191        struct xfs_extent_free_item     *free;
 192        int                             error;
 193
 194        free = container_of(item, struct xfs_extent_free_item, xefi_list);
 195        error = xfs_trans_free_extent(tp, done_item,
 196                        free->xefi_startblock,
 197                        free->xefi_blockcount,
 198                        &free->xefi_oinfo);
 199        kmem_free(free);
 200        return error;
 201}
 202
 203/* Abort all pending EFIs. */
 204STATIC void
 205xfs_extent_free_abort_intent(
 206        void                            *intent)
 207{
 208        xfs_efi_release(intent);
 209}
 210
 211/* Cancel a free extent. */
 212STATIC void
 213xfs_extent_free_cancel_item(
 214        struct list_head                *item)
 215{
 216        struct xfs_extent_free_item     *free;
 217
 218        free = container_of(item, struct xfs_extent_free_item, xefi_list);
 219        kmem_free(free);
 220}
 221
 222static const struct xfs_defer_op_type xfs_extent_free_defer_type = {
 223        .type           = XFS_DEFER_OPS_TYPE_FREE,
 224        .max_items      = XFS_EFI_MAX_FAST_EXTENTS,
 225        .diff_items     = xfs_extent_free_diff_items,
 226        .create_intent  = xfs_extent_free_create_intent,
 227        .abort_intent   = xfs_extent_free_abort_intent,
 228        .log_item       = xfs_extent_free_log_item,
 229        .create_done    = xfs_extent_free_create_done,
 230        .finish_item    = xfs_extent_free_finish_item,
 231        .cancel_item    = xfs_extent_free_cancel_item,
 232};
 233
 234/* Register the deferred op type. */
 235void
 236xfs_extent_free_init_defer_op(void)
 237{
 238        xfs_defer_init_op_type(&xfs_extent_free_defer_type);
 239}
 240