[PATCH 08/14] GFS: diaper device The diaper device is a block device within gfs that gets transparently inserted between the real device the and rest of the filesystem. This patch also includes optional code to help in memory debugging. Signed-off-by: Ken Preslan Signed-off-by: David Teigland --- fs/gfs2/debug.c | 210 ++++++++++++++++++ fs/gfs2/debug.h | 49 ++++ fs/gfs2/diaper.c | 633 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/gfs2/diaper.h | 27 ++ 4 files changed, 919 insertions(+) --- a/fs/gfs2/diaper.c 1970-01-01 07:30:00.000000000 +0730 +++ b/fs/gfs2/diaper.c 2005-08-01 14:13:08.008808352 +0800 @@ -0,0 +1,633 @@ +/****************************************************************************** +******************************************************************************* +** +** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. +** Copyright (C) 2004-2005 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. +** +******************************************************************************* +******************************************************************************/ + +/* + * When a GFS node encounters a serious problem (a filesystem inconsistency or + * a fatal I/O error), it needs a way to gracefully stop accessing the + * filesystem. This is more complicated to do for a cluster filesystem than a + * local filesystem. Locks must be freed. The node's journal must be replayed + * by another node in the cluster. That journal replay shouldn't require the + * departing node to be fenced. + * + * GFS uses three tools to do this. + * + * 1) The diaper device is a block device within GFS that gets + * transparently inserted between the real device the and rest of the + * filesystem. It creates a single function that touches all GFS disk + * I/O as it goes by. This allows GFS to easily keep a count of the + * number of outstanding disk I/O requests. It also allows GFS to + * easily stop any new I/O from being sent to the disk. + * + * 2) The file lm.c contains wrappers for all the lock module calls. + * They allow GFS to easily stop any new requests from being sent to + * the lock module. + * + * 3) There is one lock module call, lm_withdraw, performs the action of + * shutting down the filesystem mount on that node. At that point, + * the filesystem is making two guarantees: A) that it isn't currently + * modifying the disk and never will again and B) it will never again + * call back into the lock module. The lock module then cleans up any + * network I/O, frees any used memory, and tells the cluster that this + * mount is leaving and all recovery steps *except fencing* should + * occur. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gfs2.h" +#include "diaper.h" +#include "ops_fstype.h" + +struct diaper_holder { + struct list_head dh_list; + unsigned int dh_count; + + struct gendisk *dh_gendisk; + + struct block_device *dh_real; + struct block_device *dh_diaper; + + struct gfs2_sbd *dh_sbd; + mempool_t *dh_mempool; + struct super_block *dh_dummy_sb; +}; + +struct bio_wrapper { + struct bio *bw_orig; + struct diaper_holder *bw_dh; +}; + +static int diaper_major = 0; +static LIST_HEAD(diaper_list); +static spinlock_t diaper_lock; +static DEFINE_IDR(diaper_idr); +kmem_cache_t *diaper_slab; + +/** + * diaper_open - + * @inode: + * @file: + * + * Don't allow these devices to be opened from userspace + * or from other kernel routines. They should only be opened + * from this file. + * + * Returns: -EOPNOTSUPP + */ + +static int diaper_open(struct inode *inode, struct file *file) +{ + return -EOPNOTSUPP; +} + +static struct block_device_operations diaper_fops = { + .owner = THIS_MODULE, + .open = diaper_open, +}; + +/** + * diaper_end_io - Called at the end of a block I/O + * @bio: + * @bytes_done: + * @error: + * + * Interrupt context + * + * Returns: an integer thats usually discarded + */ + +static int diaper_end_io(struct bio *bio, unsigned int bytes_done, int error) +{ + struct bio_wrapper *bw = (struct bio_wrapper *)bio->bi_private; + struct diaper_holder *dh = bw->bw_dh; + struct gfs2_sbd *sdp = dh->dh_sbd; + + bio_endio(bw->bw_orig, bytes_done, error); + if (bio->bi_size) + return 1; + + atomic_dec(&sdp->sd_bio_outstanding); + bio_put(bio); + mempool_free(bw, dh->dh_mempool); + + return 0; +} + +/** + * diaper_make_request - + * @q: + * @bio: + * + * Returns: 0 + */ + +static int diaper_make_request(request_queue_t *q, struct bio *bio) +{ + struct diaper_holder *dh = (struct diaper_holder *)q->queuedata; + struct gfs2_sbd *sdp = dh->dh_sbd; + struct bio_wrapper *bw; + struct bio *bi; + + atomic_inc(&sdp->sd_bio_outstanding); + if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags))) { + atomic_dec(&sdp->sd_bio_outstanding); + bio_endio(bio, bio->bi_size, 0); + return 0; + } + if (bio_rw(bio) == WRITE) + atomic_add(bio->bi_size >> 9, &sdp->sd_bio_writes); + else + atomic_add(bio->bi_size >> 9, &sdp->sd_bio_reads); + + bw = mempool_alloc(dh->dh_mempool, GFP_NOIO); + bw->bw_orig = bio; + bw->bw_dh = dh; + + bi = bio_clone(bio, GFP_NOIO); + bi->bi_bdev = dh->dh_real; + bi->bi_end_io = diaper_end_io; + bi->bi_private = bw; + + generic_make_request(bi); + + return 0; +} + +/** + * minor_get - + * + * Returns: a unused minor number + */ + +static int minor_get(void) +{ + int minor; + int error; + + for (;;) { + if (!idr_pre_get(&diaper_idr, GFP_KERNEL)) + return -ENOMEM; + + spin_lock(&diaper_lock); + error = idr_get_new(&diaper_idr, NULL, &minor); + spin_unlock(&diaper_lock); + + if (!error) + break; + if (error != -EAGAIN) + return error; + } + + return minor; +} + +/** + * minor_put - Free a used minor number + * @minor: + * + */ + +static void minor_put(int minor) +{ + spin_lock(&diaper_lock); + idr_remove(&diaper_idr, minor); + spin_unlock(&diaper_lock); +} + +/** + * gfs2_dummy_write_super_lockfs - pass a freeze from real device to the diaper + * @sb: the real device's dummy sb + * + */ + +static void gfs2_dummy_write_super_lockfs(struct super_block *sb) +{ + struct diaper_holder *dh = (struct diaper_holder *)sb->s_fs_info; + freeze_bdev(dh->dh_diaper); +} + +/** + * gfs2_dummy_unlockfs - pass a thaw from the real device to the diaper + * @sb: the real device's dummy sb + * + */ + +static void gfs2_dummy_unlockfs(struct super_block *sb) +{ + struct diaper_holder *dh = (struct diaper_holder *)sb->s_fs_info; + thaw_bdev(dh->dh_diaper, dh->dh_sbd->sd_vfs); +} + +struct super_operations gfs2_dummy_sops = { + .write_super_lockfs = gfs2_dummy_write_super_lockfs, + .unlockfs = gfs2_dummy_unlockfs, +}; + +/** + * gfs2_dummy_sb - create a dummy superblock for the real device + * @dh: + * + * Returns: errno + */ + +static int get_dummy_sb(struct diaper_holder *dh) +{ + struct block_device *real = dh->dh_real; + struct super_block *sb; + struct inode *inode; + int error; + + down(&real->bd_mount_sem); + sb = sget(&gfs2_fs_type, gfs2_test_bdev_super, gfs2_set_bdev_super, + real); + up(&real->bd_mount_sem); + if (IS_ERR(sb)) + return PTR_ERR(sb); + + error = -ENOMEM; + inode = new_inode(sb); + if (!inode) + goto fail; + + make_bad_inode(inode); + + sb->s_root = d_alloc_root(inode); + if (!sb->s_root) + goto fail_iput; + + sb->s_op = &gfs2_dummy_sops; + sb->s_fs_info = dh; + + up_write(&sb->s_umount); + module_put(gfs2_fs_type.owner); + + dh->dh_dummy_sb = sb; + + return 0; + + fail_iput: + iput(inode); + + fail: + up_write(&sb->s_umount); + deactivate_super(sb); + return error; +} + +static int diaper_congested(void *congested_data, int bdi_bits) +{ + struct diaper_holder *dh = (struct diaper_holder *)congested_data; + request_queue_t *q = bdev_get_queue(dh->dh_real); + return bdi_congested(&q->backing_dev_info, bdi_bits); +} + +static void diaper_unplug(request_queue_t *q) +{ + struct diaper_holder *dh = (struct diaper_holder *)q->queuedata; + request_queue_t *rq = bdev_get_queue(dh->dh_real); + + if (rq->unplug_fn) + rq->unplug_fn(rq); +} + +static int diaper_flush(request_queue_t *q, struct gendisk *disk, + sector_t *error_sector) +{ + struct diaper_holder *dh = (struct diaper_holder *)q->queuedata; + request_queue_t *rq = bdev_get_queue(dh->dh_real); + int error = -EOPNOTSUPP; + + if (rq->issue_flush_fn) + error = rq->issue_flush_fn(rq, dh->dh_real->bd_disk, NULL); + + return error; +} + +/** + * diaper_get - Do the work of creating a diaper device + * @real: + * @flags: + * + * Returns: the diaper device or ERR_PTR() + */ + +static struct diaper_holder *diaper_get(struct block_device *real, int flags) +{ + struct diaper_holder *dh; + struct gendisk *gd; + struct block_device *diaper; + unsigned int minor; + int error = -ENOMEM; + + minor = minor_get(); + if (minor < 0) + return ERR_PTR(error); + + dh = kmalloc(sizeof(struct diaper_holder), GFP_KERNEL); + if (!dh) + goto fail; + memset(dh, 0, sizeof(struct diaper_holder)); + + gd = alloc_disk(1); + if (!gd) + goto fail_kfree; + + gd->queue = blk_alloc_queue(GFP_KERNEL); + if (!gd->queue) + goto fail_gd; + + gd->queue->queuedata = dh; + gd->queue->backing_dev_info.congested_fn = diaper_congested; + gd->queue->backing_dev_info.congested_data = dh; + gd->queue->unplug_fn = diaper_unplug; + gd->queue->issue_flush_fn = diaper_flush; + blk_queue_make_request(gd->queue, diaper_make_request); + blk_queue_stack_limits(gd->queue, bdev_get_queue(real)); + if (bdev_get_queue(real)->merge_bvec_fn && + gd->queue->max_sectors > (PAGE_SIZE >> 9)) + blk_queue_max_sectors(gd->queue, PAGE_SIZE >> 9); + blk_queue_hardsect_size(gd->queue, bdev_hardsect_size(real)); + + gd->major = diaper_major; + gd->first_minor = minor; + { + char buf[BDEVNAME_SIZE]; + bdevname(real, buf); + snprintf(gd->disk_name, sizeof(gd->disk_name), + "diapered_%s", buf); + } + gd->fops = &diaper_fops; + gd->private_data = dh; + gd->capacity--; + + add_disk(gd); + + diaper = bdget_disk(gd, 0); + if (!diaper) + goto fail_remove; + + down(&diaper->bd_sem); + if (!diaper->bd_openers) { + diaper->bd_disk = gd; + diaper->bd_contains = diaper; + bd_set_size(diaper, 0x7FFFFFFFFFFFFFFFULL); + diaper->bd_inode->i_data.backing_dev_info = &gd->queue->backing_dev_info; + } else + printk("GFS2: diaper: reopening\n"); + diaper->bd_openers++; + up(&diaper->bd_sem); + + dh->dh_mempool = mempool_create(512, + mempool_alloc_slab, mempool_free_slab, + diaper_slab); + if (!dh->dh_mempool) + goto fail_bdput; + + dh->dh_count = 1; + dh->dh_gendisk = gd; + dh->dh_real = real; + dh->dh_diaper = diaper; + + error = get_dummy_sb(dh); + if (error) + goto fail_mempool; + + return dh; + + fail_mempool: + mempool_destroy(dh->dh_mempool); + + fail_bdput: + down(&diaper->bd_sem); + if (!--diaper->bd_openers) { + invalidate_bdev(diaper, 1); + diaper->bd_contains = NULL; + diaper->bd_disk = NULL; + } else + printk("GFS2: diaper: not closed\n"); + up(&diaper->bd_sem); + bdput(diaper); + + fail_remove: + del_gendisk(gd); + blk_put_queue(gd->queue); + + fail_gd: + put_disk(gd); + + fail_kfree: + kfree(dh); + + fail: + minor_put(minor); + return ERR_PTR(error); +} + +/** + * diaper_put - Do the work of destroying a diaper device + * @dh: + * + */ + +static void diaper_put(struct diaper_holder *dh) +{ + struct block_device *diaper = dh->dh_diaper; + struct gendisk *gd = dh->dh_gendisk; + int minor = dh->dh_gendisk->first_minor; + + generic_shutdown_super(dh->dh_dummy_sb); + + mempool_destroy(dh->dh_mempool); + + down(&diaper->bd_sem); + if (!--diaper->bd_openers) { + invalidate_bdev(diaper, 1); + diaper->bd_contains = NULL; + diaper->bd_disk = NULL; + } else + printk("GFS2: diaper: not closed\n"); + up(&diaper->bd_sem); + + bdput(diaper); + del_gendisk(gd); + blk_put_queue(gd->queue); + put_disk(gd); + kfree(dh); + minor_put(minor); +} + +/** + * gfs2_diaper_get - Get a hold of an existing diaper or create a new one + * @real: + * @flags: + * + * Returns: the diaper device or ERR_PTR() + */ + +struct block_device *gfs2_diaper_get(struct block_device *real, int flags) +{ + struct diaper_holder *dh, *dh_new = NULL; + int found; + + for (;;) { + found = FALSE; + spin_lock(&diaper_lock); + list_for_each_entry(dh, &diaper_list, dh_list) { + if (dh->dh_real == real) { + dh->dh_count++; + found = TRUE; + break; + } + } + if (!found) + dh = NULL; + if (!dh && dh_new) { + dh = dh_new; + list_add(&dh->dh_list, &diaper_list); + dh_new = NULL; + } + spin_unlock(&diaper_lock); + + if (dh) { + if (dh_new) + diaper_put(dh_new); + return dh->dh_diaper; + } + + dh_new = diaper_get(real, flags); + if (IS_ERR(dh_new)) + return (struct block_device *)dh_new; + } +} + +/** + * gfs2_diaper_put - Drop a reference on a diaper + * @diaper: + * + */ + +void gfs2_diaper_put(struct block_device *diaper) +{ + struct diaper_holder *dh; + + spin_lock(&diaper_lock); + list_for_each_entry(dh, &diaper_list, dh_list) { + if (dh->dh_diaper == diaper) { + if (!--dh->dh_count) { + list_del(&dh->dh_list); + spin_unlock(&diaper_lock); + diaper_put(dh); + } else + spin_unlock(&diaper_lock); + return; + } + } + spin_unlock(&diaper_lock); + + printk("GFS2: diaper: unknown undiaper\n"); +} + +/** + * gfs2_diaper_register_sbd - + * @diaper: + * @sdp: + * + */ + +void gfs2_diaper_register_sbd(struct block_device *diaper, struct gfs2_sbd *sdp) +{ + struct diaper_holder *dh; + + spin_lock(&diaper_lock); + list_for_each_entry(dh, &diaper_list, dh_list) { + if (dh->dh_diaper == diaper) { + dh->dh_sbd = sdp; + spin_unlock(&diaper_lock); + return; + } + } + spin_unlock(&diaper_lock); + + printk("GFS2: diaper: unknown register\n"); +} + +/** + * gfs2_diaper_2real - + * @diaper: + * + * Returns: the real device cooresponding to the diaper + */ + +struct block_device *gfs2_diaper_2real(struct block_device *diaper) +{ + struct diaper_holder *dh; + + spin_lock(&diaper_lock); + list_for_each_entry(dh, &diaper_list, dh_list) { + if (dh->dh_diaper == diaper) { + spin_unlock(&diaper_lock); + return dh->dh_real; + } + } + spin_unlock(&diaper_lock); + + printk("GFS2: diaper: unknown 2real\n"); + return NULL; +} + +/** + * gfs2_diaper_init - + * + * Returns: errno + */ + +int gfs2_diaper_init(void) +{ + spin_lock_init(&diaper_lock); + + diaper_slab = kmem_cache_create("gfs2_bio_wrapper", + sizeof(struct bio_wrapper), + 0, 0, NULL, NULL); + if (!diaper_slab) + return -ENOMEM; + + diaper_major = register_blkdev(0, "gfs2_diaper"); + if (diaper_major < 0) { + kmem_cache_destroy(diaper_slab); + return diaper_major; + } + + return 0; +} + +/** + * gfs2_diaper_uninit - + * + */ + +void gfs2_diaper_uninit(void) +{ + kmem_cache_destroy(diaper_slab); + unregister_blkdev(diaper_major, "gfs2_diaper"); +} + --- a/fs/gfs2/diaper.h 1970-01-01 07:30:00.000000000 +0730 +++ b/fs/gfs2/diaper.h 2005-08-01 14:13:08.008808352 +0800 @@ -0,0 +1,27 @@ +/****************************************************************************** +******************************************************************************* +** +** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. +** Copyright (C) 2004-2005 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. +** +******************************************************************************* +******************************************************************************/ + +#ifndef __DIAPER_DOT_H__ +#define __DIAPER_DOT_H__ + +struct block_device *gfs2_diaper_get(struct block_device *real, int flags); +void gfs2_diaper_put(struct block_device *diaper); + +void gfs2_diaper_register_sbd(struct block_device *diaper, + struct gfs2_sbd *sdp); +struct block_device *gfs2_diaper_2real(struct block_device *diaper); + +int gfs2_diaper_init(void); +void gfs2_diaper_uninit(void); + +#endif /* __DIAPER_DOT_H__ */ --- a/fs/gfs2/debug.c 1970-01-01 07:30:00.000000000 +0730 +++ b/fs/gfs2/debug.c 2005-08-01 14:13:08.007808504 +0800 @@ -0,0 +1,210 @@ +/****************************************************************************** +******************************************************************************* +** +** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. +** Copyright (C) 2004-2005 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 +#include +#include +#include +#include +#include +#include + +#define WANT_DEBUG_NAMES +#include "gfs2.h" + +#undef kmalloc +#undef kfree + +void *gmalloc_nofail_real(unsigned int size, int flags, char *file, + unsigned int line) +{ + void *x; + for (;;) { + x = kmalloc(size, flags); + if (x) + return x; + if (time_after_eq(jiffies, gfs2_malloc_warning + 5 * HZ)) { + printk("GFS2: out of memory: %s, %u\n", + __FILE__, __LINE__); + gfs2_malloc_warning = jiffies; + } + yield(); + } +} + +#if defined(GFS2_MEMORY_SIMPLE) + +atomic_t gfs2_memory_count; + +void gfs2_memory_add_i(void *data, char *file, unsigned int line) +{ + atomic_inc(&gfs2_memory_count); +} + +void gfs2_memory_rm_i(void *data, char *file, unsigned int line) +{ + if (data) + atomic_dec(&gfs2_memory_count); +} + +void *gmalloc(unsigned int size, int flags, char *file, unsigned int line) +{ + void *data = kmalloc(size, flags); + if (data) + atomic_inc(&gfs2_memory_count); + return data; +} + +void *gmalloc_nofail(unsigned int size, int flags, char *file, + unsigned int line) +{ + atomic_inc(&gfs2_memory_count); + return gmalloc_nofail_real(size, flags, file, line); +} + +void gfree(void *data, char *file, unsigned int line) +{ + if (data) { + atomic_dec(&gfs2_memory_count); + kfree(data); + } +} + +void gfs2_memory_init(void) +{ + atomic_set(&gfs2_memory_count, 0); +} + +void gfs2_memory_uninit(void) +{ + int x = atomic_read(&gfs2_memory_count); + if (x) + printk("GFS2: %d memory allocations remaining\n", x); +} + +#elif defined(GFS2_MEMORY_BRUTE) + +#define GFS2_MEMORY_HASH_SHIFT 13 +#define GFS2_MEMORY_HASH_SIZE (1 << GFS2_MEMORY_HASH_SHIFT) +#define GFS2_MEMORY_HASH_MASK (GFS2_MEMORY_HASH_SIZE - 1) + +struct gfs2_memory { + struct list_head gm_list; + void *gm_data; + char *gm_file; + unsigned int gm_line; +}; + +static spinlock_t memory_lock; +static struct list_head memory_list[GFS2_MEMORY_HASH_SIZE]; + +static inline struct list_head *memory_bucket(void *data) +{ + return memory_list + + (gfs2_hash(&data, sizeof(void *)) & + GFS2_MEMORY_HASH_MASK); +} + +void gfs2_memory_add_i(void *data, char *file, unsigned int line) +{ + struct gfs2_memory *gm; + + RETRY_MALLOC(gm = kmalloc(sizeof(struct gfs2_memory), GFP_KERNEL), gm); + gm->gm_data = data; + gm->gm_file = file; + gm->gm_line = line; + + spin_lock(&memory_lock); + list_add(&gm->gm_list, memory_bucket(data)); + spin_unlock(&memory_lock); +} + +void gfs2_memory_rm_i(void *data, char *file, unsigned int line) +{ + struct list_head *head = memory_bucket(data); + struct gfs2_memory *gm; + int found = FALSE; + + if (!data) + return; + + spin_lock(&memory_lock); + list_for_each_entry(gm, head, gm_list) { + if (gm->gm_data == data) { + list_del(&gm->gm_list); + found = TRUE; + break; + } + } + spin_unlock(&memory_lock); + + if (!found) + printk("GFS2: freeing unalloced memory from (%s, %u)\n", + file, line); + else + kfree(gm); +} + +void *gmalloc(unsigned int size, int flags, char *file, unsigned int line) +{ + void *data = kmalloc(size, flags); + if (data) + gfs2_memory_add_i(data, file, line); + return data; +} + +void *gmalloc_nofail(unsigned int size, int flags, char *file, + unsigned int line) +{ + void *data = gmalloc_nofail_real(size, flags, file, line); + gfs2_memory_add_i(data, file, line); + return data; +} + +void gfree(void *data, char *file, unsigned int line) +{ + if (data) { + gfs2_memory_rm_i(data, file, line); + kfree(data); + } +} + +void gfs2_memory_init(void) +{ + unsigned int x; + + spin_lock_init(&memory_lock); + for (x = 0; x < GFS2_MEMORY_HASH_SIZE; x++) + INIT_LIST_HEAD(memory_list + x); +} + +void gfs2_memory_uninit(void) +{ + unsigned int x; + struct list_head *head; + struct gfs2_memory *gm; + + for (x = 0; x < GFS2_MEMORY_HASH_SIZE; x++) { + head = memory_list + x; + while (!list_empty(head)) { + gm = list_entry(head->next, struct gfs2_memory, gm_list); + printk("GFS2: unfreed memory from (%s, %u)\n", + gm->gm_file, gm->gm_line); + list_del(&gm->gm_list); + kfree(gm); + } + } +} + +#endif + --- a/fs/gfs2/debug.h 1970-01-01 07:30:00.000000000 +0730 +++ b/fs/gfs2/debug.h 2005-08-01 14:13:08.008808352 +0800 @@ -0,0 +1,49 @@ +/****************************************************************************** +******************************************************************************* +** +** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. +** Copyright (C) 2004-2005 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. +** +******************************************************************************* +******************************************************************************/ + +#ifndef __DEBUG_DOT_H__ +#define __DEBUG_DOT_H__ + +#if defined(GFS2_MEMORY_SIMPLE) || defined(GFS2_MEMORY_BRUTE) + +void gfs2_memory_init(void); +void gfs2_memory_uninit(void); +void gfs2_memory_add_i(void *data, char *file, unsigned int line); +void gfs2_memory_rm_i(void *data, char *file, unsigned int line); +void *gmalloc(unsigned int size, int flags, char *file, unsigned int line); +void gfree(void *data, char *file, unsigned int line); + +#define gfs2_memory_add(data) gfs2_memory_add_i((data), __FILE__, __LINE__) +#define gfs2_memory_rm(data) gfs2_memory_rm_i((data), __FILE__, __LINE__) +#define kmalloc(size, flags) gmalloc((size), (flags), __FILE__, __LINE__) +#define kfree(data) gfree((data), __FILE__, __LINE__) + +void *gmalloc_nofail(unsigned int size, int flags, char *file, + unsigned int line); +#define kmalloc_nofail(size, flags) \ + gmalloc_nofail((size), (flags), __FILE__, __LINE__) + +#else +#define gfs2_memory_init() do {} while (0) +#define gfs2_memory_uninit() do {} while (0) +#define gfs2_memory_add(x) do {} while (0) +#define gfs2_memory_rm(x) do {} while (0) + +void *gmalloc_nofail_real(unsigned int size, int flags, char *file, + unsigned int line); +#define kmalloc_nofail(size, flags) \ + gmalloc_nofail_real((size), (flags), __FILE__, __LINE__) +#endif + +#endif /* __DEBUG_DOT_H__ */ +