patch-2.4.1 linux/fs/reiserfs/bitmap.c

Next file: linux/fs/reiserfs/buffer2.c
Previous file: linux/fs/reiserfs/README
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.0/linux/fs/reiserfs/bitmap.c linux/fs/reiserfs/bitmap.c
@@ -0,0 +1,679 @@
+/*
+ * Copyright 2000 by Hans Reiser, licensing governed by reiserfs/README
+ */
+#ifdef __KERNEL__
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/reiserfs_fs.h>
+#include <linux/locks.h>
+#include <asm/bitops.h>
+
+#else
+
+#include "nokernel.h"
+
+#endif
+
+
+#ifdef CONFIG_REISERFS_CHECK
+
+/* this is a safety check to make sure
+** blocks are reused properly.  used for debugging only.
+**
+** this checks, that block can be reused, and it has correct state
+**   (free or busy) 
+*/
+int is_reusable (struct super_block * s, unsigned long block, int bit_value)
+{
+    int i, j;
+  
+    if (block == 0 || block >= SB_BLOCK_COUNT (s)) {
+	reiserfs_warning ("vs-4010: is_reusable: block number is out of range %lu (%u)\n",
+			  block, SB_BLOCK_COUNT (s));
+	return 0;
+    }
+
+    /* it can't be one of the bitmap blocks */
+    for (i = 0; i < SB_BMAP_NR (s); i ++)
+	if (block == SB_AP_BITMAP (s)[i]->b_blocknr) {
+	    reiserfs_warning ("vs: 4020: is_reusable: "
+			      "bitmap block %lu(%u) can't be freed or reused\n",
+			      block, SB_BMAP_NR (s));
+	    return 0;
+	}
+  
+    i = block / (s->s_blocksize << 3);
+    if (i >= SB_BMAP_NR (s)) {
+	reiserfs_warning ("vs-4030: is_reusable: there is no so many bitmap blocks: "
+			  "block=%lu, bitmap_nr=%d\n", block, i);
+	return 0;
+    }
+
+    j = block % (s->s_blocksize << 3);
+    if ((bit_value == 0 && 
+         reiserfs_test_le_bit(j, SB_AP_BITMAP(s)[i]->b_data)) ||
+	(bit_value == 1 && 
+	 reiserfs_test_le_bit(j, SB_AP_BITMAP (s)[i]->b_data) == 0)) {
+	reiserfs_warning ("vs-4040: is_reusable: corresponding bit of block %lu does not "
+			  "match required value (i==%d, j==%d) test_bit==%d\n",
+		block, i, j, reiserfs_test_le_bit (j, SB_AP_BITMAP (s)[i]->b_data));
+	return 0;
+    }
+
+    if (bit_value == 0 && block == SB_ROOT_BLOCK (s)) {
+	reiserfs_warning ("vs-4050: is_reusable: this is root block (%u), "
+			  "it must be busy", SB_ROOT_BLOCK (s));
+	return 0;
+    }
+
+    return 1;
+}
+
+
+
+
+#endif /* CONFIG_REISERFS_CHECK */
+
+#if 0
+/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
+int is_used (struct super_block * s, unsigned long block)
+{
+    int i, j;
+
+    i = block / (s->s_blocksize << 3);
+    j = block % (s->s_blocksize << 3);
+    if (reiserfs_test_le_bit(j, SB_AP_BITMAP (s)[i]->b_data))
+	return 1;
+    return 0;
+  
+}
+/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
+#endif
+
+
+/* get address of corresponding bit (bitmap block number and offset in it) */
+static inline void get_bit_address (struct super_block * s, unsigned long block, int * bmap_nr, int * offset)
+{
+                                /* It is in the bitmap block number equal to the block number divided by the number of
+                                   bits in a block. */
+    *bmap_nr = block / (s->s_blocksize << 3);
+                                /* Within that bitmap block it is located at bit offset *offset. */
+    *offset = block % (s->s_blocksize << 3);
+    return;
+}
+
+
+/* There would be a modest performance benefit if we write a version
+   to free a list of blocks at once. -Hans */
+				/* I wonder if it would be less modest
+                                   now that we use journaling. -Hans */
+void reiserfs_free_block (struct reiserfs_transaction_handle *th, unsigned long block)
+{
+    struct super_block * s = th->t_super;
+    struct reiserfs_super_block * rs;
+    struct buffer_head * sbh;
+    struct buffer_head ** apbh;
+    int nr, offset;
+
+#ifdef CONFIG_REISERFS_CHECK
+    if (!s)
+	reiserfs_panic (s, "vs-4060: reiserfs_free_block: trying to free block on nonexistent device");
+
+    if (is_reusable (s, block, 1) == 0)
+	reiserfs_panic (s, "vs-4070: reiserfs_free_block: can not free such block");
+#endif
+
+  rs = SB_DISK_SUPER_BLOCK (s);
+  sbh = SB_BUFFER_WITH_SB (s);
+  apbh = SB_AP_BITMAP (s);
+
+  get_bit_address (s, block, &nr, &offset);
+
+  /* mark it before we clear it, just in case */
+  journal_mark_freed(th, s, block) ;
+
+  reiserfs_prepare_for_journal(s, apbh[nr], 1 ) ;
+
+  /* clear bit for the given block in bit map */
+  if (!reiserfs_test_and_clear_le_bit (offset, apbh[nr]->b_data)) {
+      reiserfs_warning ("vs-4080: reiserfs_free_block: "
+			"free_block (%04x:%lu)[dev:blocknr]: bit already cleared\n", 
+	    s->s_dev, block);
+  }
+  journal_mark_dirty (th, s, apbh[nr]);
+
+  reiserfs_prepare_for_journal(s, sbh, 1) ;
+  /* update super block */
+  rs->s_free_blocks = cpu_to_le32 (le32_to_cpu (rs->s_free_blocks) + 1);
+
+  journal_mark_dirty (th, s, sbh);
+  s->s_dirt = 1;
+}
+
+
+
+/* beginning from offset-th bit in bmap_nr-th bitmap block,
+   find_forward finds the closest zero bit. It returns 1 and zero
+   bit address (bitmap, offset) if zero bit found or 0 if there is no
+   zero bit in the forward direction */
+/* The function is NOT SCHEDULE-SAFE! */
+static int find_forward (struct super_block * s, int * bmap_nr, int * offset, int for_unformatted)
+{
+  int i, j;
+  struct buffer_head * bh;
+  unsigned long block_to_try = 0;
+  unsigned long next_block_to_try = 0 ;
+
+  for (i = *bmap_nr; i < SB_BMAP_NR (s); i ++, *offset = 0) {
+    /* get corresponding bitmap block */
+    bh = SB_AP_BITMAP (s)[i];
+    if (buffer_locked (bh)) {
+        __wait_on_buffer (bh);
+    }
+retry:
+    j = reiserfs_find_next_zero_le_bit ((unsigned long *)bh->b_data, 
+                                         s->s_blocksize << 3, *offset);
+
+    /* wow, this really needs to be redone.  We can't allocate a block if
+    ** it is in the journal somehow.  reiserfs_in_journal makes a suggestion
+    ** for a good block if the one you ask for is in the journal.  Note,
+    ** reiserfs_in_journal might reject the block it suggests.  The big
+    ** gain from the suggestion is when a big file has been deleted, and
+    ** many blocks show free in the real bitmap, but are all not free
+    ** in the journal list bitmaps.
+    **
+    ** this whole system sucks.  The bitmaps should reflect exactly what
+    ** can and can't be allocated, and the journal should update them as
+    ** it goes.  TODO.
+    */
+    if (j < (s->s_blocksize << 3)) {
+      block_to_try = (i * (s->s_blocksize << 3)) + j; 
+
+      /* the block is not in the journal, we can proceed */
+      if (!(reiserfs_in_journal(s, s->s_dev, block_to_try, s->s_blocksize, for_unformatted, &next_block_to_try))) {
+	*bmap_nr = i;
+	*offset = j;
+	return 1;
+      } 
+      /* the block is in the journal */
+      else if ((j+1) < (s->s_blocksize << 3)) { /* try again */
+	/* reiserfs_in_journal suggested a new block to try */
+	if (next_block_to_try > 0) {
+	  int new_i ;
+	  get_bit_address (s, next_block_to_try, &new_i, offset);
+
+	  /* block is not in this bitmap. reset i and continue
+	  ** we only reset i if new_i is in a later bitmap.
+	  */
+	  if (new_i > i) {
+	    i = (new_i - 1 ); /* i gets incremented by the for loop */
+	    continue ;
+	  }
+	} else {
+	  /* no suggestion was made, just try the next block */
+	  *offset = j+1 ;
+	}
+	goto retry ;
+      }
+    }
+  }
+    /* zero bit not found */
+    return 0;
+}
+
+/* return 0 if no free blocks, else return 1 */
+/* The function is NOT SCHEDULE-SAFE!  
+** because the bitmap block we want to change could be locked, and on its
+** way to the disk when we want to read it, and because of the 
+** flush_async_commits.  Per bitmap block locks won't help much, and 
+** really aren't needed, as we retry later on if we try to set the bit
+** and it is already set.
+*/
+static int find_zero_bit_in_bitmap (struct super_block * s, 
+                                    unsigned long search_start, 
+				    int * bmap_nr, int * offset, 
+				    int for_unformatted)
+{
+  int retry_count = 0 ;
+  /* get bit location (bitmap number and bit offset) of search_start block */
+  get_bit_address (s, search_start, bmap_nr, offset);
+
+    /* note that we search forward in the bitmap, benchmarks have shown that it is better to allocate in increasing
+       sequence, which is probably due to the disk spinning in the forward direction.. */
+    if (find_forward (s, bmap_nr, offset, for_unformatted) == 0) {
+      /* there wasn't a free block with number greater than our
+         starting point, so we are going to go to the beginning of the disk */
+
+retry:
+      search_start = 0; /* caller will reset search_start for itself also. */
+      get_bit_address (s, search_start, bmap_nr, offset);
+      if (find_forward (s, bmap_nr,offset,for_unformatted) == 0) {
+	if (for_unformatted) {	/* why only unformatted nodes? -Hans */
+	  if (retry_count == 0) {
+	    /* we've got a chance that flushing async commits will free up
+	    ** some space.  Sync then retry
+	    */
+	    flush_async_commits(s) ;
+	    retry_count++ ;
+	    goto retry ;
+	  } else if (retry_count > 0) {
+	    /* nothing more we can do.  Make the others wait, flush
+	    ** all log blocks to disk, and flush to their home locations.
+	    ** this will free up any blocks held by the journal
+	    */
+	    SB_JOURNAL(s)->j_must_wait = 1 ;
+	  }
+	}
+        return 0;
+      }
+    }
+  return 1;
+}
+
+/* get amount_needed free block numbers from scanning the bitmap of
+   free/used blocks.
+   
+   Optimize layout by trying to find them starting from search_start
+   and moving in increasing blocknr direction.  (This was found to be
+   faster than using a bi-directional elevator_direction, in part
+   because of disk spin direction, in part because by the time one
+   reaches the end of the disk the beginning of the disk is the least
+   congested).
+
+   search_start is the block number of the left
+   semantic neighbor of the node we create.
+
+   return CARRY_ON if everything is ok
+   return NO_DISK_SPACE if out of disk space
+   return NO_MORE_UNUSED_CONTIGUOUS_BLOCKS if the block we found is not contiguous to the last one
+   
+   return block numbers found, in the array free_blocknrs.  assumes
+   that any non-zero entries already present in the array are valid.
+   This feature is perhaps convenient coding when one might not have
+   used all blocknrs from the last time one called this function, or
+   perhaps it is an archaism from the days of schedule tracking, one
+   of us ought to reread the code that calls this, and analyze whether
+   it is still the right way to code it.
+
+   spare space is used only when priority is set to 1. reiserfsck has
+   its own reiserfs_new_blocknrs, which can use reserved space
+
+   exactly what reserved space?  the SPARE_SPACE?  if so, please comment reiserfs.h.
+
+   Give example of who uses spare space, and say that it is a deadlock
+   avoidance mechanism.  -Hans */
+
+/* This function is NOT SCHEDULE-SAFE! */
+
+static int do_reiserfs_new_blocknrs (struct reiserfs_transaction_handle *th,
+                                     unsigned long * free_blocknrs, 
+				     unsigned long search_start, 
+				     int amount_needed, int priority, 
+				     int for_unformatted,
+				     int for_prealloc)
+{
+  struct super_block * s = th->t_super;
+  int i, j;
+  unsigned long * block_list_start = free_blocknrs;
+  int init_amount_needed = amount_needed;
+  unsigned long new_block = 0 ; 
+
+    if (SB_FREE_BLOCKS (s) < SPARE_SPACE && !priority)
+	/* we can answer NO_DISK_SPACE being asked for new block with
+	   priority 0 */
+	return NO_DISK_SPACE;
+
+#ifdef CONFIG_REISERFS_CHECK
+    if (!s)
+	reiserfs_panic (s, "vs-4090: reiserfs_new_blocknrs: trying to get new block from nonexistent device");
+    
+    if (search_start == MAX_B_NUM)
+	reiserfs_panic (s, "vs-4100: reiserfs_new_blocknrs: we are optimizing location based on "
+			"the bogus location of a temp buffer (%lu).", search_start);
+    
+    if (amount_needed < 1 || amount_needed > 2) 
+	reiserfs_panic (s, "vs-4110: reiserfs_new_blocknrs: amount_needed parameter incorrect (%d)", amount_needed);
+#endif /* CONFIG_REISERFS_CHECK */
+
+  /* We continue the while loop if another process snatches our found
+   * free block from us after we find it but before we successfully
+   * mark it as in use, or if we need to use sync to free up some
+   * blocks on the preserve list.  */
+
+  while (amount_needed--) {
+    /* skip over any blocknrs already gotten last time. */
+    if (*(free_blocknrs) != 0) {
+#ifdef CONFIG_REISERFS_CHECK
+      if (is_reusable (s, *free_blocknrs, 1) == 0)
+	reiserfs_panic(s, "vs-4120: reiserfs_new_blocknrs: bad blocknr on free_blocknrs list");
+#endif /* CONFIG_REISERFS_CHECK */
+      free_blocknrs++;
+      continue;
+    }
+    /* look for zero bits in bitmap */
+    if (find_zero_bit_in_bitmap(s,search_start, &i, &j,for_unformatted) == 0) {
+      if (find_zero_bit_in_bitmap(s,search_start,&i,&j, for_unformatted) == 0) {
+				/* recode without the goto and without
+				   the if.  It will require a
+				   duplicate for.  This is worth the
+				   code clarity.  Your way was
+				   admirable, and just a bit too
+				   clever in saving instructions.:-)
+				   I'd say create a new function, but
+				   that would slow things also, yes?
+				   -Hans */
+free_and_return:
+	for ( ; block_list_start != free_blocknrs; block_list_start++) {
+	  reiserfs_free_block (th, *block_list_start);
+	  *block_list_start = 0;
+	}
+	if (for_prealloc) 
+	    return NO_MORE_UNUSED_CONTIGUOUS_BLOCKS;
+	else
+	    return NO_DISK_SPACE;
+      }
+    }
+    
+    /* i and j now contain the results of the search. i = bitmap block
+       number containing free block, j = offset in this block.  we
+       compute the blocknr which is our result, store it in
+       free_blocknrs, and increment the pointer so that on the next
+       loop we will insert into the next location in the array.  Also
+       in preparation for the next loop, search_start is changed so
+       that the next search will not rescan the same range but will
+       start where this search finished.  Note that while it is
+       possible that schedule has occurred and blocks have been freed
+       in that range, it is perhaps more important that the blocks
+       returned be near each other than that they be near their other
+       neighbors, and it also simplifies and speeds the code this way.  */
+
+    /* journal: we need to make sure the block we are giving out is not
+    ** a log block, horrible things would happen there.
+    */
+    new_block = (i * (s->s_blocksize << 3)) + j; 
+    if (for_prealloc && (new_block - 1) != search_start) {
+      /* preallocated blocks must be contiguous, bail if we didnt find one.
+      ** this is not a bug.  We want to do the check here, before the
+      ** bitmap block is prepared, and before we set the bit and log the
+      ** bitmap. 
+      **
+      ** If we do the check after this function returns, we have to 
+      ** call reiserfs_free_block for new_block, which would be pure
+      ** overhead.
+      **
+      ** for_prealloc should only be set if the caller can deal with the
+      ** NO_MORE_UNUSED_CONTIGUOUS_BLOCKS return value.  This can be
+      ** returned before the disk is actually full
+      */
+      goto free_and_return ;
+    }
+    search_start = new_block ;
+    if (search_start >= reiserfs_get_journal_block(s) &&
+        search_start < (reiserfs_get_journal_block(s) + JOURNAL_BLOCK_COUNT)) {
+	reiserfs_warning("vs-4130: reiserfs_new_blocknrs: trying to allocate log block %lu\n",
+			 search_start) ;
+	search_start++ ;
+	amount_needed++ ;
+	continue ;
+    }
+       
+
+    reiserfs_prepare_for_journal(s, SB_AP_BITMAP(s)[i], 1) ;
+
+#ifdef CONFIG_REISERFS_CHECK
+    if (buffer_locked (SB_AP_BITMAP (s)[i]) || is_reusable (s, search_start, 0) == 0)
+	reiserfs_panic (s, "vs-4140: reiserfs_new_blocknrs: bitmap block is locked or bad block number found");
+#endif
+
+    /* if this bit was already set, we've scheduled, and someone else
+    ** has allocated it.  loop around and try again
+    */
+    if (reiserfs_test_and_set_le_bit (j, SB_AP_BITMAP (s)[i]->b_data)) {
+	reiserfs_warning("vs-4150: reiserfs_new_blocknrs, block not free");
+	reiserfs_restore_prepared_buffer(s, SB_AP_BITMAP(s)[i]) ;
+	amount_needed++ ;
+	continue ;
+    }    
+    journal_mark_dirty (th, s, SB_AP_BITMAP (s)[i]); 
+    *free_blocknrs = search_start ;
+    free_blocknrs ++;
+  }
+
+  reiserfs_prepare_for_journal(s, SB_BUFFER_WITH_SB(s), 1) ;
+  /* update free block count in super block */
+  s->u.reiserfs_sb.s_rs->s_free_blocks = cpu_to_le32 (SB_FREE_BLOCKS (s) - init_amount_needed);
+  journal_mark_dirty (th, s, SB_BUFFER_WITH_SB (s));
+  s->s_dirt = 1;
+
+  return CARRY_ON;
+}
+
+// this is called only by get_empty_nodes with for_preserve_list==0
+int reiserfs_new_blocknrs (struct reiserfs_transaction_handle *th, unsigned long * free_blocknrs,
+			    unsigned long search_start, int amount_needed) {
+  return do_reiserfs_new_blocknrs(th, free_blocknrs, search_start, amount_needed, 0/*for_preserve_list-priority*/, 0/*for_formatted*/, 0/*for_prealloc */) ;
+}
+
+
+// called by get_new_buffer and by reiserfs_get_block with amount_needed == 1 and for_preserve_list == 0
+int reiserfs_new_unf_blocknrs(struct reiserfs_transaction_handle *th, unsigned long * free_blocknrs,
+			      unsigned long search_start) {
+  return do_reiserfs_new_blocknrs(th, free_blocknrs, search_start, 
+                                  1/*amount_needed*/,
+				  0/*for_preserve_list-priority*/, 
+				  1/*for formatted*/,
+				  0/*for prealloc */) ;
+}
+
+#ifdef REISERFS_PREALLOCATE
+
+/* 
+** We pre-allocate 8 blocks.  Pre-allocation is used for files > 16 KB only.
+** This lowers fragmentation on large files by grabbing a contiguous set of
+** blocks at once.  It also limits the number of times the bitmap block is
+** logged by making X number of allocation changes in a single transaction.
+**
+** We are using a border to divide the disk into two parts.  The first part
+** is used for tree blocks, which have a very high turnover rate (they
+** are constantly allocated then freed)
+**
+** The second part of the disk is for the unformatted nodes of larger files.
+** Putting them away from the tree blocks lowers fragmentation, and makes
+** it easier to group files together.  There are a number of different
+** allocation schemes being tried right now, each is documented below.
+**
+** A great deal of the allocator's speed comes because reiserfs_get_block
+** sends us the block number of the last unformatted node in the file.  Once
+** a given block is allocated past the border, we don't collide with the
+** blocks near the search_start again.
+** 
+*/
+int reiserfs_new_unf_blocknrs2 (struct reiserfs_transaction_handle *th, 
+				struct inode       * p_s_inode,
+				unsigned long      * free_blocknrs,
+				unsigned long        search_start)
+{
+  int ret=0, blks_gotten=0;
+  unsigned long border = 0;
+  unsigned long bstart = 0;
+  unsigned long hash_in, hash_out;
+  int allocated[PREALLOCATION_SIZE];
+  int blks;
+
+  if (!reiserfs_no_border(th->t_super)) {
+    /* we default to having the border at the 10% mark of the disk.  This
+    ** is an arbitrary decision and it needs tuning.  It also needs a limit
+    ** to prevent it from taking too much space on huge drives.
+    */
+    bstart = (SB_BLOCK_COUNT(th->t_super) / 10); 
+  }
+  if (!reiserfs_no_unhashed_relocation(th->t_super)) {
+    /* this is a very simple first attempt at preventing too much grouping
+    ** around the border value.  Since k_dir_id is never larger than the
+    ** highest allocated oid, it is far from perfect, and files will tend
+    ** to be grouped towards the start of the border
+    */
+    border = (INODE_PKEY(p_s_inode)->k_dir_id) % (SB_BLOCK_COUNT(th->t_super) - bstart - 1) ;
+  } else {
+    /* why would we want to delcare a local variable to this if statement
+    ** name border????? -chris
+    ** unsigned long border = 0;
+    */
+    if (!reiserfs_hashed_relocation(th->t_super)) {
+      hash_in = (INODE_PKEY(p_s_inode))->k_dir_id;
+				/* I wonder if the CPU cost of the
+                                   hash will obscure the layout
+                                   effect? Of course, whether that
+                                   effect is good or bad we don't
+                                   know.... :-) */
+      
+      hash_out = keyed_hash(((char *) (&hash_in)), 4);
+      border = hash_out % (SB_BLOCK_COUNT(th->t_super) - bstart - 1) ;
+    }
+  }
+  border += bstart ;
+  allocated[0] = 0 ; /* important.  Allows a check later on to see if at
+                      * least one block was allocated.  This prevents false
+		      * no disk space returns
+		      */
+
+  if ( (p_s_inode->i_size < 4 * 4096) || 
+       !(S_ISREG(p_s_inode->i_mode)) )
+    {
+      if ( search_start < border 
+	   || (
+				/* allow us to test whether it is a
+                                   good idea to prevent files from
+                                   getting too far away from their
+                                   packing locality by some unexpected
+                                   means.  This might be poor code for
+                                   directories whose files total
+                                   larger than 1/10th of the disk, and
+                                   it might be good code for
+                                   suffering from old insertions when the disk
+                                   was almost full. */
+               /* changed from !reiserfs_test3(th->t_super), which doesn't
+               ** seem like a good idea.  Think about adding blocks to
+               ** a large file.  If you've allocated 10% of the disk
+               ** in contiguous blocks, you start over at the border value
+               ** for every new allocation.  This throws away all the
+               ** information sent in about the last block that was allocated
+               ** in the file.  Not a good general case at all.
+               ** -chris
+               */
+	       reiserfs_test4(th->t_super) && 
+	       (search_start > border + (SB_BLOCK_COUNT(th->t_super) / 10))
+	       )
+	   )
+	search_start=border;
+  
+      ret = do_reiserfs_new_blocknrs(th, free_blocknrs, search_start, 
+				     1/*amount_needed*/, 
+				     0/*use reserved blocks for root */,
+				     1/*for_formatted*/,
+				     0/*for prealloc */) ;  
+      return ret;
+    }
+
+  /* take a block off the prealloc list and return it -Hans */
+  if (p_s_inode->u.reiserfs_i.i_prealloc_count > 0) {
+    p_s_inode->u.reiserfs_i.i_prealloc_count--;
+    *free_blocknrs = p_s_inode->u.reiserfs_i.i_prealloc_block++;
+    return ret;
+  }
+
+				/* else get a new preallocation for the file */
+  reiserfs_discard_prealloc (th, p_s_inode);
+  /* this uses the last preallocated block as the search_start.  discard
+  ** prealloc does not zero out this number.
+  */
+  if (search_start <= p_s_inode->u.reiserfs_i.i_prealloc_block) {
+    search_start = p_s_inode->u.reiserfs_i.i_prealloc_block;
+  }
+  
+  /* doing the compare again forces search_start to be >= the border,
+  ** even if the file already had prealloction done.  This seems extra,
+  ** and should probably be removed
+  */
+  if ( search_start < border ) search_start=border; 
+  
+  *free_blocknrs = 0;
+  blks = PREALLOCATION_SIZE-1;
+  for (blks_gotten=0; blks_gotten<PREALLOCATION_SIZE; blks_gotten++) {
+    ret = do_reiserfs_new_blocknrs(th, free_blocknrs, search_start, 
+				   1/*amount_needed*/, 
+				   0/*for root reserved*/,
+				   1/*for_formatted*/,
+				   (blks_gotten > 0)/*must_be_contiguous*/) ;
+    /* if we didn't find a block this time, adjust blks to reflect
+    ** the actual number of blocks allocated
+    */ 
+    if (ret != CARRY_ON) {
+      blks = blks_gotten > 0 ? (blks_gotten - 1) : 0 ;
+      break ;
+    }
+    allocated[blks_gotten]= *free_blocknrs;
+#ifdef CONFIG_REISERFS_CHECK
+    if ( (blks_gotten>0) && (allocated[blks_gotten] - allocated[blks_gotten-1]) != 1 ) {
+      /* this should be caught by new_blocknrs now, checking code */
+      reiserfs_warning("yura-1, reiserfs_new_unf_blocknrs2: pre-allocated not contiguous set of blocks!\n") ;
+      reiserfs_free_block(th, allocated[blks_gotten]);
+      blks = blks_gotten-1; 
+      break;
+    }
+#endif
+    if (blks_gotten==0) {
+      p_s_inode->u.reiserfs_i.i_prealloc_block = *free_blocknrs;
+    }
+    search_start = *free_blocknrs; 
+    *free_blocknrs = 0;
+  }
+  p_s_inode->u.reiserfs_i.i_prealloc_count = blks;
+  *free_blocknrs = p_s_inode->u.reiserfs_i.i_prealloc_block;
+  p_s_inode->u.reiserfs_i.i_prealloc_block++;
+
+  /* we did actually manage to get 1 block */
+  if (ret != CARRY_ON && allocated[0] > 0) {
+    return CARRY_ON ;
+  }
+  /* NO_MORE_UNUSED_CONTIGUOUS_BLOCKS should only mean something to
+  ** the preallocation code.  The rest of the filesystem asks for a block
+  ** and should either get it, or know the disk is full.  The code
+  ** above should never allow ret == NO_MORE_UNUSED_CONTIGUOUS_BLOCK,
+  ** as it doesn't send for_prealloc = 1 to do_reiserfs_new_blocknrs
+  ** unless it has already successfully allocated at least one block.
+  ** Just in case, we translate into a return value the rest of the
+  ** filesystem can understand.
+  **
+  ** It is an error to change this without making the
+  ** rest of the filesystem understand NO_MORE_UNUSED_CONTIGUOUS_BLOCKS
+  ** If you consider it a bug to return NO_DISK_SPACE here, fix the rest
+  ** of the fs first.
+  */
+  if (ret == NO_MORE_UNUSED_CONTIGUOUS_BLOCKS) {
+#ifdef CONFIG_REISERFS_CHECK
+    reiserfs_warning("reiser-2015: this shouldn't happen, may cause false out of disk space error");
+#endif
+     return NO_DISK_SPACE; 
+  }
+  return ret;
+}
+
+//
+// a portion of this function, was derived from minix or ext2's
+// analog. You should be able to tell which portion by looking at the
+// ext2 code and comparing. 
+
+void reiserfs_discard_prealloc (struct reiserfs_transaction_handle *th, 
+				struct inode * inode)
+{
+    if (inode->u.reiserfs_i.i_prealloc_count > 0) {
+      while (inode->u.reiserfs_i.i_prealloc_count--) {
+	reiserfs_free_block(th,inode->u.reiserfs_i.i_prealloc_block);
+	inode->u.reiserfs_i.i_prealloc_block++;
+      }
+    }
+    inode->u.reiserfs_i.i_prealloc_count = 0;
+}
+#endif

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)