| Start/ | End/ | |||
| True | False | - | Line | Source |
| 1 | /* | |||
| 2 | * hugetlbpage-backed filesystem. Based on ramfs. | |||
| 3 | * | |||
| 4 | * William Irwin, 2002 | |||
| 5 | * | |||
| 6 | * Copyright (C) 2002 Linus Torvalds. | |||
| 7 | */ | |||
| 8 | ||||
| 9 | #include <linux/module.h> | |||
| 10 | #include <linux/thread_info.h> | |||
| 11 | #include <asm/current.h> | |||
| 12 | #include <linux/sched.h> /* remove ASAP */ | |||
| 13 | #include <linux/fs.h> | |||
| 14 | #include <linux/mount.h> | |||
| 15 | #include <linux/file.h> | |||
| 16 | #include <linux/writeback.h> | |||
| 17 | #include <linux/pagemap.h> | |||
| 18 | #include <linux/highmem.h> | |||
| 19 | #include <linux/init.h> | |||
| 20 | #include <linux/string.h> | |||
| 21 | #include <linux/capability.h> | |||
| 22 | #include <linux/backing-dev.h> | |||
| 23 | #include <linux/hugetlb.h> | |||
| 24 | #include <linux/pagevec.h> | |||
| 25 | #include <linux/quotaops.h> | |||
| 26 | #include <linux/slab.h> | |||
| 27 | #include <linux/dnotify.h> | |||
| 28 | #include <linux/statfs.h> | |||
| 29 | #include <linux/security.h> | |||
| 30 | ||||
| 31 | #include <asm/uaccess.h> | |||
| 32 | ||||
| 33 | /* some random number */ | |||
| 34 | #define HUGETLBFS_MAGIC 0x958458f6 | |||
| 35 | ||||
| 36 | static struct super_operations hugetlbfs_ops; | |||
| 37 | static struct address_space_operations hugetlbfs_aops; | |||
| 38 | struct file_operations hugetlbfs_file_operations; | |||
| 39 | static struct inode_operations hugetlbfs_dir_inode_operations; | |||
| 40 | static struct inode_operations hugetlbfs_inode_operations; | |||
| 41 | ||||
| 42 | static struct backing_dev_info hugetlbfs_backing_dev_info = { | |||
| 43 | .ra_pages = 0, /* No readahead */ | |||
| 44 | .capabilities = BDI_CAP_NO_ACCT_DIRTY | BDI_CAP_NO_WRITEBACK, | |||
| 45 | }; | |||
| 46 | ||||
| 47 | int sysctl_hugetlb_shm_group; | |||
| 48 | ||||
| 0 | 0 | - | 49 | static void huge_pagevec_release(struct pagevec *pvec) |
| 50 | { | |||
| 51 | int i; | |||
| 52 | ||||
| 0 | 0 | - | 53 | for (i = 0; i < pagevec_count(pvec); ++i) |
| 54 | put_page(pvec->pages[i]); | |||
| 55 | ||||
| 56 | pagevec_reinit(pvec); | |||
| 57 | } | |||
| 58 | ||||
| 59 | /* | |||
| 60 | * huge_pages_needed tries to determine the number of new huge pages that | |||
| 61 | * will be required to fully populate this VMA. This will be equal to | |||
| 62 | * the size of the VMA in huge pages minus the number of huge pages | |||
| 63 | * (covered by this VMA) that are found in the page cache. | |||
| 64 | * | |||
| 65 | * Result is in bytes to be compatible with is_hugepage_mem_enough() | |||
| 66 | */ | |||
| 67 | static unsigned long | |||
| 0 | 0 | - | 68 | huge_pages_needed(struct address_space *mapping, struct vm_area_struct *vma) |
| 69 | { | |||
| 70 | int i; | |||
| 71 | struct pagevec pvec; | |||
| 72 | unsigned long start = vma->vm_start; | |||
| 73 | unsigned long end = vma->vm_end; | |||
| 74 | unsigned long hugepages = (end - start) >> HPAGE_SHIFT; | |||
| 75 | pgoff_t next = vma->vm_pgoff >> (HPAGE_SHIFT - PAGE_SHIFT); | |||
| 76 | pgoff_t endpg = next + hugepages; | |||
| 77 | ||||
| 78 | pagevec_init(&pvec, 0); | |||
| 0 | 0 | - | 79 | while (next < endpg) { |
| 0 | 0 | - | 80 | if (!pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) |
| 0 | - | 81 | break; | |
| 0 | 0 | - | 82 | for (i = 0; i < pagevec_count(&pvec); i++) { |
| 83 | struct page *page = pvec.pages[i]; | |||
| 0 | 0 | - | 84 | if (page->index > next) |
| 85 | next = page->index; | |||
| 0 | 0 | - | 86 | if (page->index >= endpg) |
| 0 | - | 87 | break; | |
| 88 | next++; | |||
| 89 | hugepages--; | |||
| 90 | } | |||
| 91 | huge_pagevec_release(&pvec); | |||
| 92 | } | |||
| 0 | - | 93 | return hugepages << HPAGE_SHIFT; | |
| 94 | } | |||
| 95 | ||||
| 0 | 0 | - | 96 | static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma) |
| 97 | { | |||
| 98 | struct inode *inode = file->f_dentry->d_inode; | |||
| 99 | struct address_space *mapping = inode->i_mapping; | |||
| 100 | unsigned long bytes; | |||
| 101 | loff_t len, vma_len; | |||
| 102 | int ret; | |||
| 103 | ||||
| 0 | 0 | - | 104 | if (vma->vm_pgoff & (HPAGE_SIZE / PAGE_SIZE - 1)) |
| 0 | - | 105 | return -EINVAL; | |
| 106 | ||||
| 0 | 0 | - | 107 | if (vma->vm_start & ~HPAGE_MASK) |
| 0 | - | 108 | return -EINVAL; | |
| 109 | ||||
| 0 | 0 | - | 110 | if (vma->vm_end & ~HPAGE_MASK) |
| 0 | - | 111 | return -EINVAL; | |
| 112 | ||||
| 0 | 0 | - | 113 | if (vma->vm_end - vma->vm_start < HPAGE_SIZE) |
| 0 | - | 114 | return -EINVAL; | |
| 115 | ||||
| 116 | bytes = huge_pages_needed(mapping, vma); | |||
| 0 | 0 | - | 117 | if (!is_hugepage_mem_enough(bytes)) |
| 0 | - | 118 | return -ENOMEM; | |
| 119 | ||||
| 120 | vma_len = (loff_t)(vma->vm_end - vma->vm_start); | |||
| 121 | ||||
| 122 | mutex_lock(&inode->i_mutex); | |||
| 123 | file_accessed(file); | |||
| 124 | vma->vm_flags |= VM_HUGETLB | VM_RESERVED; | |||
| 125 | vma->vm_ops = &hugetlb_vm_ops; | |||
| 126 | ||||
| 127 | ret = -ENOMEM; | |||
| 128 | len = vma_len + ((loff_t)vma->vm_pgoff << PAGE_SHIFT); | |||
| 0 | 0 | - | 129 | if (!(vma->vm_flags & VM_WRITE) && len > inode->i_size) |
| 0 | - | 129 | !(F) && T | |
| 0 | - | 129 | !(T) && _ | |
| 0 | - | 129 | !(F) && F | |
| 0 | - | 130 | goto out; | |
| 131 | ||||
| 132 | ret = 0; | |||
| 133 | hugetlb_prefault_arch_hook(vma->vm_mm); | |||
| 0 | 0 | - | 133 | do-while (0) |
| 0 | 0 | - | 134 | if (inode->i_size < len) |
| 135 | inode->i_size = len; | |||
| 136 | out: | |||
| 137 | mutex_unlock(&inode->i_mutex); | |||
| 138 | ||||
| 0 | - | 139 | return ret; | |
| 140 | } | |||
| 141 | ||||
| 142 | /* | |||
| 143 | * Called under down_write(mmap_sem). | |||
| 144 | */ | |||
| 145 | ||||
| 146 | #ifdef HAVE_ARCH_HUGETLB_UNMAPPED_AREA | |||
| 147 | unsigned long hugetlb_get_unmapped_area(struct file *file, unsigned long addr, | |||
| 148 | unsigned long len, unsigned long pgoff, unsigned long flags); | |||
| 149 | #else | |||
| 150 | static unsigned long | |||
| 0 | 0 | - | 151 | hugetlb_get_unmapped_area(struct file *file, unsigned long addr, |
| 152 | unsigned long len, unsigned long pgoff, unsigned long flags) | |||
| 153 | { | |||
| 154 | struct mm_struct *mm = current->mm; | |||
| 155 | struct vm_area_struct *vma; | |||
| 156 | unsigned long start_addr; | |||
| 157 | ||||
| 0 | 0 | - | 158 | if (len & ~HPAGE_MASK) |
| 0 | - | 159 | return -EINVAL; | |
| 0 | 0 | - | 160 | if (len > TASK_SIZE) |
| 0 | 0 | - | 160 | ternary-?: test_ti_thread_flag ( current_threa.. |
| 0 | 0 | - | 160 | ternary-?: ( get_current ( ) -> personality & .. |
| 0 | - | 161 | return -ENOMEM; | |
| 162 | ||||
| 0 | 0 | - | 163 | if (addr) { |
| 164 | addr = ALIGN(addr, HPAGE_SIZE); | |||
| 165 | vma = find_vma(mm, addr); | |||
| 166 | if (TASK_SIZE - len >= addr && | |||
| 0 | 0 | - | 167 | (!vma || addr + len <= vma->vm_start)) |
| 0 | - | 167 | T && (T || _) | |
| 0 | - | 167 | T && (F || T) | |
| 0 | - | 167 | T && (F || F) | |
| 0 | - | 167 | F && (_ || _) | |
| 0 | 0 | - | 167 | ternary-?: test_ti_thread_flag ( current_thr.. |
| 0 | 0 | - | 167 | ternary-?: ( get_current ( ) -> personality .. |
| 0 | - | 168 | return addr; | |
| 169 | } | |||
| 170 | ||||
| 171 | start_addr = mm->free_area_cache; | |||
| 172 | ||||
| 0 | 0 | - | 173 | if (len <= mm->cached_hole_size) |
| 174 | start_addr = TASK_UNMAPPED_BASE; | |||
| 0 | 0 | - | 174 | ternary-?: test_ti_thread_flag ( current_thr.. |
| 0 | 0 | - | 174 | ternary-?: ( get_current ( ) -> personality .. |
| 175 | ||||
| 176 | full_search: | |||
| 177 | addr = ALIGN(start_addr, HPAGE_SIZE); | |||
| 178 | ||||
| 0 | 0 | - | 179 | for (vma = find_vma(mm, addr); ; vma = vma->vm_next) { |
| 180 | /* At this point: (!vma || addr < vma->vm_end). */ | |||
| 0 | 0 | - | 181 | if (TASK_SIZE - len < addr) { |
| 0 | 0 | - | 181 | ternary-?: test_ti_thread_flag ( current_thr.. |
| 0 | 0 | - | 181 | ternary-?: ( get_current ( ) -> personality .. |
| 182 | /* | |||
| 183 | * Start a new search - just in case we missed | |||
| 184 | * some holes. | |||
| 185 | */ | |||
| 0 | 0 | - | 186 | if (start_addr != TASK_UNMAPPED_BASE) { |
| 0 | 0 | - | 186 | ternary-?: test_ti_thread_flag ( current_t.. |
| 0 | 0 | - | 186 | ternary-?: ( get_current ( ) -> personalit.. |
| 187 | start_addr = TASK_UNMAPPED_BASE; | |||
| 0 | 0 | - | 187 | ternary-?: test_ti_thread_flag ( current.. |
| 0 | 0 | - | 187 | ternary-?: ( get_current ( ) -> personal.. |
| 0 | - | 188 | goto full_search; | |
| 189 | } | |||
| 0 | - | 190 | return -ENOMEM; | |
| 191 | } | |||
| 192 | ||||
| 0 | 0 | - | 193 | if (!vma || addr + len <= vma->vm_start) |
| 0 | - | 193 | T || _ | |
| 0 | - | 193 | F || T | |
| 0 | - | 193 | F || F | |
| 0 | - | 194 | return addr; | |
| 195 | addr = ALIGN(vma->vm_end, HPAGE_SIZE); | |||
| 196 | } | |||
| 197 | } | |||
| 198 | #endif | |||
| 199 | ||||
| 200 | /* | |||
| 201 | * Read a page. Again trivial. If it didn't already exist | |||
| 202 | * in the page cache, it is zero-filled. | |||
| 203 | */ | |||
| 0 | 0 | - | 204 | static int hugetlbfs_readpage(struct file *file, struct page * page) |
| 205 | { | |||
| 206 | unlock_page(page); | |||
| 0 | - | 207 | return -EINVAL; | |
| 208 | } | |||
| 209 | ||||
| 0 | 0 | - | 210 | static int hugetlbfs_prepare_write(struct file *file, |
| 211 | struct page *page, unsigned offset, unsigned to) | |||
| 212 | { | |||
| 0 | - | 213 | return -EINVAL; | |
| 214 | } | |||
| 215 | ||||
| 0 | 0 | - | 216 | static int hugetlbfs_commit_write(struct file *file, |
| 217 | struct page *page, unsigned offset, unsigned to) | |||
| 218 | { | |||
| 0 | - | 219 | return -EINVAL; | |
| 220 | } | |||
| 221 | ||||
| 0 | 0 | - | 222 | static void truncate_huge_page(struct page *page) |
| 223 | { | |||
| 224 | clear_page_dirty(page); | |||
| 225 | ClearPageUptodate(page); | |||
| 226 | remove_from_page_cache(page); | |||
| 227 | put_page(page); | |||
| 228 | } | |||
| 229 | ||||
| 0 | 0 | - | 230 | static void truncate_hugepages(struct address_space *mapping, loff_t lstart) |
| 231 | { | |||
| 232 | const pgoff_t start = lstart >> HPAGE_SHIFT; | |||
| 233 | struct pagevec pvec; | |||
| 234 | pgoff_t next; | |||
| 235 | int i; | |||
| 236 | ||||
| 237 | pagevec_init(&pvec, 0); | |||
| 238 | next = start; | |||
| 0 | 0 | - | 239 | while (1) { |
| 0 | 0 | - | 240 | if (!pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) { |
| 0 | 0 | - | 241 | if (next == start) |
| 0 | - | 242 | break; | |
| 243 | next = start; | |||
| 0 | - | 244 | continue; | |
| 245 | } | |||
| 246 | ||||
| 0 | 0 | - | 247 | for (i = 0; i < pagevec_count(&pvec); ++i) { |
| 248 | struct page *page = pvec.pages[i]; | |||
| 249 | ||||
| 250 | lock_page(page); | |||
| 0 | 0 | - | 251 | if (page->index > next) |
| 252 | next = page->index; | |||
| 253 | ++next; | |||
| 254 | truncate_huge_page(page); | |||
| 255 | unlock_page(page); | |||
| 256 | hugetlb_put_quota(mapping); | |||
| 257 | } | |||
| 258 | huge_pagevec_release(&pvec); | |||
| 259 | } | |||
| 260 | BUG_ON(!lstart && mapping->nrpages); | |||
| 0 | 0 | - | 260 | if (__builtin_expect ( ! ! ( ( ! lstart && m.. |
| 0 | 0 | - | 260 | do-while (0) |
| 261 | } | |||
| 262 | ||||
| 0 | 0 | - | 263 | static void hugetlbfs_delete_inode(struct inode *inode) |
| 264 | { | |||
| 0 | 0 | - | 265 | if (inode->i_data.nrpages) |
| 266 | truncate_hugepages(&inode->i_data, 0); | |||
| 267 | clear_inode(inode); | |||
| 268 | } | |||
| 269 | ||||
| 0 | 0 | - | 270 | static void hugetlbfs_forget_inode(struct inode *inode) |
| 271 | { | |||
| 272 | struct super_block *sb = inode->i_sb; | |||
| 273 | ||||
| 0 | 0 | - | 274 | if (!hlist_unhashed(&inode->i_hash)) { |
| 0 | 0 | - | 275 | if (!(inode->i_state & (I_DIRTY|I_LOCK))) |
| 276 | list_move(&inode->i_list, &inode_unused); | |||
| 277 | inodes_stat.nr_unused++; | |||
| 0 | 0 | - | 278 | if (!sb || (sb->s_flags & MS_ACTIVE)) { |
| 0 | - | 278 | T || (_) | |
| 0 | - | 278 | F || (T) | |
| 0 | - | 278 | F || (F) | |
| 279 | spin_unlock(&inode_lock); | |||
| 279 | do | |||
| 0 | 0 | - | 279 | do-while (0) |
| 0 | 0 | - | 279 | do-while (0) |
| 0 | - | 280 | return; | |
| 281 | } | |||
| 282 | inode->i_state |= I_WILL_FREE; | |||
| 283 | spin_unlock(&inode_lock); | |||
| 283 | do | |||
| 0 | 0 | - | 283 | do-while (0) |
| 0 | 0 | - | 283 | do-while (0) |
| 284 | /* | |||
| 285 | * write_inode_now is a noop as we set BDI_CAP_NO_WRITEBACK | |||
| 286 | * in our backing_dev_info. | |||
| 287 | */ | |||
| 288 | write_inode_now(inode, 1); | |||
| 289 | spin_lock(&inode_lock); | |||
| 289 | do | |||
| 0 | 0 | - | 289 | do-while (0) |
| 0 | 0 | - | 289 | do-while (0) |
| 290 | inode->i_state &= ~I_WILL_FREE; | |||
| 291 | inodes_stat.nr_unused--; | |||
| 292 | hlist_del_init(&inode->i_hash); | |||
| 293 | } | |||
| 294 | list_del_init(&inode->i_list); | |||
| 295 | list_del_init(&inode->i_sb_list); | |||
| 296 | inode->i_state |= I_FREEING; | |||
| 297 | inodes_stat.nr_inodes--; | |||
| 298 | spin_unlock(&inode_lock); | |||
| 298 | do | |||
| 0 | 0 | - | 298 | do-while (0) |
| 0 | 0 | - | 298 | do-while (0) |
| 0 | 0 | - | 299 | if (inode->i_data.nrpages) |
| 300 | truncate_hugepages(&inode->i_data, 0); | |||
| 301 | clear_inode(inode); | |||
| 302 | destroy_inode(inode); | |||
| 303 | } | |||
| 304 | ||||
| 0 | 0 | - | 305 | static void hugetlbfs_drop_inode(struct inode *inode) |
| 306 | { | |||
| 0 | 0 | - | 307 | if (!inode->i_nlink) |
| 308 | generic_delete_inode(inode); | |||
| 309 | else | |||
| 310 | hugetlbfs_forget_inode(inode); | |||
| 311 | } | |||
| 312 | ||||
| 313 | /* | |||
| 314 | * h_pgoff is in HPAGE_SIZE units. | |||
| 315 | * vma->vm_pgoff is in PAGE_SIZE units. | |||
| 316 | */ | |||
| 317 | static inline void | |||
| 0 | 0 | - | 318 | hugetlb_vmtruncate_list(struct prio_tree_root *root, unsigned long h_pgoff) |
| 319 | { | |||
| 320 | struct vm_area_struct *vma; | |||
| 321 | struct prio_tree_iter iter; | |||
| 322 | ||||
| 0 | 0 | - | 323 | vma_prio_tree_foreach(vma, &iter, root, h_pgoff, ULONG_MAX) { |
| 324 | unsigned long h_vm_pgoff; | |||
| 325 | unsigned long v_offset; | |||
| 326 | ||||
| 327 | h_vm_pgoff = vma->vm_pgoff >> (HPAGE_SHIFT - PAGE_SHIFT); | |||
| 328 | v_offset = (h_pgoff - h_vm_pgoff) << HPAGE_SHIFT; | |||
| 329 | /* | |||
| 330 | * Is this VMA fully outside the truncation point? | |||
| 331 | */ | |||
| 0 | 0 | - | 332 | if (h_vm_pgoff >= h_pgoff) |
| 333 | v_offset = 0; | |||
| 334 | ||||
| 335 | unmap_hugepage_range(vma, | |||
| 336 | vma->vm_start + v_offset, vma->vm_end); | |||
| 337 | } | |||
| 338 | } | |||
| 339 | ||||
| 340 | /* | |||
| 341 | * Expanding truncates are not allowed. | |||
| 342 | */ | |||
| 0 | 0 | - | 343 | static int hugetlb_vmtruncate(struct inode *inode, loff_t offset) |
| 344 | { | |||
| 345 | unsigned long pgoff; | |||
| 346 | struct address_space *mapping = inode->i_mapping; | |||
| 347 | ||||
| 0 | 0 | - | 348 | if (offset > inode->i_size) |
| 0 | - | 349 | return -EINVAL; | |
| 350 | ||||
| 351 | BUG_ON(offset & ~HPAGE_MASK); | |||
| 0 | 0 | - | 351 | if (__builtin_expect ( ! ! ( ( offset & ~ ( .. |
| 0 | 0 | - | 351 | do-while (0) |
| 352 | pgoff = offset >> HPAGE_SHIFT; | |||
| 353 | ||||
| 354 | inode->i_size = offset; | |||
| 355 | spin_lock(&mapping->i_mmap_lock); | |||
| 355 | do | |||
| 0 | 0 | - | 355 | do-while (0) |
| 0 | 0 | - | 355 | do-while (0) |
| 0 | 0 | - | 356 | if (!prio_tree_empty(&mapping->i_mmap)) |
| 357 | hugetlb_vmtruncate_list(&mapping->i_mmap, pgoff); | |||
| 358 | spin_unlock(&mapping->i_mmap_lock); | |||
| 358 | do | |||
| 0 | 0 | - | 358 | do-while (0) |
| 0 | 0 | - | 358 | do-while (0) |
| 359 | truncate_hugepages(mapping, offset); | |||
| 0 | - | 360 | return 0; | |
| 361 | } | |||
| 362 | ||||
| 0 | 0 | - | 363 | static int hugetlbfs_setattr(struct dentry *dentry, struct iattr *attr) |
| 364 | { | |||
| 365 | struct inode *inode = dentry->d_inode; | |||
| 366 | int error; | |||
| 367 | unsigned int ia_valid = attr->ia_valid; | |||
| 368 | ||||
| 369 | BUG_ON(!inode); | |||
| 0 | 0 | - | 369 | if (__builtin_expect ( ! ! ( ( ! inode ) != .. |
| 0 | 0 | - | 369 | do-while (0) |
| 370 | ||||
| 371 | error = inode_change_ok(inode, attr); | |||
| 0 | 0 | - | 372 | if (error) |
| 0 | - | 373 | goto out; | |
| 374 | ||||
| 0 | 0 | - | 375 | if (ia_valid & ATTR_SIZE) { |
| 376 | error = -EINVAL; | |||
| 0 | 0 | - | 377 | if (!(attr->ia_size & ~HPAGE_MASK)) |
| 378 | error = hugetlb_vmtruncate(inode, attr->ia_size); | |||
| 0 | 0 | - | 379 | if (error) |
| 0 | - | 380 | goto out; | |
| 381 | attr->ia_valid &= ~ATTR_SIZE; | |||
| 382 | } | |||
| 383 | error = inode_setattr(inode, attr); | |||
| 384 | out: | |||
| 0 | - | 385 | return error; | |
| 386 | } | |||
| 387 | ||||
| 6 | 0 | 388 | static struct inode *hugetlbfs_get_inode(struct super_block *sb, uid_t uid, | |
| 389 | gid_t gid, int mode, dev_t dev) | |||
| 390 | { | |||
| 391 | struct inode *inode; | |||
| 392 | ||||
| 393 | inode = new_inode(sb); | |||
| 6 | 0 | - | 394 | if (inode) { |
| 395 | struct hugetlbfs_inode_info *info; | |||
| 396 | inode->i_mode = mode; | |||
| 397 | inode->i_uid = uid; | |||
| 398 | inode->i_gid = gid; | |||
| 399 | inode->i_blksize = HPAGE_SIZE; | |||
| 400 | inode->i_blocks = 0; | |||
| 401 | inode->i_mapping->a_ops = &hugetlbfs_aops; | |||
| 402 | inode->i_mapping->backing_dev_info =&hugetlbfs_backing_dev_info; | |||
| 403 | inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; | |||
| 404 | info = HUGETLBFS_I(inode); | |||
| 405 | mpol_shared_policy_init(&info->policy, MPOL_DEFAULT, NULL); | |||
| 406 | switch (mode & S_IFMT) { | |||
| 0 | - | 407 | default: | |
| 408 | init_special_inode(inode, mode, dev); | |||
| 0 | - | 409 | break; | |
| 0 | - | 410 | case S_IFREG: | |
| 411 | inode->i_op = &hugetlbfs_inode_operations; | |||
| 412 | inode->i_fop = &hugetlbfs_file_operations; | |||
| 0 | - | 413 | break; | |
| 6 | 414 | case S_IFDIR: | ||
| 415 | inode->i_op = &hugetlbfs_dir_inode_operations; | |||
| 416 | inode->i_fop = &simple_dir_operations; | |||
| 417 | ||||
| 418 | /* directory inodes start off with i_nlink == 2 (for "." entry) */ | |||
| 419 | inode->i_nlink++; | |||
| 6 | 420 | break; | ||
| 0 | - | 421 | case S_IFLNK: | |
| 422 | inode->i_op = &page_symlink_inode_operations; | |||
| 0 | - | 423 | break; | |
| 424 | } | |||
| 425 | } | |||
| 6 | 426 | return inode; | ||
| 427 | } | |||
| 428 | ||||
| 429 | /* | |||
| 430 | * File creation. Allocate an inode, and we're done.. | |||
| 431 | */ | |||
| 0 | 0 | - | 432 | static int hugetlbfs_mknod(struct inode *dir, |
| 433 | struct dentry *dentry, int mode, dev_t dev) | |||
| 434 | { | |||
| 435 | struct inode *inode; | |||
| 436 | int error = -ENOSPC; | |||
| 437 | gid_t gid; | |||
| 438 | ||||
| 0 | 0 | - | 439 | if (dir->i_mode & S_ISGID) { |
| 440 | gid = dir->i_gid; | |||
| 0 | 0 | - | 441 | if (S_ISDIR(mode)) |
| 442 | mode |= S_ISGID; | |||
| 443 | } else { | |||
| 444 | gid = current->fsgid; | |||
| 445 | } | |||
| 446 | inode = hugetlbfs_get_inode(dir->i_sb, current->fsuid, gid, mode, dev); | |||
| 0 | 0 | - | 447 | if (inode) { |
| 448 | dir->i_ctime = dir->i_mtime = CURRENT_TIME; | |||
| 449 | d_instantiate(dentry, inode); | |||
| 450 | dget(dentry); /* Extra count - pin the dentry in core */ | |||
| 451 | error = 0; | |||
| 452 | } | |||
| 0 | - | 453 | return error; | |
| 454 | } | |||
| 455 | ||||
| 0 | 0 | - | 456 | static int hugetlbfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) |
| 457 | { | |||
| 458 | int retval = hugetlbfs_mknod(dir, dentry, mode | S_IFDIR, 0); | |||
| 0 | 0 | - | 459 | if (!retval) |
| 460 | dir->i_nlink++; | |||
| 0 | - | 461 | return retval; | |
| 462 | } | |||
| 463 | ||||
| 0 | 0 | - | 464 | static int hugetlbfs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd) |
| 465 | { | |||
| 0 | - | 466 | return hugetlbfs_mknod(dir, dentry, mode | S_IFREG, 0); | |
| 467 | } | |||
| 468 | ||||
| 0 | 0 | - | 469 | static int hugetlbfs_symlink(struct inode *dir, |
| 470 | struct dentry *dentry, const char *symname) | |||
| 471 | { | |||