CVSROOT: /cvs/cluster Module name: cluster Branch: RHEL5 Changes by: [EMAIL PROTECTED] 2008-01-24 18:29:13
Modified files: gfs/gfs_quota : gfs_quota.h main.c Makefile Added files: gfs/gfs_quota : layout.c Log message: fix for bug 430133 - We don't run through the entire gfs_quota sparse file to do a list operation anymore. We get the layout of the gfs_quota file on disk and only read quota information off the data blocks that are actually in use. This is the userland (gfs_quota) part of the fix. There is another accompanying gfs-kernel fix (bz 430134) Patches: http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/gfs/gfs_quota/layout.c.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=NONE&r2=1.3.2.1 http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/gfs/gfs_quota/gfs_quota.h.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=1.2&r2=1.2.16.1 http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/gfs/gfs_quota/main.c.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=1.3&r2=1.3.2.1 http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/gfs/gfs_quota/Makefile.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=1.5&r2=1.5.2.1 /cvs/cluster/cluster/gfs/gfs_quota/layout.c,v --> standard output revision 1.3.2.1 --- cluster/gfs/gfs_quota/layout.c +++ - 2008-01-24 18:29:13.782331000 +0000 @@ -0,0 +1,614 @@ +/****************************************************************************** +******************************************************************************* +** +** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. +** Copyright (C) 2004 Red Hat, Inc. All rights reserved. +** +** This copyrighted material is made available to anyone wishing to use, +** modify, copy, or redistribute it subject to the terms and conditions +** of the GNU General Public License v.2. +** +******************************************************************************* +******************************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <inttypes.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <limits.h> +#include <errno.h> + +#define __user +#include <linux/gfs_ioctl.h> +#include <linux/gfs_ondisk.h> + +#include "osi_list.h" +#include "linux_endian.h" + +#include "gfs_quota.h" + +#define LAYOUT_DATA_QUANTUM (4194304) + +extern void +print_quota(commandline_t *comline, + int user, uint32_t id, + struct gfs_quota *q, + struct gfs_sb *sb); + +struct extent { + osi_list_t list; + + uint64_t offset; + uint64_t start; + unsigned int len; +}; +typedef struct extent extent_t; + +struct buffer { + osi_list_t list; + uint64_t blkno; + char *data; + + int touched; +}; +typedef struct buffer buffer_t; + +struct lblk_range { + osi_list_t list; + + uint64_t start; + unsigned int len; +}; +typedef struct lblk_range lblk_range_t; + +struct world { + char *buf_data; + unsigned int buf_size; + int buf_count; + osi_list_t blist; + osi_list_t elist; + + struct gfs_sb sb; + unsigned int diptrs; + unsigned int inptrs; + unsigned int jbsize; + unsigned int hash_bsize; + unsigned int hash_ptrs; + + buffer_t *dibh; + struct gfs_dinode di; +}; +typedef struct world world_t; + +typedef void (*pointer_call_t) (world_t *w, + unsigned int height, uint64_t bn, void *data); + +static lblk_range_t *lblk_range_list; +static unsigned int j_blk_size; + +/** + * add_lblk_range - Add a range of logical block numbers to lblk_list + * @lblk_list: the list to add to + * @start: the starting block number of the range + * @len: the length of the range + * + */ + +static void +add_lblk_range(lblk_range_t *lblk_list, uint64_t start, unsigned int len) +{ + lblk_range_t *tmp; + + tmp = malloc(sizeof(lblk_range_t)); + if (!tmp) + die("out of memory\n"); + + tmp->start = start; + tmp->len = len; + + osi_list_add_prev(&tmp->list, &lblk_list->list); +} + +/** + * build_list - build a list of buffer_t's to represent the data from the kernel + * @w: the world structure + * + */ + +static void +build_list(world_t *w) +{ + buffer_t *b; + unsigned int x; + + for (x = 0; x < w->buf_count; x += sizeof(uint64_t) + w->sb.sb_bsize) { + b = malloc(sizeof(buffer_t)); + if (!b) + die("out of memory\n"); + + memset(b, 0, sizeof(buffer_t)); + + b->blkno = *(uint64_t *) (w->buf_data + x); + b->data = w->buf_data + x + sizeof(uint64_t); + + osi_list_add_prev(&b->list, &w->blist); + } + + if (x != w->buf_count) + die("the kernel passed back unaligned data\n"); +} + +/** + * check_list - check the buffers passed back by the kernel + * @w: the world + * + */ + +static void +check_list(world_t *w) +{ + osi_list_t *tmp; + buffer_t *b; + struct gfs_meta_header mh; + char *type; + + for (tmp = w->blist.next; tmp != &w->blist; tmp = tmp->next) { + b = osi_list_entry(tmp, buffer_t, list); + + gfs_meta_header_in(&mh, b->data); + + if (mh.mh_magic != GFS_MAGIC) + die("bad magic number on block\n"); + + switch (mh.mh_type) { + case GFS_METATYPE_DI: + type = "GFS_METATYPE_DI"; + + if (w->dibh) + die("more than one dinode in file\n"); + else { + w->dibh = b; + gfs_dinode_in(&w->di, b->data); + + b->touched = TRUE; + } + break; + case GFS_METATYPE_IN: + type = "GFS_METATYPE_IN"; + break; + case GFS_METATYPE_LF: + type = "GFS_METATYPE_LF"; + break; + case GFS_METATYPE_JD: + type = "GFS_METATYPE_JD"; + break; + case GFS_METATYPE_EA: + type = "GFS_METATYPE_EA"; + break; + case GFS_METATYPE_ED: + die("GFS_METATYPE_ED shouldn't be present\n"); + default: + die("strange meta type\n"); + } + } + + if (!w->dibh) + die("no dinode\n"); +} + +/** + * getbuf - get the buffer_t for a given block number + * @w: the world + * @blkno: the block number + * + * Returns: the buffer_t + */ + +static buffer_t * +getbuf(world_t *w, uint64_t blkno) +{ + osi_list_t *tmp; + buffer_t *b; + + for (tmp = w->blist.next; tmp != &w->blist; tmp = tmp->next) { + b = osi_list_entry(tmp, buffer_t, list); + if (b->blkno == blkno) { + osi_list_del(&b->list); + osi_list_add(&b->list, &w->blist); + + b->touched = TRUE; + + return b; + } + } + + die("buffer not found\n"); +} + +/** + * recursive_scan - call a function for each block pointer in a file + * @w: the world + * @height: the height of the block being pointed to + * @block: the block being pointed to + * @pc: the function to call + * @data: private data for the @pc function + * + */ + +static void +recursive_scan(world_t *w, unsigned int height, + uint64_t block, pointer_call_t pc, void *data) +{ + buffer_t *b = NULL; + uint64_t *top, *bottom; + uint64_t bn; + + if (!height) { + b = w->dibh; + + top = (uint64_t *) (b->data + sizeof(struct gfs_dinode)); + bottom = + (uint64_t *) (b->data + sizeof(struct gfs_dinode)) + + w->diptrs; + } else { + b = getbuf(w, block); + + top = (uint64_t *) (b->data + sizeof(struct gfs_indirect)); + bottom = + (uint64_t *) (b->data + sizeof(struct gfs_indirect)) + + w->inptrs; + } + + for (; top < bottom; top++) { + bn = gfs64_to_cpu(*top); + + pc(w, height, bn, data); + + if (bn && height < w->di.di_height - 1) + recursive_scan(w, height + 1, bn, pc, data); + } +} + +/** + * add_extent - add an extend to the list of the file's data extents + * @w: the world + * @offset: the starting logical block of the extent + * @start: the starting disk block of the extent + * @len: the number of blocks in the extent + * + */ + +static void +add_extent(world_t *w, uint64_t offset, uint64_t start, unsigned int len) +{ + extent_t *e; + + e = malloc(sizeof(extent_t)); + if (!e) + die("out of memory\n"); + + memset(e, 0, sizeof(extent_t)); + + e->offset = offset; + e->start = start; + e->len = len; + + osi_list_add_prev(&e->list, &w->elist); +} + +struct do_pf_s { + unsigned int height; + uint64_t offset; + uint64_t start; + uint64_t skip; + unsigned int len; +}; +typedef struct do_pf_s do_pf_t; + +/** + * do_pf: called for every pointer in the file (prints/collects extent info) + * @w: the world + * @height: the height of the block containing the pointer + * @bn: the contents of the pointer + * @data: a do_pf_t structure + * + */ + +static void +do_pf(world_t *w, unsigned int height, uint64_t bn, void *data) +{ + do_pf_t *pf = (do_pf_t *) data; + unsigned int x; + uint64_t skip; + + if (pf->height < height + 1) + return; + + if (!bn) { + if (pf->height == height + 1) + pf->skip++; + else { + x = pf->height - height - 1; + skip = w->inptrs; + while (--x) + skip *= w->inptrs; + pf->skip += skip; + } + + return; + } + + if (pf->height == height + 1) { + if (pf->start + pf->len == bn && pf->len == pf->skip) { + pf->len++; + pf->skip++; + } else { + if (pf->start) { + if (pf->height == w->di.di_height) { + add_extent(w, pf->offset, pf->start, + pf->len); + add_lblk_range(lblk_range_list, + pf->offset, pf->len); + } + } + + pf->offset += pf->skip; + pf->start = bn; + pf->len = 1; + pf->skip = 1; + } + } +} + +/** + * get_dblk_ranges - Run through the file and add valid data block ranges to + * lblk_range_list + * @w: the world + * + */ + +static void +get_dblk_ranges(world_t *w) +{ + do_pf_t pf; + unsigned int h; + + for (h = 1; h <= w->di.di_height; h++) { + + memset(&pf, 0, sizeof(do_pf_t)); + pf.height = h; + + recursive_scan(w, 0, 0, do_pf, &pf); + + if (pf.start) { + if (h == w->di.di_height) { + add_extent(w, pf.offset, pf.start, pf.len); + add_lblk_range(lblk_range_list, pf.offset, + pf.len); + } + } + } +} + +/** + * compute_layout - Computes the layout and store the valid data page ranges + * in lblk_range_list + * @argc: + * @argv: + * + */ + +void +compute_layout(char *path) +{ + world_t w; + int fd; + int retry = TRUE; + struct gfs_ioctl gi; + int error; + + memset(&w, 0, sizeof(world_t)); + w.buf_size = LAYOUT_DATA_QUANTUM; + osi_list_init(&w.blist); + osi_list_init(&w.elist); + + + fd = open(path, O_RDONLY); + if (fd < 0) + die("can't open %s: %s\n", path, strerror(errno)); + + check_for_gfs(fd, path); + + { + char *argv[] = { "get_super" }; + + gi.gi_argc = 1; + gi.gi_argv = argv; + gi.gi_data = (char *)&w.sb; + gi.gi_size = sizeof(struct gfs_sb); + + error = ioctl(fd, GFS_IOCTL_SUPER, &gi); + if (error != gi.gi_size) + die("error doing get_super (%d): %s\n", + error, strerror(errno)); + } + + w.diptrs = (w.sb.sb_bsize - sizeof(struct gfs_dinode)) / + sizeof(uint64_t); + w.inptrs = (w.sb.sb_bsize - sizeof(struct gfs_indirect)) / + sizeof(uint64_t); + w.jbsize = w.sb.sb_bsize - sizeof(struct gfs_meta_header); + j_blk_size = w.jbsize; + w.hash_bsize = w.sb.sb_bsize / 2; + w.hash_ptrs = w.hash_bsize / sizeof(uint64_t); + + for (;;) { + char *argv[] = { "get_file_meta_quota" }; + + w.buf_data = malloc(w.buf_size); + if (!w.buf_data) + die("out of memory\n"); + + gi.gi_argc = 1; + gi.gi_argv = argv; + gi.gi_data = w.buf_data; + gi.gi_size = w.buf_size; + + w.buf_count = ioctl(fd, GFS_IOCTL_SUPER, &gi); + if (w.buf_count >= 0) + break; + + if (errno == ENOMEM) { + if (retry) { + free(w.buf_data); + w.buf_size += LAYOUT_DATA_QUANTUM; + continue; + } else + die("%u bytes isn't enough memory\n", + w.buf_size); + } + die("error doing get_file_meta: %s\n", + strerror(errno)); + } + + build_list(&w); + check_list(&w); + + get_dblk_ranges(&w); + + close(fd); +} + +/** + * print_quota_range - print the quota information in the given sequence of + * blocks + * @argc: + * @argv: + * + */ +void +print_quota_range(commandline_t *comline, int type, uint64_t start, + unsigned int len) +{ + uint64_t blk_offt, blk_end_offt, offset; + uint64_t quo; + unsigned int rem; + int fd; + struct gfs_sb sb; + struct gfs_ioctl gi; + char buf[sizeof(struct gfs_quota)]; + struct gfs_quota q; + uint64_t hidden_blocks = 0; + uint32_t id; + int error; + + blk_offt = start * (uint64_t)j_blk_size; + blk_end_offt = blk_offt + (uint64_t)(len * j_blk_size); + + quo = blk_offt / (uint64_t)(2 * sizeof(struct gfs_quota)); + rem = blk_offt % (uint64_t)(2 * sizeof(struct gfs_quota)); + + offset = type == GQ_ID_USER ? blk_offt : + blk_offt + sizeof(struct gfs_quota); + + if (rem && type == GQ_ID_USER) + offset = (quo + 1) * (uint64_t)(2 * sizeof(struct gfs_quota)); + + if (rem && type == GQ_ID_GROUP) { + if (rem <= sizeof(struct gfs_quota)) + offset = quo * (uint64_t)(2 * sizeof(struct gfs_quota)) + + sizeof(struct gfs_quota); + else + offset = (quo + 1) * + (uint64_t)(2 * sizeof(struct gfs_quota)) + + sizeof(struct gfs_quota); + } + + fd = open(comline->filesystem, O_RDONLY); + if (fd < 0) + die("can't open file %s: %s\n", comline->filesystem, + strerror(errno)); + + check_for_gfs(fd, comline->filesystem); + do_get_super(fd, &sb); + + if (comline->no_hidden_file_blocks) + hidden_blocks = compute_hidden_blocks(comline, fd); + + do { + char *argv[] = { "do_hfile_read", "quota" }; + + gi.gi_argc = 2; + gi.gi_argv = argv; + gi.gi_data = buf; + gi.gi_size = sizeof(struct gfs_quota); + gi.gi_offset = offset; + + memset(buf, 0, sizeof(struct gfs_quota)); + + error = ioctl(fd, GFS_IOCTL_SUPER, &gi); + if (error < 0) + die("can't read quota file: %s\n", + strerror(errno)); + + gfs_quota_in(&q, buf); + + id = (offset / sizeof(struct gfs_quota)) >> 1; + if (!id && comline->no_hidden_file_blocks) + q.qu_value -= hidden_blocks; + + if (q.qu_limit || q.qu_warn || q.qu_value) + print_quota(comline, type == GQ_ID_GROUP ? FALSE : TRUE, + id, &q, &sb); + + offset += 2 * sizeof(struct gfs_quota); + } while ((error == sizeof(struct gfs_quota)) && + (offset < blk_end_offt)); + + close(fd); +} + +void +print_quota_file(commandline_t *comline) +{ + lblk_range_t lblk_range, *bar; + osi_list_t *t, *x; + + lblk_range_list = &lblk_range; + + osi_list_init(&lblk_range_list->list); + + compute_layout(comline->filesystem); + + if (osi_list_empty(&lblk_range_list->list)) { + /* stuffed quota inode */ + print_quota_range(comline, GQ_ID_USER, 0, 1); + print_quota_range(comline, GQ_ID_GROUP, 0, 1); + } else { + /* print the user quotas */ + osi_list_foreach(t, &lblk_range_list->list) { + bar = osi_list_entry(t, lblk_range_t, list); + print_quota_range(comline, GQ_ID_USER, bar->start, + bar->len); + } + /* print the group quotas */ + osi_list_foreach(t, &lblk_range_list->list) { + bar = osi_list_entry(t, lblk_range_t, list); + print_quota_range(comline, GQ_ID_GROUP, bar->start, + bar->len); + } + /* free the list */ + osi_list_foreach_safe(t, &lblk_range_list->list, x) { + bar = osi_list_entry(t, lblk_range_t, list); + osi_list_del(&bar->list); + free(bar); + } + } +} + --- cluster/gfs/gfs_quota/gfs_quota.h 2005/01/04 10:07:09 1.2 +++ cluster/gfs/gfs_quota/gfs_quota.h 2008/01/24 18:29:13 1.2.16.1 @@ -101,4 +101,7 @@ uint32_t name_to_id(int user, char *name, int numbers); char *id_to_name(int user, uint32_t id, int numbers); +/* layout.c */ + +void print_quota_file(commandline_t *comline); #endif /* __GFS_QUOTA_DOT_H__ */ --- cluster/gfs/gfs_quota/main.c 2006/08/21 21:44:28 1.3 +++ cluster/gfs/gfs_quota/main.c 2008/01/24 18:29:13 1.3.2.1 @@ -345,7 +345,7 @@ * */ -static void +void print_quota(commandline_t *comline, int user, uint32_t id, struct gfs_quota *q, @@ -402,68 +402,7 @@ static void do_list(commandline_t *comline) { - int fd; - struct gfs_sb sb; - struct gfs_ioctl gi; - char buf[sizeof(struct gfs_quota)]; - struct gfs_quota q; - uint64_t offset; - uint64_t hidden_blocks = 0; - uint32_t id; - int pass = 0; - int error; - - if (!*comline->filesystem) - die("need a filesystem to work on\n"); - - fd = open(comline->filesystem, O_RDONLY); - if (fd < 0) - die("can't open file %s: %s\n", comline->filesystem, - strerror(errno)); - - check_for_gfs(fd, comline->filesystem); - do_get_super(fd, &sb); - - if (comline->no_hidden_file_blocks) - hidden_blocks = compute_hidden_blocks(comline, fd); - - for (pass = 0; pass < 2; pass++) { - if (!pass) - offset = 0; - else - offset = sizeof(struct gfs_quota); - - do { - char *argv[] = { "do_hfile_read", "quota" }; - - gi.gi_argc = 2; - gi.gi_argv = argv; - gi.gi_data = buf; - gi.gi_size = sizeof(struct gfs_quota); - gi.gi_offset = offset; - - memset(buf, 0, sizeof(struct gfs_quota)); - - error = ioctl(fd, GFS_IOCTL_SUPER, &gi); - if (error < 0) - die("can't read quota file: %s\n", - strerror(errno)); - - gfs_quota_in(&q, buf); - - id = (offset / sizeof(struct gfs_quota)) >> 1; - if (!id && comline->no_hidden_file_blocks) - q.qu_value -= hidden_blocks; - - if (q.qu_limit || q.qu_warn || q.qu_value) - print_quota(comline, (pass) ? FALSE : TRUE, id, - &q, &sb); - - offset += 2 * sizeof(struct gfs_quota); - } while (error == sizeof(struct gfs_quota)); - } - - close(fd); + print_quota_file(comline); } /** --- cluster/gfs/gfs_quota/Makefile 2006/08/11 15:18:11 1.5 +++ cluster/gfs/gfs_quota/Makefile 2008/01/24 18:29:13 1.5.2.1 @@ -19,7 +19,8 @@ check.c \ main.c \ names.c \ - ondisk.c + ondisk.c \ + layout.c CFLAGS+= -O2 -DHELPER_PROGRAM -D_FILE_OFFSET_BITS=64 \ -DGFS_RELEASE_NAME=\"${RELEASE}\"