X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Fhugetlbfs%2Finode.c;h=e6bd553fdc4cf29214e6f2b3a7fe50817e9e8d80;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=8d456419118c46ca07ed6838001cff29e620333b;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index 8d4564191..e6bd553fd 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -33,49 +34,68 @@ #define HUGETLBFS_MAGIC 0x958458f6 static struct super_operations hugetlbfs_ops; -static struct address_space_operations hugetlbfs_aops; -struct file_operations hugetlbfs_file_operations; +static const struct address_space_operations hugetlbfs_aops; +const struct file_operations hugetlbfs_file_operations; static struct inode_operations hugetlbfs_dir_inode_operations; static struct inode_operations hugetlbfs_inode_operations; static struct backing_dev_info hugetlbfs_backing_dev_info = { .ra_pages = 0, /* No readahead */ - .memory_backed = 1, /* Does not contribute to dirty memory */ + .capabilities = BDI_CAP_NO_ACCT_DIRTY | BDI_CAP_NO_WRITEBACK, }; +int sysctl_hugetlb_shm_group; + +static void huge_pagevec_release(struct pagevec *pvec) +{ + int i; + + for (i = 0; i < pagevec_count(pvec); ++i) + put_page(pvec->pages[i]); + + pagevec_reinit(pvec); +} + static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma) { - struct inode *inode = file->f_dentry->d_inode; - struct address_space *mapping = inode->i_mapping; + struct inode *inode = file->f_path.dentry->d_inode; loff_t len, vma_len; int ret; - if (vma->vm_start & ~HPAGE_MASK) - return -EINVAL; - - if (vma->vm_end & ~HPAGE_MASK) - return -EINVAL; - - if (vma->vm_end - vma->vm_start < HPAGE_SIZE) - return -EINVAL; + /* + * vma alignment has already been checked by prepare_hugepage_range. + * If you add any error returns here, do so after setting VM_HUGETLB, + * so is_vm_hugetlb_page tests below unmap_region go the right way + * when do_mmap_pgoff unwinds (may be important on powerpc and ia64). + */ + vma->vm_flags |= VM_HUGETLB | VM_RESERVED; + vma->vm_ops = &hugetlb_vm_ops; vma_len = (loff_t)(vma->vm_end - vma->vm_start); - down(&inode->i_sem); + mutex_lock(&inode->i_mutex); file_accessed(file); - vma->vm_flags |= VM_HUGETLB | VM_RESERVED; - vma->vm_ops = &hugetlb_vm_ops; - ret = hugetlb_prefault(mapping, vma); - len = vma_len + ((loff_t)vma->vm_pgoff << PAGE_SHIFT); - if (ret == 0 && inode->i_size < len) + + ret = -ENOMEM; + len = vma_len + ((loff_t)vma->vm_pgoff << PAGE_SHIFT); + + if (vma->vm_flags & VM_MAYSHARE && + hugetlb_reserve_pages(inode, vma->vm_pgoff >> (HPAGE_SHIFT-PAGE_SHIFT), + len >> HPAGE_SHIFT)) + goto out; + + ret = 0; + hugetlb_prefault_arch_hook(vma->vm_mm); + if (vma->vm_flags & VM_WRITE && inode->i_size < len) inode->i_size = len; - up(&inode->i_sem); +out: + mutex_unlock(&inode->i_mutex); return ret; } /* - * Called under down_write(mmap_sem), page_table_lock is not held + * Called under down_write(mmap_sem). */ #ifdef HAVE_ARCH_HUGETLB_UNMAPPED_AREA @@ -88,6 +108,7 @@ hugetlb_get_unmapped_area(struct file *file, unsigned long addr, { struct mm_struct *mm = current->mm; struct vm_area_struct *vma; + unsigned long start_addr; if (len & ~HPAGE_MASK) return -EINVAL; @@ -102,12 +123,28 @@ hugetlb_get_unmapped_area(struct file *file, unsigned long addr, return addr; } - addr = ALIGN(mm->free_area_cache, HPAGE_SIZE); + start_addr = mm->free_area_cache; + + if (len <= mm->cached_hole_size) + start_addr = TASK_UNMAPPED_BASE; + +full_search: + addr = ALIGN(start_addr, HPAGE_SIZE); for (vma = find_vma(mm, addr); ; vma = vma->vm_next) { /* At this point: (!vma || addr < vma->vm_end). */ - if (TASK_SIZE - len < addr) + if (TASK_SIZE - len < addr) { + /* + * Start a new search - just in case we missed + * some holes. + */ + if (start_addr != TASK_UNMAPPED_BASE) { + start_addr = TASK_UNMAPPED_BASE; + goto full_search; + } return -ENOMEM; + } + if (!vma || addr + len <= vma->vm_start) return addr; addr = ALIGN(vma->vm_end, HPAGE_SIZE); @@ -137,30 +174,21 @@ static int hugetlbfs_commit_write(struct file *file, return -EINVAL; } -void huge_pagevec_release(struct pagevec *pvec) +static void truncate_huge_page(struct page *page) { - int i; - - for (i = 0; i < pagevec_count(pvec); ++i) - put_page(pvec->pages[i]); - - pagevec_reinit(pvec); -} - -void truncate_huge_page(struct page *page) -{ - clear_page_dirty(page); + cancel_dirty_page(page, /* No IO accounting for huge pages? */0); ClearPageUptodate(page); remove_from_page_cache(page); put_page(page); } -void truncate_hugepages(struct address_space *mapping, loff_t lstart) +static void truncate_hugepages(struct inode *inode, loff_t lstart) { + struct address_space *mapping = &inode->i_data; const pgoff_t start = lstart >> HPAGE_SHIFT; struct pagevec pvec; pgoff_t next; - int i; + int i, freed = 0; pagevec_init(&pvec, 0); next = start; @@ -182,72 +210,50 @@ void truncate_hugepages(struct address_space *mapping, loff_t lstart) truncate_huge_page(page); unlock_page(page); hugetlb_put_quota(mapping); + freed++; } huge_pagevec_release(&pvec); } BUG_ON(!lstart && mapping->nrpages); + hugetlb_unreserve_pages(inode, start, freed); } static void hugetlbfs_delete_inode(struct inode *inode) { - struct hugetlbfs_sb_info *sbinfo = HUGETLBFS_SB(inode->i_sb); - - hlist_del_init(&inode->i_hash); - list_del_init(&inode->i_list); - inode->i_state |= I_FREEING; - inodes_stat.nr_inodes--; - spin_unlock(&inode_lock); - - if (inode->i_data.nrpages) - truncate_hugepages(&inode->i_data, 0); - - security_inode_delete(inode); - - if (sbinfo->free_inodes >= 0) { - spin_lock(&sbinfo->stat_lock); - sbinfo->free_inodes++; - spin_unlock(&sbinfo->stat_lock); - } - + truncate_hugepages(inode, 0); clear_inode(inode); - destroy_inode(inode); } -static void hugetlbfs_forget_inode(struct inode *inode) +static void hugetlbfs_forget_inode(struct inode *inode) __releases(inode_lock) { - struct super_block *super_block = inode->i_sb; - struct hugetlbfs_sb_info *sbinfo = HUGETLBFS_SB(super_block); - - if (hlist_unhashed(&inode->i_hash)) - goto out_truncate; - - if (!(inode->i_state & (I_DIRTY|I_LOCK))) { - list_del(&inode->i_list); - list_add(&inode->i_list, &inode_unused); - } - inodes_stat.nr_unused++; - if (!super_block || (super_block->s_flags & MS_ACTIVE)) { + struct super_block *sb = inode->i_sb; + + if (!hlist_unhashed(&inode->i_hash)) { + if (!(inode->i_state & (I_DIRTY|I_LOCK))) + list_move(&inode->i_list, &inode_unused); + inodes_stat.nr_unused++; + if (!sb || (sb->s_flags & MS_ACTIVE)) { + spin_unlock(&inode_lock); + return; + } + inode->i_state |= I_WILL_FREE; spin_unlock(&inode_lock); - return; + /* + * write_inode_now is a noop as we set BDI_CAP_NO_WRITEBACK + * in our backing_dev_info. + */ + write_inode_now(inode, 1); + spin_lock(&inode_lock); + inode->i_state &= ~I_WILL_FREE; + inodes_stat.nr_unused--; + hlist_del_init(&inode->i_hash); } - - /* write_inode_now() ? */ - inodes_stat.nr_unused--; - hlist_del_init(&inode->i_hash); -out_truncate: list_del_init(&inode->i_list); + list_del_init(&inode->i_sb_list); inode->i_state |= I_FREEING; inodes_stat.nr_inodes--; spin_unlock(&inode_lock); - if (inode->i_data.nrpages) - truncate_hugepages(&inode->i_data, 0); - - if (sbinfo->free_inodes >= 0) { - spin_lock(&sbinfo->stat_lock); - sbinfo->free_inodes++; - spin_unlock(&sbinfo->stat_lock); - } - + truncate_hugepages(inode, 0); clear_inode(inode); destroy_inode(inode); } @@ -255,52 +261,33 @@ out_truncate: static void hugetlbfs_drop_inode(struct inode *inode) { if (!inode->i_nlink) - hugetlbfs_delete_inode(inode); + generic_delete_inode(inode); else hugetlbfs_forget_inode(inode); } -/* - * h_pgoff is in HPAGE_SIZE units. - * vma->vm_pgoff is in PAGE_SIZE units. - */ -static void -hugetlb_vmtruncate_list(struct list_head *list, unsigned long h_pgoff) +static inline void +hugetlb_vmtruncate_list(struct prio_tree_root *root, pgoff_t pgoff) { struct vm_area_struct *vma; + struct prio_tree_iter iter; - list_for_each_entry(vma, list, shared) { - unsigned long h_vm_pgoff; - unsigned long v_length; - unsigned long h_length; + vma_prio_tree_foreach(vma, &iter, root, pgoff, ULONG_MAX) { unsigned long v_offset; - h_vm_pgoff = vma->vm_pgoff << (HPAGE_SHIFT - PAGE_SHIFT); - v_length = vma->vm_end - vma->vm_start; - h_length = v_length >> HPAGE_SHIFT; - v_offset = (h_pgoff - h_vm_pgoff) << HPAGE_SHIFT; - /* - * Is this VMA fully outside the truncation point? + * Can the expression below overflow on 32-bit arches? + * No, because the prio_tree returns us only those vmas + * which overlap the truncated area starting at pgoff, + * and no vma on a 32-bit arch can span beyond the 4GB. */ - if (h_vm_pgoff >= h_pgoff) { - zap_hugepage_range(vma, vma->vm_start, v_length); - continue; - } - - /* - * Is this VMA fully inside the truncaton point? - */ - if (h_vm_pgoff + (v_length >> HPAGE_SHIFT) <= h_pgoff) - continue; + if (vma->vm_pgoff < pgoff) + v_offset = (pgoff - vma->vm_pgoff) << PAGE_SHIFT; + else + v_offset = 0; - /* - * The VMA straddles the truncation point. v_offset is the - * offset (in bytes) into the VMA where the point lies. - */ - zap_hugepage_range(vma, - vma->vm_start + v_offset, - v_length - v_offset); + __unmap_hugepage_range(vma, + vma->vm_start + v_offset, vma->vm_end); } } @@ -309,23 +296,21 @@ hugetlb_vmtruncate_list(struct list_head *list, unsigned long h_pgoff) */ static int hugetlb_vmtruncate(struct inode *inode, loff_t offset) { - unsigned long pgoff; + pgoff_t pgoff; struct address_space *mapping = inode->i_mapping; if (offset > inode->i_size) return -EINVAL; BUG_ON(offset & ~HPAGE_MASK); - pgoff = offset >> HPAGE_SHIFT; + pgoff = offset >> PAGE_SHIFT; inode->i_size = offset; - down(&mapping->i_shared_sem); - if (!list_empty(&mapping->i_mmap)) + spin_lock(&mapping->i_mmap_lock); + if (!prio_tree_empty(&mapping->i_mmap)) hugetlb_vmtruncate_list(&mapping->i_mmap, pgoff); - if (!list_empty(&mapping->i_mmap_shared)) - hugetlb_vmtruncate_list(&mapping->i_mmap_shared, pgoff); - up(&mapping->i_shared_sem); - truncate_hugepages(mapping, offset); + spin_unlock(&mapping->i_mmap_lock); + truncate_hugepages(inode, offset); return 0; } @@ -341,9 +326,6 @@ static int hugetlbfs_setattr(struct dentry *dentry, struct iattr *attr) if (error) goto out; - error = security_inode_setattr(dentry, attr); - if (error) - goto out; if (ia_valid & ATTR_SIZE) { error = -EINVAL; if (!(attr->ia_size & ~HPAGE_MASK)) @@ -361,28 +343,20 @@ static struct inode *hugetlbfs_get_inode(struct super_block *sb, uid_t uid, gid_t gid, int mode, dev_t dev) { struct inode *inode; - struct hugetlbfs_sb_info *sbinfo = HUGETLBFS_SB(sb); - - if (sbinfo->free_inodes >= 0) { - spin_lock(&sbinfo->stat_lock); - if (!sbinfo->free_inodes) { - spin_unlock(&sbinfo->stat_lock); - return NULL; - } - sbinfo->free_inodes--; - spin_unlock(&sbinfo->stat_lock); - } inode = new_inode(sb); if (inode) { + struct hugetlbfs_inode_info *info; inode->i_mode = mode; inode->i_uid = uid; inode->i_gid = gid; - inode->i_blksize = HPAGE_SIZE; inode->i_blocks = 0; inode->i_mapping->a_ops = &hugetlbfs_aops; inode->i_mapping->backing_dev_info =&hugetlbfs_backing_dev_info; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + INIT_LIST_HEAD(&inode->i_mapping->private_list); + info = HUGETLBFS_I(inode); + mpol_shared_policy_init(&info->policy, MPOL_DEFAULT, NULL); switch (mode & S_IFMT) { default: init_special_inode(inode, mode, dev); @@ -396,7 +370,7 @@ static struct inode *hugetlbfs_get_inode(struct super_block *sb, uid_t uid, inode->i_fop = &simple_dir_operations; /* directory inodes start off with i_nlink == 2 (for "." entry) */ - inode->i_nlink++; + inc_nlink(inode); break; case S_IFLNK: inode->i_op = &page_symlink_inode_operations; @@ -437,7 +411,7 @@ static int hugetlbfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) { int retval = hugetlbfs_mknod(dir, dentry, mode | S_IFDIR, 0); if (!retval) - dir->i_nlink++; + inc_nlink(dir); return retval; } @@ -475,25 +449,32 @@ static int hugetlbfs_symlink(struct inode *dir, } /* - * For direct-IO reads into hugetlb pages + * mark the head page dirty */ -int hugetlbfs_set_page_dirty(struct page *page) +static int hugetlbfs_set_page_dirty(struct page *page) { + struct page *head = (struct page *)page_private(page); + + SetPageDirty(head); return 0; } -static int hugetlbfs_statfs(struct super_block *sb, struct kstatfs *buf) +static int hugetlbfs_statfs(struct dentry *dentry, struct kstatfs *buf) { - struct hugetlbfs_sb_info *sbinfo = HUGETLBFS_SB(sb); + struct hugetlbfs_sb_info *sbinfo = HUGETLBFS_SB(dentry->d_sb); buf->f_type = HUGETLBFS_MAGIC; buf->f_bsize = HPAGE_SIZE; if (sbinfo) { spin_lock(&sbinfo->stat_lock); - buf->f_blocks = sbinfo->max_blocks; - buf->f_bavail = buf->f_bfree = sbinfo->free_blocks; - buf->f_files = sbinfo->max_inodes; - buf->f_ffree = sbinfo->free_inodes; + /* If no limits set, just report 0 for max/free/used + * blocks, like simple_statfs() */ + if (sbinfo->max_blocks >= 0) { + buf->f_blocks = sbinfo->max_blocks; + buf->f_bavail = buf->f_bfree = sbinfo->free_blocks; + buf->f_files = sbinfo->max_inodes; + buf->f_ffree = sbinfo->free_inodes; + } spin_unlock(&sbinfo->stat_lock); } buf->f_namelen = NAME_MAX; @@ -510,14 +491,73 @@ static void hugetlbfs_put_super(struct super_block *sb) } } -static struct address_space_operations hugetlbfs_aops = { +static inline int hugetlbfs_dec_free_inodes(struct hugetlbfs_sb_info *sbinfo) +{ + if (sbinfo->free_inodes >= 0) { + spin_lock(&sbinfo->stat_lock); + if (unlikely(!sbinfo->free_inodes)) { + spin_unlock(&sbinfo->stat_lock); + return 0; + } + sbinfo->free_inodes--; + spin_unlock(&sbinfo->stat_lock); + } + + return 1; +} + +static void hugetlbfs_inc_free_inodes(struct hugetlbfs_sb_info *sbinfo) +{ + if (sbinfo->free_inodes >= 0) { + spin_lock(&sbinfo->stat_lock); + sbinfo->free_inodes++; + spin_unlock(&sbinfo->stat_lock); + } +} + + +static struct kmem_cache *hugetlbfs_inode_cachep; + +static struct inode *hugetlbfs_alloc_inode(struct super_block *sb) +{ + struct hugetlbfs_sb_info *sbinfo = HUGETLBFS_SB(sb); + struct hugetlbfs_inode_info *p; + + if (unlikely(!hugetlbfs_dec_free_inodes(sbinfo))) + return NULL; + p = kmem_cache_alloc(hugetlbfs_inode_cachep, GFP_KERNEL); + if (unlikely(!p)) { + hugetlbfs_inc_free_inodes(sbinfo); + return NULL; + } + return &p->vfs_inode; +} + +static void hugetlbfs_destroy_inode(struct inode *inode) +{ + hugetlbfs_inc_free_inodes(HUGETLBFS_SB(inode->i_sb)); + mpol_free_shared_policy(&HUGETLBFS_I(inode)->policy); + kmem_cache_free(hugetlbfs_inode_cachep, HUGETLBFS_I(inode)); +} + +static const struct address_space_operations hugetlbfs_aops = { .readpage = hugetlbfs_readpage, .prepare_write = hugetlbfs_prepare_write, .commit_write = hugetlbfs_commit_write, .set_page_dirty = hugetlbfs_set_page_dirty, }; -struct file_operations hugetlbfs_file_operations = { + +static void init_once(void *foo, struct kmem_cache *cachep, unsigned long flags) +{ + struct hugetlbfs_inode_info *ei = (struct hugetlbfs_inode_info *)foo; + + if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == + SLAB_CTOR_CONSTRUCTOR) + inode_init_once(&ei->vfs_inode); +} + +const struct file_operations hugetlbfs_file_operations = { .mmap = hugetlbfs_file_mmap, .fsync = simple_sync_file, .get_unmapped_area = hugetlb_get_unmapped_area, @@ -541,7 +581,10 @@ static struct inode_operations hugetlbfs_inode_operations = { }; static struct super_operations hugetlbfs_ops = { + .alloc_inode = hugetlbfs_alloc_inode, + .destroy_inode = hugetlbfs_destroy_inode, .statfs = hugetlbfs_statfs, + .delete_inode = hugetlbfs_delete_inode, .drop_inode = hugetlbfs_drop_inode, .put_super = hugetlbfs_put_super, }; @@ -577,7 +620,6 @@ hugetlbfs_parse_options(char *options, struct hugetlbfs_config *pconfig) do_div(size, 100); rest++; } - size &= HPAGE_MASK; pconfig->nr_blocks = (size >> HPAGE_SHIFT); value = rest; } else if (!strcmp(opt,"nr_inodes")) { @@ -620,10 +662,12 @@ hugetlbfs_fill_super(struct super_block *sb, void *data, int silent) sbinfo->free_blocks = config.nr_blocks; sbinfo->max_inodes = config.nr_inodes; sbinfo->free_inodes = config.nr_inodes; + sb->s_maxbytes = MAX_LFS_FILESIZE; sb->s_blocksize = HPAGE_SIZE; sb->s_blocksize_bits = HPAGE_SHIFT; sb->s_magic = HUGETLBFS_MAGIC; sb->s_op = &hugetlbfs_ops; + sb->s_time_gran = 1; inode = hugetlbfs_get_inode(sb, config.uid, config.gid, S_IFDIR | config.mode, 0); if (!inode) @@ -669,10 +713,10 @@ void hugetlb_put_quota(struct address_space *mapping) } } -static struct super_block *hugetlbfs_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int hugetlbfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, struct vfsmount *mnt) { - return get_sb_nodev(fs_type, flags, data, hugetlbfs_fill_super); + return get_sb_nodev(fs_type, flags, data, hugetlbfs_fill_super, mnt); } static struct file_system_type hugetlbfs_fs_type = { @@ -683,44 +727,37 @@ static struct file_system_type hugetlbfs_fs_type = { static struct vfsmount *hugetlbfs_vfsmount; -/* - * Return the next identifier for a shm file - */ -static unsigned long hugetlbfs_counter(void) +static int can_do_hugetlb_shm(void) { - static spinlock_t lock = SPIN_LOCK_UNLOCKED; - static unsigned long counter; - unsigned long ret; - - spin_lock(&lock); - ret = ++counter; - spin_unlock(&lock); - return ret; + return likely(capable(CAP_IPC_LOCK) || + in_group_p(sysctl_hugetlb_shm_group) || + can_do_mlock()); } struct file *hugetlb_zero_setup(size_t size) { - int error; + int error = -ENOMEM; struct file *file; struct inode *inode; struct dentry *dentry, *root; struct qstr quick_string; char buf[16]; + static atomic_t counter; - if (!capable(CAP_IPC_LOCK)) + if (!can_do_hugetlb_shm()) return ERR_PTR(-EPERM); - if (!is_hugepage_mem_enough(size)) + if (!user_shm_lock(size, current->user)) return ERR_PTR(-ENOMEM); root = hugetlbfs_vfsmount->mnt_root; - snprintf(buf, 16, "%lu", hugetlbfs_counter()); + snprintf(buf, 16, "%u", atomic_inc_return(&counter)); quick_string.name = buf; quick_string.len = strlen(quick_string.name); quick_string.hash = 0; dentry = d_alloc(root, &quick_string); if (!dentry) - return ERR_PTR(-ENOMEM); + goto out_shm_unlock; error = -ENFILE; file = get_empty_filp(); @@ -733,20 +770,28 @@ struct file *hugetlb_zero_setup(size_t size) if (!inode) goto out_file; + error = -ENOMEM; + if (hugetlb_reserve_pages(inode, 0, size >> HPAGE_SHIFT)) + goto out_inode; + d_instantiate(dentry, inode); inode->i_size = size; inode->i_nlink = 0; - file->f_vfsmnt = mntget(hugetlbfs_vfsmount); - file->f_dentry = dentry; + file->f_path.mnt = mntget(hugetlbfs_vfsmount); + file->f_path.dentry = dentry; file->f_mapping = inode->i_mapping; file->f_op = &hugetlbfs_file_operations; file->f_mode = FMODE_WRITE | FMODE_READ; return file; +out_inode: + iput(inode); out_file: put_filp(file); out_dentry: dput(dentry); +out_shm_unlock: + user_shm_unlock(size, current->user); return ERR_PTR(error); } @@ -755,9 +800,15 @@ static int __init init_hugetlbfs_fs(void) int error; struct vfsmount *vfsmount; + hugetlbfs_inode_cachep = kmem_cache_create("hugetlbfs_inode_cache", + sizeof(struct hugetlbfs_inode_info), + 0, 0, init_once, NULL); + if (hugetlbfs_inode_cachep == NULL) + return -ENOMEM; + error = register_filesystem(&hugetlbfs_fs_type); if (error) - return error; + goto out; vfsmount = kern_mount(&hugetlbfs_fs_type); @@ -767,11 +818,16 @@ static int __init init_hugetlbfs_fs(void) } error = PTR_ERR(vfsmount); + + out: + if (error) + kmem_cache_destroy(hugetlbfs_inode_cachep); return error; } static void __exit exit_hugetlbfs_fs(void) { + kmem_cache_destroy(hugetlbfs_inode_cachep); unregister_filesystem(&hugetlbfs_fs_type); }