LCOV - code coverage report
Current view: top level - block - badblocks.c (source / functions) Hit Total Coverage
Test: landlock.info Lines: 0 249 0.0 %
Date: 2021-04-22 12:43:58 Functions: 0 11 0.0 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-2.0
       2             : /*
       3             :  * Bad block management
       4             :  *
       5             :  * - Heavily based on MD badblocks code from Neil Brown
       6             :  *
       7             :  * Copyright (c) 2015, Intel Corporation.
       8             :  */
       9             : 
      10             : #include <linux/badblocks.h>
      11             : #include <linux/seqlock.h>
      12             : #include <linux/device.h>
      13             : #include <linux/kernel.h>
      14             : #include <linux/module.h>
      15             : #include <linux/stddef.h>
      16             : #include <linux/types.h>
      17             : #include <linux/slab.h>
      18             : 
      19             : /**
      20             :  * badblocks_check() - check a given range for bad sectors
      21             :  * @bb:         the badblocks structure that holds all badblock information
      22             :  * @s:          sector (start) at which to check for badblocks
      23             :  * @sectors:    number of sectors to check for badblocks
      24             :  * @first_bad:  pointer to store location of the first badblock
      25             :  * @bad_sectors: pointer to store number of badblocks after @first_bad
      26             :  *
      27             :  * We can record which blocks on each device are 'bad' and so just
      28             :  * fail those blocks, or that stripe, rather than the whole device.
      29             :  * Entries in the bad-block table are 64bits wide.  This comprises:
      30             :  * Length of bad-range, in sectors: 0-511 for lengths 1-512
      31             :  * Start of bad-range, sector offset, 54 bits (allows 8 exbibytes)
      32             :  *  A 'shift' can be set so that larger blocks are tracked and
      33             :  *  consequently larger devices can be covered.
      34             :  * 'Acknowledged' flag - 1 bit. - the most significant bit.
      35             :  *
      36             :  * Locking of the bad-block table uses a seqlock so badblocks_check
      37             :  * might need to retry if it is very unlucky.
      38             :  * We will sometimes want to check for bad blocks in a bi_end_io function,
      39             :  * so we use the write_seqlock_irq variant.
      40             :  *
      41             :  * When looking for a bad block we specify a range and want to
      42             :  * know if any block in the range is bad.  So we binary-search
      43             :  * to the last range that starts at-or-before the given endpoint,
      44             :  * (or "before the sector after the target range")
      45             :  * then see if it ends after the given start.
      46             :  *
      47             :  * Return:
      48             :  *  0: there are no known bad blocks in the range
      49             :  *  1: there are known bad block which are all acknowledged
      50             :  * -1: there are bad blocks which have not yet been acknowledged in metadata.
      51             :  * plus the start/length of the first bad section we overlap.
      52             :  */
      53           0 : int badblocks_check(struct badblocks *bb, sector_t s, int sectors,
      54             :                         sector_t *first_bad, int *bad_sectors)
      55             : {
      56           0 :         int hi;
      57           0 :         int lo;
      58           0 :         u64 *p = bb->page;
      59           0 :         int rv;
      60           0 :         sector_t target = s + sectors;
      61           0 :         unsigned seq;
      62             : 
      63           0 :         if (bb->shift > 0) {
      64             :                 /* round the start down, and the end up */
      65           0 :                 s >>= bb->shift;
      66           0 :                 target += (1<<bb->shift) - 1;
      67           0 :                 target >>= bb->shift;
      68           0 :                 sectors = target - s;
      69             :         }
      70             :         /* 'target' is now the first block after the bad range */
      71             : 
      72           0 : retry:
      73           0 :         seq = read_seqbegin(&bb->lock);
      74           0 :         lo = 0;
      75           0 :         rv = 0;
      76           0 :         hi = bb->count;
      77             : 
      78             :         /* Binary search between lo and hi for 'target'
      79             :          * i.e. for the last range that starts before 'target'
      80             :          */
      81             :         /* INVARIANT: ranges before 'lo' and at-or-after 'hi'
      82             :          * are known not to be the last range before target.
      83             :          * VARIANT: hi-lo is the number of possible
      84             :          * ranges, and decreases until it reaches 1
      85             :          */
      86           0 :         while (hi - lo > 1) {
      87           0 :                 int mid = (lo + hi) / 2;
      88           0 :                 sector_t a = BB_OFFSET(p[mid]);
      89             : 
      90           0 :                 if (a < target)
      91             :                         /* This could still be the one, earlier ranges
      92             :                          * could not.
      93             :                          */
      94             :                         lo = mid;
      95             :                 else
      96             :                         /* This and later ranges are definitely out. */
      97           0 :                         hi = mid;
      98             :         }
      99             :         /* 'lo' might be the last that started before target, but 'hi' isn't */
     100           0 :         if (hi > lo) {
     101             :                 /* need to check all range that end after 's' to see if
     102             :                  * any are unacknowledged.
     103             :                  */
     104           0 :                 while (lo >= 0 &&
     105           0 :                        BB_OFFSET(p[lo]) + BB_LEN(p[lo]) > s) {
     106           0 :                         if (BB_OFFSET(p[lo]) < target) {
     107             :                                 /* starts before the end, and finishes after
     108             :                                  * the start, so they must overlap
     109             :                                  */
     110           0 :                                 if (rv != -1 && BB_ACK(p[lo]))
     111             :                                         rv = 1;
     112             :                                 else
     113           0 :                                         rv = -1;
     114           0 :                                 *first_bad = BB_OFFSET(p[lo]);
     115           0 :                                 *bad_sectors = BB_LEN(p[lo]);
     116             :                         }
     117           0 :                         lo--;
     118             :                 }
     119             :         }
     120             : 
     121           0 :         if (read_seqretry(&bb->lock, seq))
     122           0 :                 goto retry;
     123             : 
     124           0 :         return rv;
     125             : }
     126             : EXPORT_SYMBOL_GPL(badblocks_check);
     127             : 
     128           0 : static void badblocks_update_acked(struct badblocks *bb)
     129             : {
     130           0 :         u64 *p = bb->page;
     131           0 :         int i;
     132           0 :         bool unacked = false;
     133             : 
     134           0 :         if (!bb->unacked_exist)
     135             :                 return;
     136             : 
     137           0 :         for (i = 0; i < bb->count ; i++) {
     138           0 :                 if (!BB_ACK(p[i])) {
     139             :                         unacked = true;
     140             :                         break;
     141             :                 }
     142             :         }
     143             : 
     144           0 :         if (!unacked)
     145           0 :                 bb->unacked_exist = 0;
     146             : }
     147             : 
     148             : /**
     149             :  * badblocks_set() - Add a range of bad blocks to the table.
     150             :  * @bb:         the badblocks structure that holds all badblock information
     151             :  * @s:          first sector to mark as bad
     152             :  * @sectors:    number of sectors to mark as bad
     153             :  * @acknowledged: weather to mark the bad sectors as acknowledged
     154             :  *
     155             :  * This might extend the table, or might contract it if two adjacent ranges
     156             :  * can be merged. We binary-search to find the 'insertion' point, then
     157             :  * decide how best to handle it.
     158             :  *
     159             :  * Return:
     160             :  *  0: success
     161             :  *  1: failed to set badblocks (out of space)
     162             :  */
     163           0 : int badblocks_set(struct badblocks *bb, sector_t s, int sectors,
     164             :                         int acknowledged)
     165             : {
     166           0 :         u64 *p;
     167           0 :         int lo, hi;
     168           0 :         int rv = 0;
     169           0 :         unsigned long flags;
     170             : 
     171           0 :         if (bb->shift < 0)
     172             :                 /* badblocks are disabled */
     173             :                 return 1;
     174             : 
     175           0 :         if (bb->shift) {
     176             :                 /* round the start down, and the end up */
     177           0 :                 sector_t next = s + sectors;
     178             : 
     179           0 :                 s >>= bb->shift;
     180           0 :                 next += (1<<bb->shift) - 1;
     181           0 :                 next >>= bb->shift;
     182           0 :                 sectors = next - s;
     183             :         }
     184             : 
     185           0 :         write_seqlock_irqsave(&bb->lock, flags);
     186             : 
     187           0 :         p = bb->page;
     188           0 :         lo = 0;
     189           0 :         hi = bb->count;
     190             :         /* Find the last range that starts at-or-before 's' */
     191           0 :         while (hi - lo > 1) {
     192           0 :                 int mid = (lo + hi) / 2;
     193           0 :                 sector_t a = BB_OFFSET(p[mid]);
     194             : 
     195           0 :                 if (a <= s)
     196             :                         lo = mid;
     197             :                 else
     198           0 :                         hi = mid;
     199             :         }
     200           0 :         if (hi > lo && BB_OFFSET(p[lo]) > s)
     201           0 :                 hi = lo;
     202             : 
     203           0 :         if (hi > lo) {
     204             :                 /* we found a range that might merge with the start
     205             :                  * of our new range
     206             :                  */
     207           0 :                 sector_t a = BB_OFFSET(p[lo]);
     208           0 :                 sector_t e = a + BB_LEN(p[lo]);
     209           0 :                 int ack = BB_ACK(p[lo]);
     210             : 
     211           0 :                 if (e >= s) {
     212             :                         /* Yes, we can merge with a previous range */
     213           0 :                         if (s == a && s + sectors >= e)
     214             :                                 /* new range covers old */
     215             :                                 ack = acknowledged;
     216             :                         else
     217           0 :                                 ack = ack && acknowledged;
     218             : 
     219           0 :                         if (e < s + sectors)
     220             :                                 e = s + sectors;
     221           0 :                         if (e - a <= BB_MAX_LEN) {
     222           0 :                                 p[lo] = BB_MAKE(a, e-a, ack);
     223           0 :                                 s = e;
     224             :                         } else {
     225             :                                 /* does not all fit in one range,
     226             :                                  * make p[lo] maximal
     227             :                                  */
     228           0 :                                 if (BB_LEN(p[lo]) != BB_MAX_LEN)
     229           0 :                                         p[lo] = BB_MAKE(a, BB_MAX_LEN, ack);
     230           0 :                                 s = a + BB_MAX_LEN;
     231             :                         }
     232           0 :                         sectors = e - s;
     233             :                 }
     234             :         }
     235           0 :         if (sectors && hi < bb->count) {
     236             :                 /* 'hi' points to the first range that starts after 's'.
     237             :                  * Maybe we can merge with the start of that range
     238             :                  */
     239           0 :                 sector_t a = BB_OFFSET(p[hi]);
     240           0 :                 sector_t e = a + BB_LEN(p[hi]);
     241           0 :                 int ack = BB_ACK(p[hi]);
     242             : 
     243           0 :                 if (a <= s + sectors) {
     244             :                         /* merging is possible */
     245           0 :                         if (e <= s + sectors) {
     246             :                                 /* full overlap */
     247             :                                 e = s + sectors;
     248             :                                 ack = acknowledged;
     249             :                         } else
     250           0 :                                 ack = ack && acknowledged;
     251             : 
     252           0 :                         a = s;
     253           0 :                         if (e - a <= BB_MAX_LEN) {
     254           0 :                                 p[hi] = BB_MAKE(a, e-a, ack);
     255           0 :                                 s = e;
     256             :                         } else {
     257           0 :                                 p[hi] = BB_MAKE(a, BB_MAX_LEN, ack);
     258           0 :                                 s = a + BB_MAX_LEN;
     259             :                         }
     260           0 :                         sectors = e - s;
     261           0 :                         lo = hi;
     262           0 :                         hi++;
     263             :                 }
     264             :         }
     265           0 :         if (sectors == 0 && hi < bb->count) {
     266             :                 /* we might be able to combine lo and hi */
     267             :                 /* Note: 's' is at the end of 'lo' */
     268           0 :                 sector_t a = BB_OFFSET(p[hi]);
     269           0 :                 int lolen = BB_LEN(p[lo]);
     270           0 :                 int hilen = BB_LEN(p[hi]);
     271           0 :                 int newlen = lolen + hilen - (s - a);
     272             : 
     273           0 :                 if (s >= a && newlen < BB_MAX_LEN) {
     274             :                         /* yes, we can combine them */
     275           0 :                         int ack = BB_ACK(p[lo]) && BB_ACK(p[hi]);
     276             : 
     277           0 :                         p[lo] = BB_MAKE(BB_OFFSET(p[lo]), newlen, ack);
     278           0 :                         memmove(p + hi, p + hi + 1,
     279           0 :                                 (bb->count - hi - 1) * 8);
     280           0 :                         bb->count--;
     281             :                 }
     282             :         }
     283           0 :         while (sectors) {
     284             :                 /* didn't merge (it all).
     285             :                  * Need to add a range just before 'hi'
     286             :                  */
     287           0 :                 if (bb->count >= MAX_BADBLOCKS) {
     288             :                         /* No room for more */
     289             :                         rv = 1;
     290             :                         break;
     291             :                 } else {
     292           0 :                         int this_sectors = sectors;
     293             : 
     294           0 :                         memmove(p + hi + 1, p + hi,
     295           0 :                                 (bb->count - hi) * 8);
     296           0 :                         bb->count++;
     297             : 
     298           0 :                         if (this_sectors > BB_MAX_LEN)
     299             :                                 this_sectors = BB_MAX_LEN;
     300           0 :                         p[hi] = BB_MAKE(s, this_sectors, acknowledged);
     301           0 :                         sectors -= this_sectors;
     302           0 :                         s += this_sectors;
     303             :                 }
     304             :         }
     305             : 
     306           0 :         bb->changed = 1;
     307           0 :         if (!acknowledged)
     308           0 :                 bb->unacked_exist = 1;
     309             :         else
     310           0 :                 badblocks_update_acked(bb);
     311           0 :         write_sequnlock_irqrestore(&bb->lock, flags);
     312             : 
     313           0 :         return rv;
     314             : }
     315             : EXPORT_SYMBOL_GPL(badblocks_set);
     316             : 
     317             : /**
     318             :  * badblocks_clear() - Remove a range of bad blocks to the table.
     319             :  * @bb:         the badblocks structure that holds all badblock information
     320             :  * @s:          first sector to mark as bad
     321             :  * @sectors:    number of sectors to mark as bad
     322             :  *
     323             :  * This may involve extending the table if we spilt a region,
     324             :  * but it must not fail.  So if the table becomes full, we just
     325             :  * drop the remove request.
     326             :  *
     327             :  * Return:
     328             :  *  0: success
     329             :  *  1: failed to clear badblocks
     330             :  */
     331           0 : int badblocks_clear(struct badblocks *bb, sector_t s, int sectors)
     332             : {
     333           0 :         u64 *p;
     334           0 :         int lo, hi;
     335           0 :         sector_t target = s + sectors;
     336           0 :         int rv = 0;
     337             : 
     338           0 :         if (bb->shift > 0) {
     339             :                 /* When clearing we round the start up and the end down.
     340             :                  * This should not matter as the shift should align with
     341             :                  * the block size and no rounding should ever be needed.
     342             :                  * However it is better the think a block is bad when it
     343             :                  * isn't than to think a block is not bad when it is.
     344             :                  */
     345           0 :                 s += (1<<bb->shift) - 1;
     346           0 :                 s >>= bb->shift;
     347           0 :                 target >>= bb->shift;
     348           0 :                 sectors = target - s;
     349             :         }
     350             : 
     351           0 :         write_seqlock_irq(&bb->lock);
     352             : 
     353           0 :         p = bb->page;
     354           0 :         lo = 0;
     355           0 :         hi = bb->count;
     356             :         /* Find the last range that starts before 'target' */
     357           0 :         while (hi - lo > 1) {
     358           0 :                 int mid = (lo + hi) / 2;
     359           0 :                 sector_t a = BB_OFFSET(p[mid]);
     360             : 
     361           0 :                 if (a < target)
     362             :                         lo = mid;
     363             :                 else
     364           0 :                         hi = mid;
     365             :         }
     366           0 :         if (hi > lo) {
     367             :                 /* p[lo] is the last range that could overlap the
     368             :                  * current range.  Earlier ranges could also overlap,
     369             :                  * but only this one can overlap the end of the range.
     370             :                  */
     371           0 :                 if ((BB_OFFSET(p[lo]) + BB_LEN(p[lo]) > target) &&
     372             :                     (BB_OFFSET(p[lo]) < target)) {
     373             :                         /* Partial overlap, leave the tail of this range */
     374           0 :                         int ack = BB_ACK(p[lo]);
     375           0 :                         sector_t a = BB_OFFSET(p[lo]);
     376           0 :                         sector_t end = a + BB_LEN(p[lo]);
     377             : 
     378           0 :                         if (a < s) {
     379             :                                 /* we need to split this range */
     380           0 :                                 if (bb->count >= MAX_BADBLOCKS) {
     381           0 :                                         rv = -ENOSPC;
     382           0 :                                         goto out;
     383             :                                 }
     384           0 :                                 memmove(p+lo+1, p+lo, (bb->count - lo) * 8);
     385           0 :                                 bb->count++;
     386           0 :                                 p[lo] = BB_MAKE(a, s-a, ack);
     387           0 :                                 lo++;
     388             :                         }
     389           0 :                         p[lo] = BB_MAKE(target, end - target, ack);
     390             :                         /* there is no longer an overlap */
     391           0 :                         hi = lo;
     392           0 :                         lo--;
     393             :                 }
     394           0 :                 while (lo >= 0 &&
     395           0 :                        (BB_OFFSET(p[lo]) + BB_LEN(p[lo]) > s) &&
     396             :                        (BB_OFFSET(p[lo]) < target)) {
     397             :                         /* This range does overlap */
     398           0 :                         if (BB_OFFSET(p[lo]) < s) {
     399             :                                 /* Keep the early parts of this range. */
     400           0 :                                 int ack = BB_ACK(p[lo]);
     401           0 :                                 sector_t start = BB_OFFSET(p[lo]);
     402             : 
     403           0 :                                 p[lo] = BB_MAKE(start, s - start, ack);
     404             :                                 /* now low doesn't overlap, so.. */
     405           0 :                                 break;
     406             :                         }
     407           0 :                         lo--;
     408             :                 }
     409             :                 /* 'lo' is strictly before, 'hi' is strictly after,
     410             :                  * anything between needs to be discarded
     411             :                  */
     412           0 :                 if (hi - lo > 1) {
     413           0 :                         memmove(p+lo+1, p+hi, (bb->count - hi) * 8);
     414           0 :                         bb->count -= (hi - lo - 1);
     415             :                 }
     416             :         }
     417             : 
     418           0 :         badblocks_update_acked(bb);
     419           0 :         bb->changed = 1;
     420           0 : out:
     421           0 :         write_sequnlock_irq(&bb->lock);
     422           0 :         return rv;
     423             : }
     424             : EXPORT_SYMBOL_GPL(badblocks_clear);
     425             : 
     426             : /**
     427             :  * ack_all_badblocks() - Acknowledge all bad blocks in a list.
     428             :  * @bb:         the badblocks structure that holds all badblock information
     429             :  *
     430             :  * This only succeeds if ->changed is clear.  It is used by
     431             :  * in-kernel metadata updates
     432             :  */
     433           0 : void ack_all_badblocks(struct badblocks *bb)
     434             : {
     435           0 :         if (bb->page == NULL || bb->changed)
     436             :                 /* no point even trying */
     437             :                 return;
     438           0 :         write_seqlock_irq(&bb->lock);
     439             : 
     440           0 :         if (bb->changed == 0 && bb->unacked_exist) {
     441           0 :                 u64 *p = bb->page;
     442           0 :                 int i;
     443             : 
     444           0 :                 for (i = 0; i < bb->count ; i++) {
     445           0 :                         if (!BB_ACK(p[i])) {
     446           0 :                                 sector_t start = BB_OFFSET(p[i]);
     447           0 :                                 int len = BB_LEN(p[i]);
     448             : 
     449           0 :                                 p[i] = BB_MAKE(start, len, 1);
     450             :                         }
     451             :                 }
     452           0 :                 bb->unacked_exist = 0;
     453             :         }
     454           0 :         write_sequnlock_irq(&bb->lock);
     455             : }
     456             : EXPORT_SYMBOL_GPL(ack_all_badblocks);
     457             : 
     458             : /**
     459             :  * badblocks_show() - sysfs access to bad-blocks list
     460             :  * @bb:         the badblocks structure that holds all badblock information
     461             :  * @page:       buffer received from sysfs
     462             :  * @unack:      weather to show unacknowledged badblocks
     463             :  *
     464             :  * Return:
     465             :  *  Length of returned data
     466             :  */
     467           0 : ssize_t badblocks_show(struct badblocks *bb, char *page, int unack)
     468             : {
     469           0 :         size_t len;
     470           0 :         int i;
     471           0 :         u64 *p = bb->page;
     472           0 :         unsigned seq;
     473             : 
     474           0 :         if (bb->shift < 0)
     475             :                 return 0;
     476             : 
     477           0 : retry:
     478           0 :         seq = read_seqbegin(&bb->lock);
     479             : 
     480           0 :         len = 0;
     481           0 :         i = 0;
     482             : 
     483           0 :         while (len < PAGE_SIZE && i < bb->count) {
     484           0 :                 sector_t s = BB_OFFSET(p[i]);
     485           0 :                 unsigned int length = BB_LEN(p[i]);
     486           0 :                 int ack = BB_ACK(p[i]);
     487             : 
     488           0 :                 i++;
     489             : 
     490           0 :                 if (unack && ack)
     491           0 :                         continue;
     492             : 
     493           0 :                 len += snprintf(page+len, PAGE_SIZE-len, "%llu %u\n",
     494             :                                 (unsigned long long)s << bb->shift,
     495           0 :                                 length << bb->shift);
     496             :         }
     497           0 :         if (unack && len == 0)
     498           0 :                 bb->unacked_exist = 0;
     499             : 
     500           0 :         if (read_seqretry(&bb->lock, seq))
     501           0 :                 goto retry;
     502             : 
     503           0 :         return len;
     504             : }
     505             : EXPORT_SYMBOL_GPL(badblocks_show);
     506             : 
     507             : /**
     508             :  * badblocks_store() - sysfs access to bad-blocks list
     509             :  * @bb:         the badblocks structure that holds all badblock information
     510             :  * @page:       buffer received from sysfs
     511             :  * @len:        length of data received from sysfs
     512             :  * @unack:      weather to show unacknowledged badblocks
     513             :  *
     514             :  * Return:
     515             :  *  Length of the buffer processed or -ve error.
     516             :  */
     517           0 : ssize_t badblocks_store(struct badblocks *bb, const char *page, size_t len,
     518             :                         int unack)
     519             : {
     520           0 :         unsigned long long sector;
     521           0 :         int length;
     522           0 :         char newline;
     523             : 
     524           0 :         switch (sscanf(page, "%llu %d%c", &sector, &length, &newline)) {
     525           0 :         case 3:
     526           0 :                 if (newline != '\n')
     527             :                         return -EINVAL;
     528           0 :                 fallthrough;
     529             :         case 2:
     530           0 :                 if (length <= 0)
     531             :                         return -EINVAL;
     532           0 :                 break;
     533             :         default:
     534             :                 return -EINVAL;
     535             :         }
     536             : 
     537           0 :         if (badblocks_set(bb, sector, length, !unack))
     538             :                 return -ENOSPC;
     539             :         else
     540           0 :                 return len;
     541             : }
     542             : EXPORT_SYMBOL_GPL(badblocks_store);
     543             : 
     544           0 : static int __badblocks_init(struct device *dev, struct badblocks *bb,
     545             :                 int enable)
     546             : {
     547           0 :         bb->dev = dev;
     548           0 :         bb->count = 0;
     549           0 :         if (enable)
     550           0 :                 bb->shift = 0;
     551             :         else
     552           0 :                 bb->shift = -1;
     553           0 :         if (dev)
     554           0 :                 bb->page = devm_kzalloc(dev, PAGE_SIZE, GFP_KERNEL);
     555             :         else
     556           0 :                 bb->page = kzalloc(PAGE_SIZE, GFP_KERNEL);
     557           0 :         if (!bb->page) {
     558           0 :                 bb->shift = -1;
     559           0 :                 return -ENOMEM;
     560             :         }
     561           0 :         seqlock_init(&bb->lock);
     562             : 
     563           0 :         return 0;
     564             : }
     565             : 
     566             : /**
     567             :  * badblocks_init() - initialize the badblocks structure
     568             :  * @bb:         the badblocks structure that holds all badblock information
     569             :  * @enable:     weather to enable badblocks accounting
     570             :  *
     571             :  * Return:
     572             :  *  0: success
     573             :  *  -ve errno: on error
     574             :  */
     575           0 : int badblocks_init(struct badblocks *bb, int enable)
     576             : {
     577           0 :         return __badblocks_init(NULL, bb, enable);
     578             : }
     579             : EXPORT_SYMBOL_GPL(badblocks_init);
     580             : 
     581           0 : int devm_init_badblocks(struct device *dev, struct badblocks *bb)
     582             : {
     583           0 :         if (!bb)
     584             :                 return -EINVAL;
     585           0 :         return __badblocks_init(dev, bb, 1);
     586             : }
     587             : EXPORT_SYMBOL_GPL(devm_init_badblocks);
     588             : 
     589             : /**
     590             :  * badblocks_exit() - free the badblocks structure
     591             :  * @bb:         the badblocks structure that holds all badblock information
     592             :  */
     593           0 : void badblocks_exit(struct badblocks *bb)
     594             : {
     595           0 :         if (!bb)
     596             :                 return;
     597           0 :         if (bb->dev)
     598           0 :                 devm_kfree(bb->dev, bb->page);
     599             :         else
     600           0 :                 kfree(bb->page);
     601           0 :         bb->page = NULL;
     602             : }
     603             : EXPORT_SYMBOL_GPL(badblocks_exit);

Generated by: LCOV version 1.14