diff options
Diffstat (limited to 'fs/orangefs/inode.c')
-rw-r--r-- | fs/orangefs/inode.c | 475 |
1 files changed, 475 insertions, 0 deletions
diff --git a/fs/orangefs/inode.c b/fs/orangefs/inode.c new file mode 100644 index 000000000000..2382e267b49e --- /dev/null +++ b/fs/orangefs/inode.c | |||
@@ -0,0 +1,475 @@ | |||
1 | /* | ||
2 | * (C) 2001 Clemson University and The University of Chicago | ||
3 | * | ||
4 | * See COPYING in top-level directory. | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | * Linux VFS inode operations. | ||
9 | */ | ||
10 | |||
11 | #include "protocol.h" | ||
12 | #include "orangefs-kernel.h" | ||
13 | #include "orangefs-bufmap.h" | ||
14 | |||
15 | static int read_one_page(struct page *page) | ||
16 | { | ||
17 | int ret; | ||
18 | int max_block; | ||
19 | ssize_t bytes_read = 0; | ||
20 | struct inode *inode = page->mapping->host; | ||
21 | const __u32 blocksize = PAGE_CACHE_SIZE; /* inode->i_blksize */ | ||
22 | const __u32 blockbits = PAGE_CACHE_SHIFT; /* inode->i_blkbits */ | ||
23 | struct iov_iter to; | ||
24 | struct bio_vec bv = {.bv_page = page, .bv_len = PAGE_SIZE}; | ||
25 | |||
26 | iov_iter_bvec(&to, ITER_BVEC | READ, &bv, 1, PAGE_SIZE); | ||
27 | |||
28 | gossip_debug(GOSSIP_INODE_DEBUG, | ||
29 | "orangefs_readpage called with page %p\n", | ||
30 | page); | ||
31 | |||
32 | max_block = ((inode->i_size / blocksize) + 1); | ||
33 | |||
34 | if (page->index < max_block) { | ||
35 | loff_t blockptr_offset = (((loff_t) page->index) << blockbits); | ||
36 | |||
37 | bytes_read = orangefs_inode_read(inode, | ||
38 | &to, | ||
39 | &blockptr_offset, | ||
40 | inode->i_size); | ||
41 | } | ||
42 | /* this will only zero remaining unread portions of the page data */ | ||
43 | iov_iter_zero(~0U, &to); | ||
44 | /* takes care of potential aliasing */ | ||
45 | flush_dcache_page(page); | ||
46 | if (bytes_read < 0) { | ||
47 | ret = bytes_read; | ||
48 | SetPageError(page); | ||
49 | } else { | ||
50 | SetPageUptodate(page); | ||
51 | if (PageError(page)) | ||
52 | ClearPageError(page); | ||
53 | ret = 0; | ||
54 | } | ||
55 | /* unlock the page after the ->readpage() routine completes */ | ||
56 | unlock_page(page); | ||
57 | return ret; | ||
58 | } | ||
59 | |||
60 | static int orangefs_readpage(struct file *file, struct page *page) | ||
61 | { | ||
62 | return read_one_page(page); | ||
63 | } | ||
64 | |||
65 | static int orangefs_readpages(struct file *file, | ||
66 | struct address_space *mapping, | ||
67 | struct list_head *pages, | ||
68 | unsigned nr_pages) | ||
69 | { | ||
70 | int page_idx; | ||
71 | int ret; | ||
72 | |||
73 | gossip_debug(GOSSIP_INODE_DEBUG, "orangefs_readpages called\n"); | ||
74 | |||
75 | for (page_idx = 0; page_idx < nr_pages; page_idx++) { | ||
76 | struct page *page; | ||
77 | |||
78 | page = list_entry(pages->prev, struct page, lru); | ||
79 | list_del(&page->lru); | ||
80 | if (!add_to_page_cache(page, | ||
81 | mapping, | ||
82 | page->index, | ||
83 | GFP_KERNEL)) { | ||
84 | ret = read_one_page(page); | ||
85 | gossip_debug(GOSSIP_INODE_DEBUG, | ||
86 | "failure adding page to cache, read_one_page returned: %d\n", | ||
87 | ret); | ||
88 | } else { | ||
89 | page_cache_release(page); | ||
90 | } | ||
91 | } | ||
92 | BUG_ON(!list_empty(pages)); | ||
93 | return 0; | ||
94 | } | ||
95 | |||
96 | static void orangefs_invalidatepage(struct page *page, | ||
97 | unsigned int offset, | ||
98 | unsigned int length) | ||
99 | { | ||
100 | gossip_debug(GOSSIP_INODE_DEBUG, | ||
101 | "orangefs_invalidatepage called on page %p " | ||
102 | "(offset is %u)\n", | ||
103 | page, | ||
104 | offset); | ||
105 | |||
106 | ClearPageUptodate(page); | ||
107 | ClearPageMappedToDisk(page); | ||
108 | return; | ||
109 | |||
110 | } | ||
111 | |||
112 | static int orangefs_releasepage(struct page *page, gfp_t foo) | ||
113 | { | ||
114 | gossip_debug(GOSSIP_INODE_DEBUG, | ||
115 | "orangefs_releasepage called on page %p\n", | ||
116 | page); | ||
117 | return 0; | ||
118 | } | ||
119 | |||
120 | /* | ||
121 | * Having a direct_IO entry point in the address_space_operations | ||
122 | * struct causes the kernel to allows us to use O_DIRECT on | ||
123 | * open. Nothing will ever call this thing, but in the future we | ||
124 | * will need to be able to use O_DIRECT on open in order to support | ||
125 | * AIO. Modeled after NFS, they do this too. | ||
126 | */ | ||
127 | /* | ||
128 | * static ssize_t orangefs_direct_IO(int rw, | ||
129 | * struct kiocb *iocb, | ||
130 | * struct iov_iter *iter, | ||
131 | * loff_t offset) | ||
132 | *{ | ||
133 | * gossip_debug(GOSSIP_INODE_DEBUG, | ||
134 | * "orangefs_direct_IO: %s\n", | ||
135 | * iocb->ki_filp->f_path.dentry->d_name.name); | ||
136 | * | ||
137 | * return -EINVAL; | ||
138 | *} | ||
139 | */ | ||
140 | |||
141 | struct backing_dev_info orangefs_backing_dev_info = { | ||
142 | .name = "orangefs", | ||
143 | .ra_pages = 0, | ||
144 | .capabilities = BDI_CAP_NO_ACCT_DIRTY | BDI_CAP_NO_WRITEBACK, | ||
145 | }; | ||
146 | |||
147 | /** ORANGEFS2 implementation of address space operations */ | ||
148 | const struct address_space_operations orangefs_address_operations = { | ||
149 | .readpage = orangefs_readpage, | ||
150 | .readpages = orangefs_readpages, | ||
151 | .invalidatepage = orangefs_invalidatepage, | ||
152 | .releasepage = orangefs_releasepage, | ||
153 | /* .direct_IO = orangefs_direct_IO */ | ||
154 | }; | ||
155 | |||
156 | static int orangefs_setattr_size(struct inode *inode, struct iattr *iattr) | ||
157 | { | ||
158 | struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode); | ||
159 | struct orangefs_kernel_op_s *new_op; | ||
160 | loff_t orig_size; | ||
161 | int ret = -EINVAL; | ||
162 | |||
163 | gossip_debug(GOSSIP_INODE_DEBUG, | ||
164 | "%s: %pU: Handle is %pU | fs_id %d | size is %llu\n", | ||
165 | __func__, | ||
166 | get_khandle_from_ino(inode), | ||
167 | &orangefs_inode->refn.khandle, | ||
168 | orangefs_inode->refn.fs_id, | ||
169 | iattr->ia_size); | ||
170 | |||
171 | /* Ensure that we have a up to date size, so we know if it changed. */ | ||
172 | ret = orangefs_inode_getattr(inode, 0, 1); | ||
173 | if (ret == -ESTALE) | ||
174 | ret = -EIO; | ||
175 | if (ret) { | ||
176 | gossip_err("%s: orangefs_inode_getattr failed, ret:%d:.\n", | ||
177 | __func__, ret); | ||
178 | return ret; | ||
179 | } | ||
180 | orig_size = i_size_read(inode); | ||
181 | |||
182 | truncate_setsize(inode, iattr->ia_size); | ||
183 | |||
184 | new_op = op_alloc(ORANGEFS_VFS_OP_TRUNCATE); | ||
185 | if (!new_op) | ||
186 | return -ENOMEM; | ||
187 | |||
188 | new_op->upcall.req.truncate.refn = orangefs_inode->refn; | ||
189 | new_op->upcall.req.truncate.size = (__s64) iattr->ia_size; | ||
190 | |||
191 | ret = service_operation(new_op, __func__, | ||
192 | get_interruptible_flag(inode)); | ||
193 | |||
194 | /* | ||
195 | * the truncate has no downcall members to retrieve, but | ||
196 | * the status value tells us if it went through ok or not | ||
197 | */ | ||
198 | gossip_debug(GOSSIP_INODE_DEBUG, | ||
199 | "orangefs: orangefs_truncate got return value of %d\n", | ||
200 | ret); | ||
201 | |||
202 | op_release(new_op); | ||
203 | |||
204 | if (ret != 0) | ||
205 | return ret; | ||
206 | |||
207 | /* | ||
208 | * Only change the c/mtime if we are changing the size or we are | ||
209 | * explicitly asked to change it. This handles the semantic difference | ||
210 | * between truncate() and ftruncate() as implemented in the VFS. | ||
211 | * | ||
212 | * The regular truncate() case without ATTR_CTIME and ATTR_MTIME is a | ||
213 | * special case where we need to update the times despite not having | ||
214 | * these flags set. For all other operations the VFS set these flags | ||
215 | * explicitly if it wants a timestamp update. | ||
216 | */ | ||
217 | if (orig_size != i_size_read(inode) && | ||
218 | !(iattr->ia_valid & (ATTR_CTIME | ATTR_MTIME))) { | ||
219 | iattr->ia_ctime = iattr->ia_mtime = | ||
220 | current_fs_time(inode->i_sb); | ||
221 | iattr->ia_valid |= ATTR_CTIME | ATTR_MTIME; | ||
222 | } | ||
223 | |||
224 | return ret; | ||
225 | } | ||
226 | |||
227 | /* | ||
228 | * Change attributes of an object referenced by dentry. | ||
229 | */ | ||
230 | int orangefs_setattr(struct dentry *dentry, struct iattr *iattr) | ||
231 | { | ||
232 | int ret = -EINVAL; | ||
233 | struct inode *inode = dentry->d_inode; | ||
234 | |||
235 | gossip_debug(GOSSIP_INODE_DEBUG, | ||
236 | "orangefs_setattr: called on %s\n", | ||
237 | dentry->d_name.name); | ||
238 | |||
239 | ret = inode_change_ok(inode, iattr); | ||
240 | if (ret) | ||
241 | goto out; | ||
242 | |||
243 | if ((iattr->ia_valid & ATTR_SIZE) && | ||
244 | iattr->ia_size != i_size_read(inode)) { | ||
245 | ret = orangefs_setattr_size(inode, iattr); | ||
246 | if (ret) | ||
247 | goto out; | ||
248 | } | ||
249 | |||
250 | setattr_copy(inode, iattr); | ||
251 | mark_inode_dirty(inode); | ||
252 | |||
253 | ret = orangefs_inode_setattr(inode, iattr); | ||
254 | gossip_debug(GOSSIP_INODE_DEBUG, | ||
255 | "orangefs_setattr: inode_setattr returned %d\n", | ||
256 | ret); | ||
257 | |||
258 | if (!ret && (iattr->ia_valid & ATTR_MODE)) | ||
259 | /* change mod on a file that has ACLs */ | ||
260 | ret = posix_acl_chmod(inode, inode->i_mode); | ||
261 | |||
262 | out: | ||
263 | gossip_debug(GOSSIP_INODE_DEBUG, "orangefs_setattr: returning %d\n", ret); | ||
264 | return ret; | ||
265 | } | ||
266 | |||
267 | /* | ||
268 | * Obtain attributes of an object given a dentry | ||
269 | */ | ||
270 | int orangefs_getattr(struct vfsmount *mnt, | ||
271 | struct dentry *dentry, | ||
272 | struct kstat *kstat) | ||
273 | { | ||
274 | int ret = -ENOENT; | ||
275 | struct inode *inode = dentry->d_inode; | ||
276 | struct orangefs_inode_s *orangefs_inode = NULL; | ||
277 | |||
278 | gossip_debug(GOSSIP_INODE_DEBUG, | ||
279 | "orangefs_getattr: called on %s\n", | ||
280 | dentry->d_name.name); | ||
281 | |||
282 | ret = orangefs_inode_getattr(inode, 0, 1); | ||
283 | if (ret == 0) { | ||
284 | generic_fillattr(inode, kstat); | ||
285 | |||
286 | /* override block size reported to stat */ | ||
287 | orangefs_inode = ORANGEFS_I(inode); | ||
288 | kstat->blksize = orangefs_inode->blksize; | ||
289 | } | ||
290 | return ret; | ||
291 | } | ||
292 | |||
293 | int orangefs_permission(struct inode *inode, int mask) | ||
294 | { | ||
295 | int ret; | ||
296 | |||
297 | if (mask & MAY_NOT_BLOCK) | ||
298 | return -ECHILD; | ||
299 | |||
300 | gossip_debug(GOSSIP_INODE_DEBUG, "%s: refreshing\n", __func__); | ||
301 | |||
302 | /* Make sure the permission (and other common attrs) are up to date. */ | ||
303 | ret = orangefs_inode_getattr(inode, 0, 0); | ||
304 | if (ret < 0) | ||
305 | return ret; | ||
306 | |||
307 | return generic_permission(inode, mask); | ||
308 | } | ||
309 | |||
310 | /* ORANGEDS2 implementation of VFS inode operations for files */ | ||
311 | struct inode_operations orangefs_file_inode_operations = { | ||
312 | .get_acl = orangefs_get_acl, | ||
313 | .set_acl = orangefs_set_acl, | ||
314 | .setattr = orangefs_setattr, | ||
315 | .getattr = orangefs_getattr, | ||
316 | .setxattr = generic_setxattr, | ||
317 | .getxattr = generic_getxattr, | ||
318 | .listxattr = orangefs_listxattr, | ||
319 | .removexattr = generic_removexattr, | ||
320 | .permission = orangefs_permission, | ||
321 | }; | ||
322 | |||
323 | static int orangefs_init_iops(struct inode *inode) | ||
324 | { | ||
325 | inode->i_mapping->a_ops = &orangefs_address_operations; | ||
326 | |||
327 | switch (inode->i_mode & S_IFMT) { | ||
328 | case S_IFREG: | ||
329 | inode->i_op = &orangefs_file_inode_operations; | ||
330 | inode->i_fop = &orangefs_file_operations; | ||
331 | inode->i_blkbits = PAGE_CACHE_SHIFT; | ||
332 | break; | ||
333 | case S_IFLNK: | ||
334 | inode->i_op = &orangefs_symlink_inode_operations; | ||
335 | break; | ||
336 | case S_IFDIR: | ||
337 | inode->i_op = &orangefs_dir_inode_operations; | ||
338 | inode->i_fop = &orangefs_dir_operations; | ||
339 | break; | ||
340 | default: | ||
341 | gossip_debug(GOSSIP_INODE_DEBUG, | ||
342 | "%s: unsupported mode\n", | ||
343 | __func__); | ||
344 | return -EINVAL; | ||
345 | } | ||
346 | |||
347 | return 0; | ||
348 | } | ||
349 | |||
350 | /* | ||
351 | * Given a ORANGEFS object identifier (fsid, handle), convert it into a ino_t type | ||
352 | * that will be used as a hash-index from where the handle will | ||
353 | * be searched for in the VFS hash table of inodes. | ||
354 | */ | ||
355 | static inline ino_t orangefs_handle_hash(struct orangefs_object_kref *ref) | ||
356 | { | ||
357 | if (!ref) | ||
358 | return 0; | ||
359 | return orangefs_khandle_to_ino(&(ref->khandle)); | ||
360 | } | ||
361 | |||
362 | /* | ||
363 | * Called to set up an inode from iget5_locked. | ||
364 | */ | ||
365 | static int orangefs_set_inode(struct inode *inode, void *data) | ||
366 | { | ||
367 | struct orangefs_object_kref *ref = (struct orangefs_object_kref *) data; | ||
368 | ORANGEFS_I(inode)->refn.fs_id = ref->fs_id; | ||
369 | ORANGEFS_I(inode)->refn.khandle = ref->khandle; | ||
370 | return 0; | ||
371 | } | ||
372 | |||
373 | /* | ||
374 | * Called to determine if handles match. | ||
375 | */ | ||
376 | static int orangefs_test_inode(struct inode *inode, void *data) | ||
377 | { | ||
378 | struct orangefs_object_kref *ref = (struct orangefs_object_kref *) data; | ||
379 | struct orangefs_inode_s *orangefs_inode = NULL; | ||
380 | |||
381 | orangefs_inode = ORANGEFS_I(inode); | ||
382 | return (!ORANGEFS_khandle_cmp(&(orangefs_inode->refn.khandle), &(ref->khandle)) | ||
383 | && orangefs_inode->refn.fs_id == ref->fs_id); | ||
384 | } | ||
385 | |||
386 | /* | ||
387 | * Front-end to lookup the inode-cache maintained by the VFS using the ORANGEFS | ||
388 | * file handle. | ||
389 | * | ||
390 | * @sb: the file system super block instance. | ||
391 | * @ref: The ORANGEFS object for which we are trying to locate an inode structure. | ||
392 | */ | ||
393 | struct inode *orangefs_iget(struct super_block *sb, struct orangefs_object_kref *ref) | ||
394 | { | ||
395 | struct inode *inode = NULL; | ||
396 | unsigned long hash; | ||
397 | int error; | ||
398 | |||
399 | hash = orangefs_handle_hash(ref); | ||
400 | inode = iget5_locked(sb, hash, orangefs_test_inode, orangefs_set_inode, ref); | ||
401 | if (!inode || !(inode->i_state & I_NEW)) | ||
402 | return inode; | ||
403 | |||
404 | error = orangefs_inode_getattr(inode, 1, 0); | ||
405 | if (error) { | ||
406 | iget_failed(inode); | ||
407 | return ERR_PTR(error); | ||
408 | } | ||
409 | |||
410 | inode->i_ino = hash; /* needed for stat etc */ | ||
411 | orangefs_init_iops(inode); | ||
412 | unlock_new_inode(inode); | ||
413 | |||
414 | gossip_debug(GOSSIP_INODE_DEBUG, | ||
415 | "iget handle %pU, fsid %d hash %ld i_ino %lu\n", | ||
416 | &ref->khandle, | ||
417 | ref->fs_id, | ||
418 | hash, | ||
419 | inode->i_ino); | ||
420 | |||
421 | return inode; | ||
422 | } | ||
423 | |||
424 | /* | ||
425 | * Allocate an inode for a newly created file and insert it into the inode hash. | ||
426 | */ | ||
427 | struct inode *orangefs_new_inode(struct super_block *sb, struct inode *dir, | ||
428 | int mode, dev_t dev, struct orangefs_object_kref *ref) | ||
429 | { | ||
430 | unsigned long hash = orangefs_handle_hash(ref); | ||
431 | struct inode *inode; | ||
432 | int error; | ||
433 | |||
434 | gossip_debug(GOSSIP_INODE_DEBUG, | ||
435 | "%s:(sb is %p | MAJOR(dev)=%u | MINOR(dev)=%u mode=%o)\n", | ||
436 | __func__, | ||
437 | sb, | ||
438 | MAJOR(dev), | ||
439 | MINOR(dev), | ||
440 | mode); | ||
441 | |||
442 | inode = new_inode(sb); | ||
443 | if (!inode) | ||
444 | return NULL; | ||
445 | |||
446 | orangefs_set_inode(inode, ref); | ||
447 | inode->i_ino = hash; /* needed for stat etc */ | ||
448 | |||
449 | error = orangefs_inode_getattr(inode, 1, 0); | ||
450 | if (error) | ||
451 | goto out_iput; | ||
452 | |||
453 | orangefs_init_iops(inode); | ||
454 | |||
455 | inode->i_mode = mode; | ||
456 | inode->i_uid = current_fsuid(); | ||
457 | inode->i_gid = current_fsgid(); | ||
458 | inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; | ||
459 | inode->i_size = PAGE_CACHE_SIZE; | ||
460 | inode->i_rdev = dev; | ||
461 | |||
462 | error = insert_inode_locked4(inode, hash, orangefs_test_inode, ref); | ||
463 | if (error < 0) | ||
464 | goto out_iput; | ||
465 | |||
466 | gossip_debug(GOSSIP_INODE_DEBUG, | ||
467 | "Initializing ACL's for inode %pU\n", | ||
468 | get_khandle_from_ino(inode)); | ||
469 | orangefs_init_acl(inode, dir); | ||
470 | return inode; | ||
471 | |||
472 | out_iput: | ||
473 | iput(inode); | ||
474 | return ERR_PTR(error); | ||
475 | } | ||