LCOV - code coverage report
Current view: top level - drivers/md - dm-region-hash.c (source / functions) Hit Total Coverage
Test: combined.info Lines: 0 297 0.0 %
Date: 2022-04-01 14:17:54 Functions: 0 26 0.0 %
Branches: 0 128 0.0 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright (C) 2003 Sistina Software Limited.
       3                 :            :  * Copyright (C) 2004-2008 Red Hat, Inc. All rights reserved.
       4                 :            :  *
       5                 :            :  * This file is released under the GPL.
       6                 :            :  */
       7                 :            : 
       8                 :            : #include <linux/dm-dirty-log.h>
       9                 :            : #include <linux/dm-region-hash.h>
      10                 :            : 
      11                 :            : #include <linux/ctype.h>
      12                 :            : #include <linux/init.h>
      13                 :            : #include <linux/module.h>
      14                 :            : #include <linux/slab.h>
      15                 :            : #include <linux/vmalloc.h>
      16                 :            : 
      17                 :            : #include "dm.h"
      18                 :            : 
      19                 :            : #define DM_MSG_PREFIX   "region hash"
      20                 :            : 
      21                 :            : /*-----------------------------------------------------------------
      22                 :            :  * Region hash
      23                 :            :  *
      24                 :            :  * The mirror splits itself up into discrete regions.  Each
      25                 :            :  * region can be in one of three states: clean, dirty,
      26                 :            :  * nosync.  There is no need to put clean regions in the hash.
      27                 :            :  *
      28                 :            :  * In addition to being present in the hash table a region _may_
      29                 :            :  * be present on one of three lists.
      30                 :            :  *
      31                 :            :  *   clean_regions: Regions on this list have no io pending to
      32                 :            :  *   them, they are in sync, we are no longer interested in them,
      33                 :            :  *   they are dull.  dm_rh_update_states() will remove them from the
      34                 :            :  *   hash table.
      35                 :            :  *
      36                 :            :  *   quiesced_regions: These regions have been spun down, ready
      37                 :            :  *   for recovery.  rh_recovery_start() will remove regions from
      38                 :            :  *   this list and hand them to kmirrord, which will schedule the
      39                 :            :  *   recovery io with kcopyd.
      40                 :            :  *
      41                 :            :  *   recovered_regions: Regions that kcopyd has successfully
      42                 :            :  *   recovered.  dm_rh_update_states() will now schedule any delayed
      43                 :            :  *   io, up the recovery_count, and remove the region from the
      44                 :            :  *   hash.
      45                 :            :  *
      46                 :            :  * There are 2 locks:
      47                 :            :  *   A rw spin lock 'hash_lock' protects just the hash table,
      48                 :            :  *   this is never held in write mode from interrupt context,
      49                 :            :  *   which I believe means that we only have to disable irqs when
      50                 :            :  *   doing a write lock.
      51                 :            :  *
      52                 :            :  *   An ordinary spin lock 'region_lock' that protects the three
      53                 :            :  *   lists in the region_hash, with the 'state', 'list' and
      54                 :            :  *   'delayed_bios' fields of the regions.  This is used from irq
      55                 :            :  *   context, so all other uses will have to suspend local irqs.
      56                 :            :  *---------------------------------------------------------------*/
      57                 :            : struct dm_region_hash {
      58                 :            :         uint32_t region_size;
      59                 :            :         unsigned region_shift;
      60                 :            : 
      61                 :            :         /* holds persistent region state */
      62                 :            :         struct dm_dirty_log *log;
      63                 :            : 
      64                 :            :         /* hash table */
      65                 :            :         rwlock_t hash_lock;
      66                 :            :         unsigned mask;
      67                 :            :         unsigned nr_buckets;
      68                 :            :         unsigned prime;
      69                 :            :         unsigned shift;
      70                 :            :         struct list_head *buckets;
      71                 :            : 
      72                 :            :         /*
      73                 :            :          * If there was a flush failure no regions can be marked clean.
      74                 :            :          */
      75                 :            :         int flush_failure;
      76                 :            : 
      77                 :            :         unsigned max_recovery; /* Max # of regions to recover in parallel */
      78                 :            : 
      79                 :            :         spinlock_t region_lock;
      80                 :            :         atomic_t recovery_in_flight;
      81                 :            :         struct list_head clean_regions;
      82                 :            :         struct list_head quiesced_regions;
      83                 :            :         struct list_head recovered_regions;
      84                 :            :         struct list_head failed_recovered_regions;
      85                 :            :         struct semaphore recovery_count;
      86                 :            : 
      87                 :            :         mempool_t region_pool;
      88                 :            : 
      89                 :            :         void *context;
      90                 :            :         sector_t target_begin;
      91                 :            : 
      92                 :            :         /* Callback function to schedule bios writes */
      93                 :            :         void (*dispatch_bios)(void *context, struct bio_list *bios);
      94                 :            : 
      95                 :            :         /* Callback function to wakeup callers worker thread. */
      96                 :            :         void (*wakeup_workers)(void *context);
      97                 :            : 
      98                 :            :         /* Callback function to wakeup callers recovery waiters. */
      99                 :            :         void (*wakeup_all_recovery_waiters)(void *context);
     100                 :            : };
     101                 :            : 
     102                 :            : struct dm_region {
     103                 :            :         struct dm_region_hash *rh;      /* FIXME: can we get rid of this ? */
     104                 :            :         region_t key;
     105                 :            :         int state;
     106                 :            : 
     107                 :            :         struct list_head hash_list;
     108                 :            :         struct list_head list;
     109                 :            : 
     110                 :            :         atomic_t pending;
     111                 :            :         struct bio_list delayed_bios;
     112                 :            : };
     113                 :            : 
     114                 :            : /*
     115                 :            :  * Conversion fns
     116                 :            :  */
     117                 :          0 : static region_t dm_rh_sector_to_region(struct dm_region_hash *rh, sector_t sector)
     118                 :            : {
     119                 :          0 :         return sector >> rh->region_shift;
     120                 :            : }
     121                 :            : 
     122                 :          0 : sector_t dm_rh_region_to_sector(struct dm_region_hash *rh, region_t region)
     123                 :            : {
     124                 :          0 :         return region << rh->region_shift;
     125                 :            : }
     126                 :            : EXPORT_SYMBOL_GPL(dm_rh_region_to_sector);
     127                 :            : 
     128                 :          0 : region_t dm_rh_bio_to_region(struct dm_region_hash *rh, struct bio *bio)
     129                 :            : {
     130                 :          0 :         return dm_rh_sector_to_region(rh, bio->bi_iter.bi_sector -
     131                 :          0 :                                       rh->target_begin);
     132                 :            : }
     133                 :            : EXPORT_SYMBOL_GPL(dm_rh_bio_to_region);
     134                 :            : 
     135                 :          0 : void *dm_rh_region_context(struct dm_region *reg)
     136                 :            : {
     137                 :          0 :         return reg->rh->context;
     138                 :            : }
     139                 :            : EXPORT_SYMBOL_GPL(dm_rh_region_context);
     140                 :            : 
     141                 :          0 : region_t dm_rh_get_region_key(struct dm_region *reg)
     142                 :            : {
     143                 :          0 :         return reg->key;
     144                 :            : }
     145                 :            : EXPORT_SYMBOL_GPL(dm_rh_get_region_key);
     146                 :            : 
     147                 :          0 : sector_t dm_rh_get_region_size(struct dm_region_hash *rh)
     148                 :            : {
     149                 :          0 :         return rh->region_size;
     150                 :            : }
     151                 :            : EXPORT_SYMBOL_GPL(dm_rh_get_region_size);
     152                 :            : 
     153                 :            : /*
     154                 :            :  * FIXME: shall we pass in a structure instead of all these args to
     155                 :            :  * dm_region_hash_create()????
     156                 :            :  */
     157                 :            : #define RH_HASH_MULT 2654435387U
     158                 :            : #define RH_HASH_SHIFT 12
     159                 :            : 
     160                 :            : #define MIN_REGIONS 64
     161                 :          0 : struct dm_region_hash *dm_region_hash_create(
     162                 :            :                 void *context, void (*dispatch_bios)(void *context,
     163                 :            :                                                      struct bio_list *bios),
     164                 :            :                 void (*wakeup_workers)(void *context),
     165                 :            :                 void (*wakeup_all_recovery_waiters)(void *context),
     166                 :            :                 sector_t target_begin, unsigned max_recovery,
     167                 :            :                 struct dm_dirty_log *log, uint32_t region_size,
     168                 :            :                 region_t nr_regions)
     169                 :            : {
     170                 :          0 :         struct dm_region_hash *rh;
     171                 :          0 :         unsigned nr_buckets, max_buckets;
     172                 :          0 :         size_t i;
     173                 :          0 :         int ret;
     174                 :            : 
     175                 :            :         /*
     176                 :            :          * Calculate a suitable number of buckets for our hash
     177                 :            :          * table.
     178                 :            :          */
     179                 :          0 :         max_buckets = nr_regions >> 6;
     180         [ #  # ]:          0 :         for (nr_buckets = 128u; nr_buckets < max_buckets; nr_buckets <<= 1)
     181                 :          0 :                 ;
     182                 :          0 :         nr_buckets >>= 1;
     183                 :            : 
     184                 :          0 :         rh = kzalloc(sizeof(*rh), GFP_KERNEL);
     185         [ #  # ]:          0 :         if (!rh) {
     186                 :          0 :                 DMERR("unable to allocate region hash memory");
     187                 :          0 :                 return ERR_PTR(-ENOMEM);
     188                 :            :         }
     189                 :            : 
     190                 :          0 :         rh->context = context;
     191                 :          0 :         rh->dispatch_bios = dispatch_bios;
     192                 :          0 :         rh->wakeup_workers = wakeup_workers;
     193                 :          0 :         rh->wakeup_all_recovery_waiters = wakeup_all_recovery_waiters;
     194                 :          0 :         rh->target_begin = target_begin;
     195                 :          0 :         rh->max_recovery = max_recovery;
     196                 :          0 :         rh->log = log;
     197                 :          0 :         rh->region_size = region_size;
     198                 :          0 :         rh->region_shift = __ffs(region_size);
     199                 :          0 :         rwlock_init(&rh->hash_lock);
     200                 :          0 :         rh->mask = nr_buckets - 1;
     201                 :          0 :         rh->nr_buckets = nr_buckets;
     202                 :            : 
     203                 :          0 :         rh->shift = RH_HASH_SHIFT;
     204                 :          0 :         rh->prime = RH_HASH_MULT;
     205                 :            : 
     206                 :          0 :         rh->buckets = vmalloc(array_size(nr_buckets, sizeof(*rh->buckets)));
     207         [ #  # ]:          0 :         if (!rh->buckets) {
     208                 :          0 :                 DMERR("unable to allocate region hash bucket memory");
     209                 :          0 :                 kfree(rh);
     210                 :          0 :                 return ERR_PTR(-ENOMEM);
     211                 :            :         }
     212                 :            : 
     213         [ #  # ]:          0 :         for (i = 0; i < nr_buckets; i++)
     214                 :          0 :                 INIT_LIST_HEAD(rh->buckets + i);
     215                 :            : 
     216                 :          0 :         spin_lock_init(&rh->region_lock);
     217                 :          0 :         sema_init(&rh->recovery_count, 0);
     218                 :          0 :         atomic_set(&rh->recovery_in_flight, 0);
     219                 :          0 :         INIT_LIST_HEAD(&rh->clean_regions);
     220                 :          0 :         INIT_LIST_HEAD(&rh->quiesced_regions);
     221                 :          0 :         INIT_LIST_HEAD(&rh->recovered_regions);
     222                 :          0 :         INIT_LIST_HEAD(&rh->failed_recovered_regions);
     223                 :          0 :         rh->flush_failure = 0;
     224                 :            : 
     225                 :          0 :         ret = mempool_init_kmalloc_pool(&rh->region_pool, MIN_REGIONS,
     226                 :            :                                         sizeof(struct dm_region));
     227         [ #  # ]:          0 :         if (ret) {
     228                 :          0 :                 vfree(rh->buckets);
     229                 :          0 :                 kfree(rh);
     230                 :          0 :                 rh = ERR_PTR(-ENOMEM);
     231                 :            :         }
     232                 :            : 
     233                 :            :         return rh;
     234                 :            : }
     235                 :            : EXPORT_SYMBOL_GPL(dm_region_hash_create);
     236                 :            : 
     237                 :          0 : void dm_region_hash_destroy(struct dm_region_hash *rh)
     238                 :            : {
     239                 :          0 :         unsigned h;
     240                 :          0 :         struct dm_region *reg, *nreg;
     241                 :            : 
     242         [ #  # ]:          0 :         BUG_ON(!list_empty(&rh->quiesced_regions));
     243         [ #  # ]:          0 :         for (h = 0; h < rh->nr_buckets; h++) {
     244         [ #  # ]:          0 :                 list_for_each_entry_safe(reg, nreg, rh->buckets + h,
     245                 :            :                                          hash_list) {
     246         [ #  # ]:          0 :                         BUG_ON(atomic_read(&reg->pending));
     247                 :          0 :                         mempool_free(reg, &rh->region_pool);
     248                 :            :                 }
     249                 :            :         }
     250                 :            : 
     251         [ #  # ]:          0 :         if (rh->log)
     252                 :          0 :                 dm_dirty_log_destroy(rh->log);
     253                 :            : 
     254                 :          0 :         mempool_exit(&rh->region_pool);
     255                 :          0 :         vfree(rh->buckets);
     256                 :          0 :         kfree(rh);
     257                 :          0 : }
     258                 :            : EXPORT_SYMBOL_GPL(dm_region_hash_destroy);
     259                 :            : 
     260                 :          0 : struct dm_dirty_log *dm_rh_dirty_log(struct dm_region_hash *rh)
     261                 :            : {
     262                 :          0 :         return rh->log;
     263                 :            : }
     264                 :            : EXPORT_SYMBOL_GPL(dm_rh_dirty_log);
     265                 :            : 
     266                 :          0 : static unsigned rh_hash(struct dm_region_hash *rh, region_t region)
     267                 :            : {
     268                 :          0 :         return (unsigned) ((region * rh->prime) >> rh->shift) & rh->mask;
     269                 :            : }
     270                 :            : 
     271                 :          0 : static struct dm_region *__rh_lookup(struct dm_region_hash *rh, region_t region)
     272                 :            : {
     273                 :          0 :         struct dm_region *reg;
     274                 :          0 :         struct list_head *bucket = rh->buckets + rh_hash(rh, region);
     275                 :            : 
     276   [ #  #  #  #  :          0 :         list_for_each_entry(reg, bucket, hash_list)
             #  #  #  # ]
     277   [ #  #  #  #  :          0 :                 if (reg->key == region)
             #  #  #  # ]
     278                 :            :                         return reg;
     279                 :            : 
     280                 :            :         return NULL;
     281                 :            : }
     282                 :            : 
     283                 :          0 : static void __rh_insert(struct dm_region_hash *rh, struct dm_region *reg)
     284                 :            : {
     285                 :          0 :         list_add(&reg->hash_list, rh->buckets + rh_hash(rh, reg->key));
     286                 :            : }
     287                 :            : 
     288                 :          0 : static struct dm_region *__rh_alloc(struct dm_region_hash *rh, region_t region)
     289                 :            : {
     290                 :          0 :         struct dm_region *reg, *nreg;
     291                 :            : 
     292                 :          0 :         nreg = mempool_alloc(&rh->region_pool, GFP_ATOMIC);
     293         [ #  # ]:          0 :         if (unlikely(!nreg))
     294                 :          0 :                 nreg = kmalloc(sizeof(*nreg), GFP_NOIO | __GFP_NOFAIL);
     295                 :            : 
     296                 :          0 :         nreg->state = rh->log->type->in_sync(rh->log, region, 1) ?
     297         [ #  # ]:          0 :                       DM_RH_CLEAN : DM_RH_NOSYNC;
     298                 :          0 :         nreg->rh = rh;
     299                 :          0 :         nreg->key = region;
     300                 :          0 :         INIT_LIST_HEAD(&nreg->list);
     301                 :          0 :         atomic_set(&nreg->pending, 0);
     302                 :          0 :         bio_list_init(&nreg->delayed_bios);
     303                 :            : 
     304                 :          0 :         write_lock_irq(&rh->hash_lock);
     305                 :          0 :         reg = __rh_lookup(rh, region);
     306         [ #  # ]:          0 :         if (reg)
     307                 :            :                 /* We lost the race. */
     308                 :          0 :                 mempool_free(nreg, &rh->region_pool);
     309                 :            :         else {
     310         [ #  # ]:          0 :                 __rh_insert(rh, nreg);
     311         [ #  # ]:          0 :                 if (nreg->state == DM_RH_CLEAN) {
     312                 :          0 :                         spin_lock(&rh->region_lock);
     313                 :          0 :                         list_add(&nreg->list, &rh->clean_regions);
     314                 :          0 :                         spin_unlock(&rh->region_lock);
     315                 :            :                 }
     316                 :            : 
     317                 :            :                 reg = nreg;
     318                 :            :         }
     319                 :          0 :         write_unlock_irq(&rh->hash_lock);
     320                 :            : 
     321                 :          0 :         return reg;
     322                 :            : }
     323                 :            : 
     324                 :          0 : static struct dm_region *__rh_find(struct dm_region_hash *rh, region_t region)
     325                 :            : {
     326                 :          0 :         struct dm_region *reg;
     327                 :            : 
     328                 :          0 :         reg = __rh_lookup(rh, region);
     329         [ #  # ]:          0 :         if (!reg) {
     330                 :          0 :                 read_unlock(&rh->hash_lock);
     331                 :          0 :                 reg = __rh_alloc(rh, region);
     332                 :          0 :                 read_lock(&rh->hash_lock);
     333                 :            :         }
     334                 :            : 
     335                 :          0 :         return reg;
     336                 :            : }
     337                 :            : 
     338                 :          0 : int dm_rh_get_state(struct dm_region_hash *rh, region_t region, int may_block)
     339                 :            : {
     340                 :          0 :         int r;
     341                 :          0 :         struct dm_region *reg;
     342                 :            : 
     343                 :          0 :         read_lock(&rh->hash_lock);
     344                 :          0 :         reg = __rh_lookup(rh, region);
     345                 :          0 :         read_unlock(&rh->hash_lock);
     346                 :            : 
     347         [ #  # ]:          0 :         if (reg)
     348                 :          0 :                 return reg->state;
     349                 :            : 
     350                 :            :         /*
     351                 :            :          * The region wasn't in the hash, so we fall back to the
     352                 :            :          * dirty log.
     353                 :            :          */
     354                 :          0 :         r = rh->log->type->in_sync(rh->log, region, may_block);
     355                 :            : 
     356                 :            :         /*
     357                 :            :          * Any error from the dirty log (eg. -EWOULDBLOCK) gets
     358                 :            :          * taken as a DM_RH_NOSYNC
     359                 :            :          */
     360         [ #  # ]:          0 :         return r == 1 ? DM_RH_CLEAN : DM_RH_NOSYNC;
     361                 :            : }
     362                 :            : EXPORT_SYMBOL_GPL(dm_rh_get_state);
     363                 :            : 
     364                 :          0 : static void complete_resync_work(struct dm_region *reg, int success)
     365                 :            : {
     366                 :          0 :         struct dm_region_hash *rh = reg->rh;
     367                 :            : 
     368                 :          0 :         rh->log->type->set_region_sync(rh->log, reg->key, success);
     369                 :            : 
     370                 :            :         /*
     371                 :            :          * Dispatch the bios before we call 'wake_up_all'.
     372                 :            :          * This is important because if we are suspending,
     373                 :            :          * we want to know that recovery is complete and
     374                 :            :          * the work queue is flushed.  If we wake_up_all
     375                 :            :          * before we dispatch_bios (queue bios and call wake()),
     376                 :            :          * then we risk suspending before the work queue
     377                 :            :          * has been properly flushed.
     378                 :            :          */
     379                 :          0 :         rh->dispatch_bios(rh->context, &reg->delayed_bios);
     380         [ #  # ]:          0 :         if (atomic_dec_and_test(&rh->recovery_in_flight))
     381                 :          0 :                 rh->wakeup_all_recovery_waiters(rh->context);
     382                 :          0 :         up(&rh->recovery_count);
     383                 :          0 : }
     384                 :            : 
     385                 :            : /* dm_rh_mark_nosync
     386                 :            :  * @ms
     387                 :            :  * @bio
     388                 :            :  *
     389                 :            :  * The bio was written on some mirror(s) but failed on other mirror(s).
     390                 :            :  * We can successfully endio the bio but should avoid the region being
     391                 :            :  * marked clean by setting the state DM_RH_NOSYNC.
     392                 :            :  *
     393                 :            :  * This function is _not_ safe in interrupt context!
     394                 :            :  */
     395                 :          0 : void dm_rh_mark_nosync(struct dm_region_hash *rh, struct bio *bio)
     396                 :            : {
     397                 :          0 :         unsigned long flags;
     398                 :          0 :         struct dm_dirty_log *log = rh->log;
     399                 :          0 :         struct dm_region *reg;
     400                 :          0 :         region_t region = dm_rh_bio_to_region(rh, bio);
     401                 :          0 :         int recovering = 0;
     402                 :            : 
     403         [ #  # ]:          0 :         if (bio->bi_opf & REQ_PREFLUSH) {
     404                 :          0 :                 rh->flush_failure = 1;
     405                 :          0 :                 return;
     406                 :            :         }
     407                 :            : 
     408         [ #  # ]:          0 :         if (bio_op(bio) == REQ_OP_DISCARD)
     409                 :            :                 return;
     410                 :            : 
     411                 :            :         /* We must inform the log that the sync count has changed. */
     412                 :          0 :         log->type->set_region_sync(log, region, 0);
     413                 :            : 
     414                 :          0 :         read_lock(&rh->hash_lock);
     415                 :          0 :         reg = __rh_find(rh, region);
     416                 :          0 :         read_unlock(&rh->hash_lock);
     417                 :            : 
     418                 :            :         /* region hash entry should exist because write was in-flight */
     419         [ #  # ]:          0 :         BUG_ON(!reg);
     420         [ #  # ]:          0 :         BUG_ON(!list_empty(&reg->list));
     421                 :            : 
     422                 :          0 :         spin_lock_irqsave(&rh->region_lock, flags);
     423                 :            :         /*
     424                 :            :          * Possible cases:
     425                 :            :          *   1) DM_RH_DIRTY
     426                 :            :          *   2) DM_RH_NOSYNC: was dirty, other preceding writes failed
     427                 :            :          *   3) DM_RH_RECOVERING: flushing pending writes
     428                 :            :          * Either case, the region should have not been connected to list.
     429                 :            :          */
     430                 :          0 :         recovering = (reg->state == DM_RH_RECOVERING);
     431                 :          0 :         reg->state = DM_RH_NOSYNC;
     432         [ #  # ]:          0 :         BUG_ON(!list_empty(&reg->list));
     433                 :          0 :         spin_unlock_irqrestore(&rh->region_lock, flags);
     434                 :            : 
     435         [ #  # ]:          0 :         if (recovering)
     436                 :          0 :                 complete_resync_work(reg, 0);
     437                 :            : }
     438                 :            : EXPORT_SYMBOL_GPL(dm_rh_mark_nosync);
     439                 :            : 
     440                 :          0 : void dm_rh_update_states(struct dm_region_hash *rh, int errors_handled)
     441                 :            : {
     442                 :          0 :         struct dm_region *reg, *next;
     443                 :            : 
     444                 :          0 :         LIST_HEAD(clean);
     445                 :          0 :         LIST_HEAD(recovered);
     446                 :          0 :         LIST_HEAD(failed_recovered);
     447                 :            : 
     448                 :            :         /*
     449                 :            :          * Quickly grab the lists.
     450                 :            :          */
     451                 :          0 :         write_lock_irq(&rh->hash_lock);
     452                 :          0 :         spin_lock(&rh->region_lock);
     453         [ #  # ]:          0 :         if (!list_empty(&rh->clean_regions)) {
     454         [ #  # ]:          0 :                 list_splice_init(&rh->clean_regions, &clean);
     455                 :            : 
     456         [ #  # ]:          0 :                 list_for_each_entry(reg, &clean, list)
     457                 :          0 :                         list_del(&reg->hash_list);
     458                 :            :         }
     459                 :            : 
     460         [ #  # ]:          0 :         if (!list_empty(&rh->recovered_regions)) {
     461         [ #  # ]:          0 :                 list_splice_init(&rh->recovered_regions, &recovered);
     462                 :            : 
     463         [ #  # ]:          0 :                 list_for_each_entry(reg, &recovered, list)
     464                 :          0 :                         list_del(&reg->hash_list);
     465                 :            :         }
     466                 :            : 
     467         [ #  # ]:          0 :         if (!list_empty(&rh->failed_recovered_regions)) {
     468         [ #  # ]:          0 :                 list_splice_init(&rh->failed_recovered_regions,
     469                 :            :                                  &failed_recovered);
     470                 :            : 
     471         [ #  # ]:          0 :                 list_for_each_entry(reg, &failed_recovered, list)
     472                 :          0 :                         list_del(&reg->hash_list);
     473                 :            :         }
     474                 :            : 
     475                 :          0 :         spin_unlock(&rh->region_lock);
     476                 :          0 :         write_unlock_irq(&rh->hash_lock);
     477                 :            : 
     478                 :            :         /*
     479                 :            :          * All the regions on the recovered and clean lists have
     480                 :            :          * now been pulled out of the system, so no need to do
     481                 :            :          * any more locking.
     482                 :            :          */
     483         [ #  # ]:          0 :         list_for_each_entry_safe(reg, next, &recovered, list) {
     484                 :          0 :                 rh->log->type->clear_region(rh->log, reg->key);
     485                 :          0 :                 complete_resync_work(reg, 1);
     486                 :          0 :                 mempool_free(reg, &rh->region_pool);
     487                 :            :         }
     488                 :            : 
     489         [ #  # ]:          0 :         list_for_each_entry_safe(reg, next, &failed_recovered, list) {
     490                 :          0 :                 complete_resync_work(reg, errors_handled ? 0 : 1);
     491                 :          0 :                 mempool_free(reg, &rh->region_pool);
     492                 :            :         }
     493                 :            : 
     494         [ #  # ]:          0 :         list_for_each_entry_safe(reg, next, &clean, list) {
     495                 :          0 :                 rh->log->type->clear_region(rh->log, reg->key);
     496                 :          0 :                 mempool_free(reg, &rh->region_pool);
     497                 :            :         }
     498                 :            : 
     499                 :          0 :         rh->log->type->flush(rh->log);
     500                 :          0 : }
     501                 :            : EXPORT_SYMBOL_GPL(dm_rh_update_states);
     502                 :            : 
     503                 :          0 : static void rh_inc(struct dm_region_hash *rh, region_t region)
     504                 :            : {
     505                 :          0 :         struct dm_region *reg;
     506                 :            : 
     507                 :          0 :         read_lock(&rh->hash_lock);
     508                 :          0 :         reg = __rh_find(rh, region);
     509                 :            : 
     510                 :          0 :         spin_lock_irq(&rh->region_lock);
     511                 :          0 :         atomic_inc(&reg->pending);
     512                 :            : 
     513         [ #  # ]:          0 :         if (reg->state == DM_RH_CLEAN) {
     514                 :          0 :                 reg->state = DM_RH_DIRTY;
     515                 :          0 :                 list_del_init(&reg->list);       /* take off the clean list */
     516                 :          0 :                 spin_unlock_irq(&rh->region_lock);
     517                 :            : 
     518                 :          0 :                 rh->log->type->mark_region(rh->log, reg->key);
     519                 :            :         } else
     520                 :          0 :                 spin_unlock_irq(&rh->region_lock);
     521                 :            : 
     522                 :            : 
     523                 :          0 :         read_unlock(&rh->hash_lock);
     524                 :          0 : }
     525                 :            : 
     526                 :          0 : void dm_rh_inc_pending(struct dm_region_hash *rh, struct bio_list *bios)
     527                 :            : {
     528                 :          0 :         struct bio *bio;
     529                 :            : 
     530         [ #  # ]:          0 :         for (bio = bios->head; bio; bio = bio->bi_next) {
     531   [ #  #  #  # ]:          0 :                 if (bio->bi_opf & REQ_PREFLUSH || bio_op(bio) == REQ_OP_DISCARD)
     532                 :          0 :                         continue;
     533                 :          0 :                 rh_inc(rh, dm_rh_bio_to_region(rh, bio));
     534                 :            :         }
     535                 :          0 : }
     536                 :            : EXPORT_SYMBOL_GPL(dm_rh_inc_pending);
     537                 :            : 
     538                 :          0 : void dm_rh_dec(struct dm_region_hash *rh, region_t region)
     539                 :            : {
     540                 :          0 :         unsigned long flags;
     541                 :          0 :         struct dm_region *reg;
     542                 :          0 :         int should_wake = 0;
     543                 :            : 
     544                 :          0 :         read_lock(&rh->hash_lock);
     545                 :          0 :         reg = __rh_lookup(rh, region);
     546                 :          0 :         read_unlock(&rh->hash_lock);
     547                 :            : 
     548                 :          0 :         spin_lock_irqsave(&rh->region_lock, flags);
     549         [ #  # ]:          0 :         if (atomic_dec_and_test(&reg->pending)) {
     550                 :            :                 /*
     551                 :            :                  * There is no pending I/O for this region.
     552                 :            :                  * We can move the region to corresponding list for next action.
     553                 :            :                  * At this point, the region is not yet connected to any list.
     554                 :            :                  *
     555                 :            :                  * If the state is DM_RH_NOSYNC, the region should be kept off
     556                 :            :                  * from clean list.
     557                 :            :                  * The hash entry for DM_RH_NOSYNC will remain in memory
     558                 :            :                  * until the region is recovered or the map is reloaded.
     559                 :            :                  */
     560                 :            : 
     561                 :            :                 /* do nothing for DM_RH_NOSYNC */
     562         [ #  # ]:          0 :                 if (unlikely(rh->flush_failure)) {
     563                 :            :                         /*
     564                 :            :                          * If a write flush failed some time ago, we
     565                 :            :                          * don't know whether or not this write made it
     566                 :            :                          * to the disk, so we must resync the device.
     567                 :            :                          */
     568                 :          0 :                         reg->state = DM_RH_NOSYNC;
     569         [ #  # ]:          0 :                 } else if (reg->state == DM_RH_RECOVERING) {
     570                 :          0 :                         list_add_tail(&reg->list, &rh->quiesced_regions);
     571         [ #  # ]:          0 :                 } else if (reg->state == DM_RH_DIRTY) {
     572                 :          0 :                         reg->state = DM_RH_CLEAN;
     573                 :          0 :                         list_add(&reg->list, &rh->clean_regions);
     574                 :            :                 }
     575                 :            :                 should_wake = 1;
     576                 :            :         }
     577                 :          0 :         spin_unlock_irqrestore(&rh->region_lock, flags);
     578                 :            : 
     579         [ #  # ]:          0 :         if (should_wake)
     580                 :          0 :                 rh->wakeup_workers(rh->context);
     581                 :          0 : }
     582                 :            : EXPORT_SYMBOL_GPL(dm_rh_dec);
     583                 :            : 
     584                 :            : /*
     585                 :            :  * Starts quiescing a region in preparation for recovery.
     586                 :            :  */
     587                 :          0 : static int __rh_recovery_prepare(struct dm_region_hash *rh)
     588                 :            : {
     589                 :          0 :         int r;
     590                 :          0 :         region_t region;
     591                 :          0 :         struct dm_region *reg;
     592                 :            : 
     593                 :            :         /*
     594                 :            :          * Ask the dirty log what's next.
     595                 :            :          */
     596                 :          0 :         r = rh->log->type->get_resync_work(rh->log, &region);
     597         [ #  # ]:          0 :         if (r <= 0)
     598                 :            :                 return r;
     599                 :            : 
     600                 :            :         /*
     601                 :            :          * Get this region, and start it quiescing by setting the
     602                 :            :          * recovering flag.
     603                 :            :          */
     604                 :          0 :         read_lock(&rh->hash_lock);
     605                 :          0 :         reg = __rh_find(rh, region);
     606                 :          0 :         read_unlock(&rh->hash_lock);
     607                 :            : 
     608                 :          0 :         spin_lock_irq(&rh->region_lock);
     609                 :          0 :         reg->state = DM_RH_RECOVERING;
     610                 :            : 
     611                 :            :         /* Already quiesced ? */
     612         [ #  # ]:          0 :         if (atomic_read(&reg->pending))
     613                 :          0 :                 list_del_init(&reg->list);
     614                 :            :         else
     615                 :          0 :                 list_move(&reg->list, &rh->quiesced_regions);
     616                 :            : 
     617                 :          0 :         spin_unlock_irq(&rh->region_lock);
     618                 :            : 
     619                 :          0 :         return 1;
     620                 :            : }
     621                 :            : 
     622                 :          0 : void dm_rh_recovery_prepare(struct dm_region_hash *rh)
     623                 :            : {
     624                 :            :         /* Extra reference to avoid race with dm_rh_stop_recovery */
     625                 :          0 :         atomic_inc(&rh->recovery_in_flight);
     626                 :            : 
     627         [ #  # ]:          0 :         while (!down_trylock(&rh->recovery_count)) {
     628                 :          0 :                 atomic_inc(&rh->recovery_in_flight);
     629         [ #  # ]:          0 :                 if (__rh_recovery_prepare(rh) <= 0) {
     630                 :          0 :                         atomic_dec(&rh->recovery_in_flight);
     631                 :          0 :                         up(&rh->recovery_count);
     632                 :          0 :                         break;
     633                 :            :                 }
     634                 :            :         }
     635                 :            : 
     636                 :            :         /* Drop the extra reference */
     637         [ #  # ]:          0 :         if (atomic_dec_and_test(&rh->recovery_in_flight))
     638                 :          0 :                 rh->wakeup_all_recovery_waiters(rh->context);
     639                 :          0 : }
     640                 :            : EXPORT_SYMBOL_GPL(dm_rh_recovery_prepare);
     641                 :            : 
     642                 :            : /*
     643                 :            :  * Returns any quiesced regions.
     644                 :            :  */
     645                 :          0 : struct dm_region *dm_rh_recovery_start(struct dm_region_hash *rh)
     646                 :            : {
     647                 :          0 :         struct dm_region *reg = NULL;
     648                 :            : 
     649                 :          0 :         spin_lock_irq(&rh->region_lock);
     650         [ #  # ]:          0 :         if (!list_empty(&rh->quiesced_regions)) {
     651                 :          0 :                 reg = list_entry(rh->quiesced_regions.next,
     652                 :            :                                  struct dm_region, list);
     653                 :          0 :                 list_del_init(&reg->list);  /* remove from the quiesced list */
     654                 :            :         }
     655                 :          0 :         spin_unlock_irq(&rh->region_lock);
     656                 :            : 
     657                 :          0 :         return reg;
     658                 :            : }
     659                 :            : EXPORT_SYMBOL_GPL(dm_rh_recovery_start);
     660                 :            : 
     661                 :          0 : void dm_rh_recovery_end(struct dm_region *reg, int success)
     662                 :            : {
     663                 :          0 :         struct dm_region_hash *rh = reg->rh;
     664                 :            : 
     665                 :          0 :         spin_lock_irq(&rh->region_lock);
     666         [ #  # ]:          0 :         if (success)
     667                 :          0 :                 list_add(&reg->list, &reg->rh->recovered_regions);
     668                 :            :         else
     669                 :          0 :                 list_add(&reg->list, &reg->rh->failed_recovered_regions);
     670                 :            : 
     671                 :          0 :         spin_unlock_irq(&rh->region_lock);
     672                 :            : 
     673                 :          0 :         rh->wakeup_workers(rh->context);
     674                 :          0 : }
     675                 :            : EXPORT_SYMBOL_GPL(dm_rh_recovery_end);
     676                 :            : 
     677                 :            : /* Return recovery in flight count. */
     678                 :          0 : int dm_rh_recovery_in_flight(struct dm_region_hash *rh)
     679                 :            : {
     680                 :          0 :         return atomic_read(&rh->recovery_in_flight);
     681                 :            : }
     682                 :            : EXPORT_SYMBOL_GPL(dm_rh_recovery_in_flight);
     683                 :            : 
     684                 :          0 : int dm_rh_flush(struct dm_region_hash *rh)
     685                 :            : {
     686                 :          0 :         return rh->log->type->flush(rh->log);
     687                 :            : }
     688                 :            : EXPORT_SYMBOL_GPL(dm_rh_flush);
     689                 :            : 
     690                 :          0 : void dm_rh_delay(struct dm_region_hash *rh, struct bio *bio)
     691                 :            : {
     692                 :          0 :         struct dm_region *reg;
     693                 :            : 
     694                 :          0 :         read_lock(&rh->hash_lock);
     695                 :          0 :         reg = __rh_find(rh, dm_rh_bio_to_region(rh, bio));
     696         [ #  # ]:          0 :         bio_list_add(&reg->delayed_bios, bio);
     697                 :          0 :         read_unlock(&rh->hash_lock);
     698                 :          0 : }
     699                 :            : EXPORT_SYMBOL_GPL(dm_rh_delay);
     700                 :            : 
     701                 :          0 : void dm_rh_stop_recovery(struct dm_region_hash *rh)
     702                 :            : {
     703                 :          0 :         int i;
     704                 :            : 
     705                 :            :         /* wait for any recovering regions */
     706         [ #  # ]:          0 :         for (i = 0; i < rh->max_recovery; i++)
     707                 :          0 :                 down(&rh->recovery_count);
     708                 :          0 : }
     709                 :            : EXPORT_SYMBOL_GPL(dm_rh_stop_recovery);
     710                 :            : 
     711                 :          0 : void dm_rh_start_recovery(struct dm_region_hash *rh)
     712                 :            : {
     713                 :          0 :         int i;
     714                 :            : 
     715         [ #  # ]:          0 :         for (i = 0; i < rh->max_recovery; i++)
     716                 :          0 :                 up(&rh->recovery_count);
     717                 :            : 
     718                 :          0 :         rh->wakeup_workers(rh->context);
     719                 :          0 : }
     720                 :            : EXPORT_SYMBOL_GPL(dm_rh_start_recovery);
     721                 :            : 
     722                 :            : MODULE_DESCRIPTION(DM_NAME " region hash");
     723                 :            : MODULE_AUTHOR("Joe Thornber/Heinz Mauelshagen <dm-devel@redhat.com>");
     724                 :            : MODULE_LICENSE("GPL");

Generated by: LCOV version 1.14