linux/fs/xfs/xfs_trans_refcount.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2016 Oracle.  All Rights Reserved.
   3 *
   4 * Author: Darrick J. Wong <darrick.wong@oracle.com>
   5 *
   6 * This program is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU General Public License
   8 * as published by the Free Software Foundation; either version 2
   9 * of the License, or (at your option) any later version.
  10 *
  11 * This program is distributed in the hope that it would be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU General Public License
  17 * along with this program; if not, write the Free Software Foundation,
  18 * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
  19 */
  20#include "xfs.h"
  21#include "xfs_fs.h"
  22#include "xfs_shared.h"
  23#include "xfs_format.h"
  24#include "xfs_log_format.h"
  25#include "xfs_trans_resv.h"
  26#include "xfs_mount.h"
  27#include "xfs_defer.h"
  28#include "xfs_trans.h"
  29#include "xfs_trans_priv.h"
  30#include "xfs_refcount_item.h"
  31#include "xfs_alloc.h"
  32#include "xfs_refcount.h"
  33
  34/*
  35 * This routine is called to allocate a "refcount update done"
  36 * log item.
  37 */
  38struct xfs_cud_log_item *
  39xfs_trans_get_cud(
  40        struct xfs_trans                *tp,
  41        struct xfs_cui_log_item         *cuip)
  42{
  43        struct xfs_cud_log_item         *cudp;
  44
  45        cudp = xfs_cud_init(tp->t_mountp, cuip);
  46        xfs_trans_add_item(tp, &cudp->cud_item);
  47        return cudp;
  48}
  49
  50/*
  51 * Finish an refcount update and log it to the CUD. Note that the
  52 * transaction is marked dirty regardless of whether the refcount
  53 * update succeeds or fails to support the CUI/CUD lifecycle rules.
  54 */
  55int
  56xfs_trans_log_finish_refcount_update(
  57        struct xfs_trans                *tp,
  58        struct xfs_cud_log_item         *cudp,
  59        struct xfs_defer_ops            *dop,
  60        enum xfs_refcount_intent_type   type,
  61        xfs_fsblock_t                   startblock,
  62        xfs_extlen_t                    blockcount,
  63        xfs_fsblock_t                   *new_fsb,
  64        xfs_extlen_t                    *new_len,
  65        struct xfs_btree_cur            **pcur)
  66{
  67        int                             error;
  68
  69        error = xfs_refcount_finish_one(tp, dop, type, startblock,
  70                        blockcount, new_fsb, new_len, pcur);
  71
  72        /*
  73         * Mark the transaction dirty, even on error. This ensures the
  74         * transaction is aborted, which:
  75         *
  76         * 1.) releases the CUI and frees the CUD
  77         * 2.) shuts down the filesystem
  78         */
  79        tp->t_flags |= XFS_TRANS_DIRTY;
  80        cudp->cud_item.li_desc->lid_flags |= XFS_LID_DIRTY;
  81
  82        return error;
  83}
  84
  85/* Sort refcount intents by AG. */
  86static int
  87xfs_refcount_update_diff_items(
  88        void                            *priv,
  89        struct list_head                *a,
  90        struct list_head                *b)
  91{
  92        struct xfs_mount                *mp = priv;
  93        struct xfs_refcount_intent      *ra;
  94        struct xfs_refcount_intent      *rb;
  95
  96        ra = container_of(a, struct xfs_refcount_intent, ri_list);
  97        rb = container_of(b, struct xfs_refcount_intent, ri_list);
  98        return  XFS_FSB_TO_AGNO(mp, ra->ri_startblock) -
  99                XFS_FSB_TO_AGNO(mp, rb->ri_startblock);
 100}
 101
 102/* Get an CUI. */
 103STATIC void *
 104xfs_refcount_update_create_intent(
 105        struct xfs_trans                *tp,
 106        unsigned int                    count)
 107{
 108        struct xfs_cui_log_item         *cuip;
 109
 110        ASSERT(tp != NULL);
 111        ASSERT(count > 0);
 112
 113        cuip = xfs_cui_init(tp->t_mountp, count);
 114        ASSERT(cuip != NULL);
 115
 116        /*
 117         * Get a log_item_desc to point at the new item.
 118         */
 119        xfs_trans_add_item(tp, &cuip->cui_item);
 120        return cuip;
 121}
 122
 123/* Set the phys extent flags for this reverse mapping. */
 124static void
 125xfs_trans_set_refcount_flags(
 126        struct xfs_phys_extent          *refc,
 127        enum xfs_refcount_intent_type   type)
 128{
 129        refc->pe_flags = 0;
 130        switch (type) {
 131        case XFS_REFCOUNT_INCREASE:
 132        case XFS_REFCOUNT_DECREASE:
 133        case XFS_REFCOUNT_ALLOC_COW:
 134        case XFS_REFCOUNT_FREE_COW:
 135                refc->pe_flags |= type;
 136                break;
 137        default:
 138                ASSERT(0);
 139        }
 140}
 141
 142/* Log refcount updates in the intent item. */
 143STATIC void
 144xfs_refcount_update_log_item(
 145        struct xfs_trans                *tp,
 146        void                            *intent,
 147        struct list_head                *item)
 148{
 149        struct xfs_cui_log_item         *cuip = intent;
 150        struct xfs_refcount_intent      *refc;
 151        uint                            next_extent;
 152        struct xfs_phys_extent          *ext;
 153
 154        refc = container_of(item, struct xfs_refcount_intent, ri_list);
 155
 156        tp->t_flags |= XFS_TRANS_DIRTY;
 157        cuip->cui_item.li_desc->lid_flags |= XFS_LID_DIRTY;
 158
 159        /*
 160         * atomic_inc_return gives us the value after the increment;
 161         * we want to use it as an array index so we need to subtract 1 from
 162         * it.
 163         */
 164        next_extent = atomic_inc_return(&cuip->cui_next_extent) - 1;
 165        ASSERT(next_extent < cuip->cui_format.cui_nextents);
 166        ext = &cuip->cui_format.cui_extents[next_extent];
 167        ext->pe_startblock = refc->ri_startblock;
 168        ext->pe_len = refc->ri_blockcount;
 169        xfs_trans_set_refcount_flags(ext, refc->ri_type);
 170}
 171
 172/* Get an CUD so we can process all the deferred refcount updates. */
 173STATIC void *
 174xfs_refcount_update_create_done(
 175        struct xfs_trans                *tp,
 176        void                            *intent,
 177        unsigned int                    count)
 178{
 179        return xfs_trans_get_cud(tp, intent);
 180}
 181
 182/* Process a deferred refcount update. */
 183STATIC int
 184xfs_refcount_update_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_refcount_intent      *refc;
 192        xfs_fsblock_t                   new_fsb;
 193        xfs_extlen_t                    new_aglen;
 194        int                             error;
 195
 196        refc = container_of(item, struct xfs_refcount_intent, ri_list);
 197        error = xfs_trans_log_finish_refcount_update(tp, done_item, dop,
 198                        refc->ri_type,
 199                        refc->ri_startblock,
 200                        refc->ri_blockcount,
 201                        &new_fsb, &new_aglen,
 202                        (struct xfs_btree_cur **)state);
 203        /* Did we run out of reservation?  Requeue what we didn't finish. */
 204        if (!error && new_aglen > 0) {
 205                ASSERT(refc->ri_type == XFS_REFCOUNT_INCREASE ||
 206                       refc->ri_type == XFS_REFCOUNT_DECREASE);
 207                refc->ri_startblock = new_fsb;
 208                refc->ri_blockcount = new_aglen;
 209                return -EAGAIN;
 210        }
 211        kmem_free(refc);
 212        return error;
 213}
 214
 215/* Clean up after processing deferred refcounts. */
 216STATIC void
 217xfs_refcount_update_finish_cleanup(
 218        struct xfs_trans        *tp,
 219        void                    *state,
 220        int                     error)
 221{
 222        struct xfs_btree_cur    *rcur = state;
 223
 224        xfs_refcount_finish_one_cleanup(tp, rcur, error);
 225}
 226
 227/* Abort all pending CUIs. */
 228STATIC void
 229xfs_refcount_update_abort_intent(
 230        void                            *intent)
 231{
 232        xfs_cui_release(intent);
 233}
 234
 235/* Cancel a deferred refcount update. */
 236STATIC void
 237xfs_refcount_update_cancel_item(
 238        struct list_head                *item)
 239{
 240        struct xfs_refcount_intent      *refc;
 241
 242        refc = container_of(item, struct xfs_refcount_intent, ri_list);
 243        kmem_free(refc);
 244}
 245
 246static const struct xfs_defer_op_type xfs_refcount_update_defer_type = {
 247        .type           = XFS_DEFER_OPS_TYPE_REFCOUNT,
 248        .max_items      = XFS_CUI_MAX_FAST_EXTENTS,
 249        .diff_items     = xfs_refcount_update_diff_items,
 250        .create_intent  = xfs_refcount_update_create_intent,
 251        .abort_intent   = xfs_refcount_update_abort_intent,
 252        .log_item       = xfs_refcount_update_log_item,
 253        .create_done    = xfs_refcount_update_create_done,
 254        .finish_item    = xfs_refcount_update_finish_item,
 255        .finish_cleanup = xfs_refcount_update_finish_cleanup,
 256        .cancel_item    = xfs_refcount_update_cancel_item,
 257};
 258
 259/* Register the deferred op type. */
 260void
 261xfs_refcount_update_init_defer_op(void)
 262{
 263        xfs_defer_init_op_type(&xfs_refcount_update_defer_type);
 264}
 265