linux-btrfs.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Hugo Mills <hugo@carfax.org.uk>
To: Btrfs mailing list <linux-btrfs@vger.kernel.org>
Subject: The *right* algorithm for determining the amount of free space
Date: Fri, 11 Mar 2016 22:17:03 +0000	[thread overview]
Message-ID: <20160311221703.GJ17196@carfax.org.uk> (raw)

[-- Attachment #1: Type: text/plain, Size: 3632 bytes --]

   I know I promised this a while ago and didn't get round to it, but
Henk's tinkering reminded me of it. I note specifically that the
algorithm used to give the free space to plain old df gives incorrect
results -- probably because it's not using the algorithm below.

   There is an algorithm that (seems to) give the correct number of
block groups which can be allocated, given a current allocation state
of the FS. I haven't been able to prove it correct, but it seems to
work. Here's some pseudocode for it:

(Note that all arrays here are 1-based, not 0-based. I originally
wrote this using maths notation, not C. I've got a slightly more
formal write-up of it in LaTeX if anyone wants, but it's full of
unproven crap as well as the description of the algorithm, so I'd
rather edit that before releasing it).

Variables:
   n: Number of devices
   c[n]: Amount of unallocated space on each device
   s: Number of chunks per block group:
        s = 1 for single
        s = 2 for RAID-1
        s = floor(n/2) for RAID-10
        s = n for RAID-0, -5, -6

FUNCTION USABLE(s)
   if RAID0
      return s
   elif RAID1 or SINGLE
      return 1
   elif RAID10
      return s/2
   elif RAID5
      return s-1
   elif RAID6
      return s-2

FUNCTION CHUNKS(n)
   if RAID0 or RAID5 or RAID6
      return n
   elif RAID1
      return 2
   elif RAID10
      return floor(n/2)
   elif SINGLE
      return 1

FUNCTION LOCAL_BOUND(n, c, s, q)
   x := sum( i=q+1..n: c[i] )
   return floor( x / (s-q) )

FUNCTION ALL_BOUNDS(n, c, s)
   r: integer list

   for q=1..n
      if c[q] >= LOCAL_BOUND(n, c, s, q) then
         r.append(c[q])
      endif
   endfor

   return r

FUNCTION BOUND(n, c, s)
   total := ( sum i=1..n (c[i]) )
   return min(floor( total / s ), ALL_BOUNDS(n, c[n], s))

FUNCTION ALLOCATE(n, c, s)
   # Reduce the free space by the maximum amount possible with s-sized
   # allocations.
   r[n]: integer

   b := BOUND(n, c, s) * s
   for i=n..1
      a := min(c[i], floor( b/i ))
      b := b - a
      r[i] := c[i] - a

   return r

FUNCTION SPACE(n, c)
   s := CHUNKS(n)
   total := 0
   while BOUND(n, c, s) > 0
      # Increase the amount of usable space in this pass
      total := total + BOUND(n, c, s)*USABLE(s)
      c := ALLOCATE(n, c, s)

      # Find the new number of usable devices
      n := 0
      while c[n+1] != 0
         n := n + 1
      s := CHUNKS(n)

   return total

   The algorithm works by setting up the list of free space, c, one
entry for each device in the FS, and sorting it in decreasing
order. The values should be measured in chunks -- so divide the free
space on each device by 1 GiB. Devices with no free space should be
dropped from the list.

   The parameter s is dependent on the RAID level. See the list above
for what it should be set to.

   The BOUND function determines the number of block group allocations
possible for a given configuration, c, and stripe size, s. Multiply
the number of allocations by s to get the number of chunks that can be
allocated.

   The ALLOCATE function returns the list of free space on each device
after the maximum number of block groups has been allocated. This
should be called repeatedly, updating the value of s each time, until
no more allocations are possible.

   I hope that's of use to someone with more spare coding time than
me. And maybe we can finally have free space estimation that gets it
right...

   Hugo.

-- 
Hugo Mills             | A gentleman doesn't do damage unless he's paid for
hugo@... carfax.org.uk | it.
http://carfax.org.uk/  |
PGP: E2AB1DE4          |                                            Juri Papay

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

             reply	other threads:[~2016-03-11 22:17 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-03-11 22:17 Hugo Mills [this message]
2016-03-11 22:18 ` The *right* algorithm for determining the amount of free space Hugo Mills
2016-03-18 17:58 ` David Sterba
2016-03-18 18:03   ` Hugo Mills

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20160311221703.GJ17196@carfax.org.uk \
    --to=hugo@carfax.org.uk \
    --cc=linux-btrfs@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).