123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453 |
- /*
- * 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 <linux/sched.h>
- #include <linux/slab.h>
- #include <linux/spinlock.h>
- #include <linux/completion.h>
- #include <linux/buffer_head.h>
- #include <linux/kthread.h>
- #include <asm/semaphore.h>
- #include "gfs2.h"
- #include "bmap.h"
- #include "inode.h"
- #include "meta_io.h"
- #include "trans.h"
- #include "unlinked.h"
- static int munge_ondisk(struct gfs2_sbd *sdp, unsigned int slot,
- struct gfs2_unlinked_tag *ut)
- {
- struct gfs2_inode *ip = get_v2ip(sdp->sd_ut_inode);
- unsigned int block, offset;
- uint64_t dblock;
- int new = 0;
- struct buffer_head *bh;
- int error;
- block = slot / sdp->sd_ut_per_block;
- offset = slot % sdp->sd_ut_per_block;
- error = gfs2_block_map(ip, block, &new, &dblock, NULL);
- if (error)
- return error;
- error = gfs2_meta_read(ip->i_gl, dblock, DIO_START | DIO_WAIT, &bh);
- if (error)
- return error;
- if (gfs2_metatype_check(sdp, bh, GFS2_METATYPE_UT)) {
- error = -EIO;
- goto out;
- }
- mutex_lock(&sdp->sd_unlinked_mutex);
- gfs2_trans_add_bh(ip->i_gl, bh, 1);
- gfs2_unlinked_tag_out(ut, bh->b_data +
- sizeof(struct gfs2_meta_header) +
- offset * sizeof(struct gfs2_unlinked_tag));
- mutex_unlock(&sdp->sd_unlinked_mutex);
- out:
- brelse(bh);
- return error;
- }
- static void ul_hash(struct gfs2_sbd *sdp, struct gfs2_unlinked *ul)
- {
- spin_lock(&sdp->sd_unlinked_spin);
- list_add(&ul->ul_list, &sdp->sd_unlinked_list);
- gfs2_assert(sdp, ul->ul_count);
- ul->ul_count++;
- atomic_inc(&sdp->sd_unlinked_count);
- spin_unlock(&sdp->sd_unlinked_spin);
- }
- static void ul_unhash(struct gfs2_sbd *sdp, struct gfs2_unlinked *ul)
- {
- spin_lock(&sdp->sd_unlinked_spin);
- list_del_init(&ul->ul_list);
- gfs2_assert(sdp, ul->ul_count > 1);
- ul->ul_count--;
- gfs2_assert_warn(sdp, atomic_read(&sdp->sd_unlinked_count) > 0);
- atomic_dec(&sdp->sd_unlinked_count);
- spin_unlock(&sdp->sd_unlinked_spin);
- }
- static struct gfs2_unlinked *ul_fish(struct gfs2_sbd *sdp)
- {
- struct list_head *head;
- struct gfs2_unlinked *ul;
- int found = 0;
- if (sdp->sd_vfs->s_flags & MS_RDONLY)
- return NULL;
- spin_lock(&sdp->sd_unlinked_spin);
- head = &sdp->sd_unlinked_list;
- list_for_each_entry(ul, head, ul_list) {
- if (test_bit(ULF_LOCKED, &ul->ul_flags))
- continue;
- list_move_tail(&ul->ul_list, head);
- ul->ul_count++;
- set_bit(ULF_LOCKED, &ul->ul_flags);
- found = 1;
- break;
- }
- if (!found)
- ul = NULL;
- spin_unlock(&sdp->sd_unlinked_spin);
- return ul;
- }
- /**
- * enforce_limit - limit the number of inodes waiting to be deallocated
- * @sdp: the filesystem
- *
- * Returns: errno
- */
- static void enforce_limit(struct gfs2_sbd *sdp)
- {
- unsigned int tries = 0, min = 0;
- int error;
- if (atomic_read(&sdp->sd_unlinked_count) >=
- gfs2_tune_get(sdp, gt_ilimit)) {
- tries = gfs2_tune_get(sdp, gt_ilimit_tries);
- min = gfs2_tune_get(sdp, gt_ilimit_min);
- }
- while (tries--) {
- struct gfs2_unlinked *ul = ul_fish(sdp);
- if (!ul)
- break;
- error = gfs2_inode_dealloc(sdp, ul);
- gfs2_unlinked_put(sdp, ul);
- if (!error) {
- if (!--min)
- break;
- } else if (error != 1)
- break;
- }
- }
- static struct gfs2_unlinked *ul_alloc(struct gfs2_sbd *sdp)
- {
- struct gfs2_unlinked *ul;
- ul = kzalloc(sizeof(struct gfs2_unlinked), GFP_KERNEL);
- if (ul) {
- INIT_LIST_HEAD(&ul->ul_list);
- ul->ul_count = 1;
- set_bit(ULF_LOCKED, &ul->ul_flags);
- }
- return ul;
- }
- int gfs2_unlinked_get(struct gfs2_sbd *sdp, struct gfs2_unlinked **ul)
- {
- unsigned int c, o = 0, b;
- unsigned char byte = 0;
- enforce_limit(sdp);
- *ul = ul_alloc(sdp);
- if (!*ul)
- return -ENOMEM;
- spin_lock(&sdp->sd_unlinked_spin);
- for (c = 0; c < sdp->sd_unlinked_chunks; c++)
- for (o = 0; o < PAGE_SIZE; o++) {
- byte = sdp->sd_unlinked_bitmap[c][o];
- if (byte != 0xFF)
- goto found;
- }
- goto fail;
- found:
- for (b = 0; b < 8; b++)
- if (!(byte & (1 << b)))
- break;
- (*ul)->ul_slot = c * (8 * PAGE_SIZE) + o * 8 + b;
- if ((*ul)->ul_slot >= sdp->sd_unlinked_slots)
- goto fail;
- sdp->sd_unlinked_bitmap[c][o] |= 1 << b;
- spin_unlock(&sdp->sd_unlinked_spin);
- return 0;
- fail:
- spin_unlock(&sdp->sd_unlinked_spin);
- kfree(*ul);
- return -ENOSPC;
- }
- void gfs2_unlinked_put(struct gfs2_sbd *sdp, struct gfs2_unlinked *ul)
- {
- gfs2_assert_warn(sdp, test_and_clear_bit(ULF_LOCKED, &ul->ul_flags));
- spin_lock(&sdp->sd_unlinked_spin);
- gfs2_assert(sdp, ul->ul_count);
- ul->ul_count--;
- if (!ul->ul_count) {
- gfs2_icbit_munge(sdp, sdp->sd_unlinked_bitmap, ul->ul_slot, 0);
- spin_unlock(&sdp->sd_unlinked_spin);
- kfree(ul);
- } else
- spin_unlock(&sdp->sd_unlinked_spin);
- }
- int gfs2_unlinked_ondisk_add(struct gfs2_sbd *sdp, struct gfs2_unlinked *ul)
- {
- int error;
- gfs2_assert_warn(sdp, test_bit(ULF_LOCKED, &ul->ul_flags));
- gfs2_assert_warn(sdp, list_empty(&ul->ul_list));
- error = munge_ondisk(sdp, ul->ul_slot, &ul->ul_ut);
- if (!error)
- ul_hash(sdp, ul);
- return error;
- }
- int gfs2_unlinked_ondisk_munge(struct gfs2_sbd *sdp, struct gfs2_unlinked *ul)
- {
- int error;
- gfs2_assert_warn(sdp, test_bit(ULF_LOCKED, &ul->ul_flags));
- gfs2_assert_warn(sdp, !list_empty(&ul->ul_list));
- error = munge_ondisk(sdp, ul->ul_slot, &ul->ul_ut);
- return error;
- }
- int gfs2_unlinked_ondisk_rm(struct gfs2_sbd *sdp, struct gfs2_unlinked *ul)
- {
- struct gfs2_unlinked_tag ut;
- int error;
- gfs2_assert_warn(sdp, test_bit(ULF_LOCKED, &ul->ul_flags));
- gfs2_assert_warn(sdp, !list_empty(&ul->ul_list));
- memset(&ut, 0, sizeof(struct gfs2_unlinked_tag));
- error = munge_ondisk(sdp, ul->ul_slot, &ut);
- if (error)
- return error;
- ul_unhash(sdp, ul);
- return 0;
- }
- /**
- * gfs2_unlinked_dealloc - Go through the list of inodes to be deallocated
- * @sdp: the filesystem
- *
- * Returns: errno
- */
- int gfs2_unlinked_dealloc(struct gfs2_sbd *sdp)
- {
- unsigned int hits, strikes;
- int error;
- for (;;) {
- hits = 0;
- strikes = 0;
- for (;;) {
- struct gfs2_unlinked *ul = ul_fish(sdp);
- if (!ul)
- return 0;
- error = gfs2_inode_dealloc(sdp, ul);
- gfs2_unlinked_put(sdp, ul);
- if (!error) {
- hits++;
- if (strikes)
- strikes--;
- } else if (error == 1) {
- strikes++;
- if (strikes >=
- atomic_read(&sdp->sd_unlinked_count)) {
- error = 0;
- break;
- }
- } else
- return error;
- }
- if (!hits || kthread_should_stop())
- break;
- cond_resched();
- }
- return 0;
- }
- int gfs2_unlinked_init(struct gfs2_sbd *sdp)
- {
- struct gfs2_inode *ip = get_v2ip(sdp->sd_ut_inode);
- unsigned int blocks = ip->i_di.di_size >> sdp->sd_sb.sb_bsize_shift;
- unsigned int x, slot = 0;
- unsigned int found = 0;
- uint64_t dblock;
- uint32_t extlen = 0;
- int error;
- if (!ip->i_di.di_size ||
- ip->i_di.di_size > (64 << 20) ||
- ip->i_di.di_size & (sdp->sd_sb.sb_bsize - 1)) {
- gfs2_consist_inode(ip);
- return -EIO;
- }
- sdp->sd_unlinked_slots = blocks * sdp->sd_ut_per_block;
- sdp->sd_unlinked_chunks = DIV_RU(sdp->sd_unlinked_slots, 8 * PAGE_SIZE);
- error = -ENOMEM;
- sdp->sd_unlinked_bitmap = kcalloc(sdp->sd_unlinked_chunks,
- sizeof(unsigned char *),
- GFP_KERNEL);
- if (!sdp->sd_unlinked_bitmap)
- return error;
- for (x = 0; x < sdp->sd_unlinked_chunks; x++) {
- sdp->sd_unlinked_bitmap[x] = kzalloc(PAGE_SIZE, GFP_KERNEL);
- if (!sdp->sd_unlinked_bitmap[x])
- goto fail;
- }
- for (x = 0; x < blocks; x++) {
- struct buffer_head *bh;
- unsigned int y;
- if (!extlen) {
- int new = 0;
- error = gfs2_block_map(ip, x, &new, &dblock, &extlen);
- if (error)
- goto fail;
- }
- gfs2_meta_ra(ip->i_gl, dblock, extlen);
- error = gfs2_meta_read(ip->i_gl, dblock, DIO_START | DIO_WAIT,
- &bh);
- if (error)
- goto fail;
- error = -EIO;
- if (gfs2_metatype_check(sdp, bh, GFS2_METATYPE_UT)) {
- brelse(bh);
- goto fail;
- }
- for (y = 0;
- y < sdp->sd_ut_per_block && slot < sdp->sd_unlinked_slots;
- y++, slot++) {
- struct gfs2_unlinked_tag ut;
- struct gfs2_unlinked *ul;
- gfs2_unlinked_tag_in(&ut, bh->b_data +
- sizeof(struct gfs2_meta_header) +
- y * sizeof(struct gfs2_unlinked_tag));
- if (!ut.ut_inum.no_addr)
- continue;
- error = -ENOMEM;
- ul = ul_alloc(sdp);
- if (!ul) {
- brelse(bh);
- goto fail;
- }
- ul->ul_ut = ut;
- ul->ul_slot = slot;
- spin_lock(&sdp->sd_unlinked_spin);
- gfs2_icbit_munge(sdp, sdp->sd_unlinked_bitmap, slot, 1);
- spin_unlock(&sdp->sd_unlinked_spin);
- ul_hash(sdp, ul);
- gfs2_unlinked_put(sdp, ul);
- found++;
- }
- brelse(bh);
- dblock++;
- extlen--;
- }
- if (found)
- fs_info(sdp, "found %u unlinked inodes\n", found);
- return 0;
- fail:
- gfs2_unlinked_cleanup(sdp);
- return error;
- }
- /**
- * gfs2_unlinked_cleanup - get rid of any extra struct gfs2_unlinked structures
- * @sdp: the filesystem
- *
- */
- void gfs2_unlinked_cleanup(struct gfs2_sbd *sdp)
- {
- struct list_head *head = &sdp->sd_unlinked_list;
- struct gfs2_unlinked *ul;
- unsigned int x;
- spin_lock(&sdp->sd_unlinked_spin);
- while (!list_empty(head)) {
- ul = list_entry(head->next, struct gfs2_unlinked, ul_list);
- if (ul->ul_count > 1) {
- list_move_tail(&ul->ul_list, head);
- spin_unlock(&sdp->sd_unlinked_spin);
- schedule();
- spin_lock(&sdp->sd_unlinked_spin);
- continue;
- }
- list_del_init(&ul->ul_list);
- atomic_dec(&sdp->sd_unlinked_count);
- gfs2_assert_warn(sdp, ul->ul_count == 1);
- gfs2_assert_warn(sdp, !test_bit(ULF_LOCKED, &ul->ul_flags));
- kfree(ul);
- }
- spin_unlock(&sdp->sd_unlinked_spin);
- gfs2_assert_warn(sdp, !atomic_read(&sdp->sd_unlinked_count));
- if (sdp->sd_unlinked_bitmap) {
- for (x = 0; x < sdp->sd_unlinked_chunks; x++)
- kfree(sdp->sd_unlinked_bitmap[x]);
- kfree(sdp->sd_unlinked_bitmap);
- }
- }
|