Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 38660
b: refs/heads/master
c: 61e085a
h: refs/heads/master
v: v3
  • Loading branch information
Steven Whitehouse committed Apr 24, 2006
1 parent 6492c3f commit fcd7643
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 73 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 1e09ae544eb1a2d11b04c6924f738a310c7a7a2b
refs/heads/master: 61e085a88cb59232eb8ff5b446d70491c7bf2c68
2 changes: 1 addition & 1 deletion trunk/fs/gfs2/bmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ int gfs2_unstuff_dinode(struct gfs2_inode *ip, gfs2_unstuffer_t unstuffer,
if (isdir) {
block = gfs2_alloc_meta(ip);

error = gfs2_dir_get_buffer(ip, block, 1, &bh);
error = gfs2_dir_get_new_buffer(ip, block, &bh);
if (error)
goto out_brelse;
gfs2_buffer_copy_tail(bh,
Expand Down
144 changes: 75 additions & 69 deletions trunk/fs/gfs2/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,61 +8,59 @@
*/

/*
* Implements Extendible Hashing as described in:
* "Extendible Hashing" by Fagin, et al in
* __ACM Trans. on Database Systems__, Sept 1979.
*
*
* Here's the layout of dirents which is essentially the same as that of ext2
* within a single block. The field de_name_len is the number of bytes
* actually required for the name (no null terminator). The field de_rec_len
* is the number of bytes allocated to the dirent. The offset of the next
* dirent in the block is (dirent + dirent->de_rec_len). When a dirent is
* deleted, the preceding dirent inherits its allocated space, ie
* prev->de_rec_len += deleted->de_rec_len. Since the next dirent is obtained
* by adding de_rec_len to the current dirent, this essentially causes the
* deleted dirent to get jumped over when iterating through all the dirents.
*
* When deleting the first dirent in a block, there is no previous dirent so
* the field de_ino is set to zero to designate it as deleted. When allocating
* a dirent, gfs2_dirent_alloc iterates through the dirents in a block. If the
* first dirent has (de_ino == 0) and de_rec_len is large enough, this first
* dirent is allocated. Otherwise it must go through all the 'used' dirents
* searching for one in which the amount of total space minus the amount of
* used space will provide enough space for the new dirent.
*
* There are two types of blocks in which dirents reside. In a stuffed dinode,
* the dirents begin at offset sizeof(struct gfs2_dinode) from the beginning of
* the block. In leaves, they begin at offset sizeof(struct gfs2_leaf) from the
* beginning of the leaf block. The dirents reside in leaves when
*
* dip->i_di.di_flags & GFS2_DIF_EXHASH is true
*
* Otherwise, the dirents are "linear", within a single stuffed dinode block.
*
* When the dirents are in leaves, the actual contents of the directory file are
* used as an array of 64-bit block pointers pointing to the leaf blocks. The
* dirents are NOT in the directory file itself. There can be more than one block
* pointer in the array that points to the same leaf. In fact, when a directory
* is first converted from linear to exhash, all of the pointers point to the
* same leaf.
*
* When a leaf is completely full, the size of the hash table can be
* doubled unless it is already at the maximum size which is hard coded into
* GFS2_DIR_MAX_DEPTH. After that, leaves are chained together in a linked list,
* but never before the maximum hash table size has been reached.
*/
* Implements Extendible Hashing as described in:
* "Extendible Hashing" by Fagin, et al in
* __ACM Trans. on Database Systems__, Sept 1979.
*
*
* Here's the layout of dirents which is essentially the same as that of ext2
* within a single block. The field de_name_len is the number of bytes
* actually required for the name (no null terminator). The field de_rec_len
* is the number of bytes allocated to the dirent. The offset of the next
* dirent in the block is (dirent + dirent->de_rec_len). When a dirent is
* deleted, the preceding dirent inherits its allocated space, ie
* prev->de_rec_len += deleted->de_rec_len. Since the next dirent is obtained
* by adding de_rec_len to the current dirent, this essentially causes the
* deleted dirent to get jumped over when iterating through all the dirents.
*
* When deleting the first dirent in a block, there is no previous dirent so
* the field de_ino is set to zero to designate it as deleted. When allocating
* a dirent, gfs2_dirent_alloc iterates through the dirents in a block. If the
* first dirent has (de_ino == 0) and de_rec_len is large enough, this first
* dirent is allocated. Otherwise it must go through all the 'used' dirents
* searching for one in which the amount of total space minus the amount of
* used space will provide enough space for the new dirent.
*
* There are two types of blocks in which dirents reside. In a stuffed dinode,
* the dirents begin at offset sizeof(struct gfs2_dinode) from the beginning of
* the block. In leaves, they begin at offset sizeof(struct gfs2_leaf) from the
* beginning of the leaf block. The dirents reside in leaves when
*
* dip->i_di.di_flags & GFS2_DIF_EXHASH is true
*
* Otherwise, the dirents are "linear", within a single stuffed dinode block.
*
* When the dirents are in leaves, the actual contents of the directory file are
* used as an array of 64-bit block pointers pointing to the leaf blocks. The
* dirents are NOT in the directory file itself. There can be more than one
* block pointer in the array that points to the same leaf. In fact, when a
* directory is first converted from linear to exhash, all of the pointers
* point to the same leaf.
*
* When a leaf is completely full, the size of the hash table can be
* doubled unless it is already at the maximum size which is hard coded into
* GFS2_DIR_MAX_DEPTH. After that, leaves are chained together in a linked list,
* but never before the maximum hash table size has been reached.
*/

#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/completion.h>
#include <linux/buffer_head.h>
#include <linux/sort.h>
#include <linux/gfs2_ondisk.h>
#include <linux/crc32.h>
#include <linux/vmalloc.h>
#include <asm/semaphore.h>

#include "gfs2.h"
#include "lm_interface.h"
Expand Down Expand Up @@ -92,33 +90,36 @@ typedef int (*leaf_call_t) (struct gfs2_inode *dip,
uint32_t index, uint32_t len, uint64_t leaf_no,
void *data);

int gfs2_dir_get_buffer(struct gfs2_inode *ip, uint64_t block, int new,
struct buffer_head **bhp)

int gfs2_dir_get_new_buffer(struct gfs2_inode *ip, uint64_t block,
struct buffer_head **bhp)
{
struct buffer_head *bh;
int error = 0;

if (new) {
bh = gfs2_meta_new(ip->i_gl, block);
gfs2_trans_add_bh(ip->i_gl, bh, 1);
gfs2_metatype_set(bh, GFS2_METATYPE_JD, GFS2_FORMAT_JD);
gfs2_buffer_clear_tail(bh, sizeof(struct gfs2_meta_header));
} else {
error = gfs2_meta_read(ip->i_gl, block, DIO_START | DIO_WAIT,
&bh);
if (error)
return error;
if (gfs2_metatype_check(ip->i_sbd, bh, GFS2_METATYPE_JD)) {
brelse(bh);
return -EIO;
}
}

bh = gfs2_meta_new(ip->i_gl, block);
gfs2_trans_add_bh(ip->i_gl, bh, 1);
gfs2_metatype_set(bh, GFS2_METATYPE_JD, GFS2_FORMAT_JD);
gfs2_buffer_clear_tail(bh, sizeof(struct gfs2_meta_header));
*bhp = bh;
return 0;
}

static int gfs2_dir_get_existing_buffer(struct gfs2_inode *ip, uint64_t block,
struct buffer_head **bhp)
{
struct buffer_head *bh;
int error;

error = gfs2_meta_read(ip->i_gl, block, DIO_START | DIO_WAIT, &bh);
if (error)
return error;
if (gfs2_metatype_check(ip->i_sbd, bh, GFS2_METATYPE_JD)) {
brelse(bh);
return -EIO;
}
*bhp = bh;
return 0;
}

static int gfs2_dir_write_stuffed(struct gfs2_inode *ip, const char *buf,
unsigned int offset, unsigned int size)
Expand Down Expand Up @@ -205,9 +206,11 @@ static int gfs2_dir_write_data(struct gfs2_inode *ip, const char *buf,
goto fail;
}

error = gfs2_dir_get_buffer(ip, dblock,
(amount == sdp->sd_jbsize) ?
1 : new, &bh);
if (amount == sdp->sd_jbsize || new)
error = gfs2_dir_get_new_buffer(ip, dblock, &bh);
else
error = gfs2_dir_get_existing_buffer(ip, dblock, &bh);

if (error)
goto fail;

Expand Down Expand Up @@ -321,7 +324,10 @@ static int gfs2_dir_read_data(struct gfs2_inode *ip, char *buf,
gfs2_meta_ra(ip->i_gl, dblock, extlen);

if (dblock) {
error = gfs2_dir_get_buffer(ip, dblock, new, &bh);
if (new)
error = gfs2_dir_get_new_buffer(ip, dblock, &bh);
else
error = gfs2_dir_get_existing_buffer(ip, dblock, &bh);
if (error)
goto fail;
dblock++;
Expand Down
4 changes: 2 additions & 2 deletions trunk/fs/gfs2/dir.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ int gfs2_dir_exhash_dealloc(struct gfs2_inode *dip);

int gfs2_diradd_alloc_required(struct inode *dir,
const struct qstr *filename);
int gfs2_dir_get_buffer(struct gfs2_inode *ip, uint64_t block, int new,
struct buffer_head **bhp);
int gfs2_dir_get_new_buffer(struct gfs2_inode *ip, uint64_t block,
struct buffer_head **bhp);

static inline uint32_t gfs2_disk_hash(const char *data, int len)
{
Expand Down

0 comments on commit fcd7643

Please sign in to comment.