diff options
Diffstat (limited to 'mm')
-rw-r--r-- | mm/filemap.h | 2 | ||||
-rw-r--r-- | mm/filemap_xip.c | 246 |
2 files changed, 57 insertions, 191 deletions
diff --git a/mm/filemap.h b/mm/filemap.h index c2d0546a57eb..13793ba0ce17 100644 --- a/mm/filemap.h +++ b/mm/filemap.h | |||
@@ -15,7 +15,7 @@ | |||
15 | #include <linux/config.h> | 15 | #include <linux/config.h> |
16 | #include <asm/uaccess.h> | 16 | #include <asm/uaccess.h> |
17 | 17 | ||
18 | extern size_t | 18 | size_t |
19 | __filemap_copy_from_user_iovec(char *vaddr, | 19 | __filemap_copy_from_user_iovec(char *vaddr, |
20 | const struct iovec *iov, | 20 | const struct iovec *iov, |
21 | size_t base, | 21 | size_t base, |
diff --git a/mm/filemap_xip.c b/mm/filemap_xip.c index 7d63acd48817..3b6e384b98a6 100644 --- a/mm/filemap_xip.c +++ b/mm/filemap_xip.c | |||
@@ -114,83 +114,28 @@ out: | |||
114 | file_accessed(filp); | 114 | file_accessed(filp); |
115 | } | 115 | } |
116 | 116 | ||
117 | /* | ||
118 | * This is the "read()" routine for all filesystems | ||
119 | * that uses the get_xip_page address space operation. | ||
120 | */ | ||
121 | static ssize_t | ||
122 | __xip_file_aio_read(struct kiocb *iocb, const struct iovec *iov, | ||
123 | unsigned long nr_segs, loff_t *ppos) | ||
124 | { | ||
125 | struct file *filp = iocb->ki_filp; | ||
126 | ssize_t retval; | ||
127 | unsigned long seg; | ||
128 | size_t count; | ||
129 | |||
130 | count = 0; | ||
131 | for (seg = 0; seg < nr_segs; seg++) { | ||
132 | const struct iovec *iv = &iov[seg]; | ||
133 | |||
134 | /* | ||
135 | * If any segment has a negative length, or the cumulative | ||
136 | * length ever wraps negative then return -EINVAL. | ||
137 | */ | ||
138 | count += iv->iov_len; | ||
139 | if (unlikely((ssize_t)(count|iv->iov_len) < 0)) | ||
140 | return -EINVAL; | ||
141 | if (access_ok(VERIFY_WRITE, iv->iov_base, iv->iov_len)) | ||
142 | continue; | ||
143 | if (seg == 0) | ||
144 | return -EFAULT; | ||
145 | nr_segs = seg; | ||
146 | count -= iv->iov_len; /* This segment is no good */ | ||
147 | break; | ||
148 | } | ||
149 | |||
150 | retval = 0; | ||
151 | if (count) { | ||
152 | for (seg = 0; seg < nr_segs; seg++) { | ||
153 | read_descriptor_t desc; | ||
154 | |||
155 | desc.written = 0; | ||
156 | desc.arg.buf = iov[seg].iov_base; | ||
157 | desc.count = iov[seg].iov_len; | ||
158 | if (desc.count == 0) | ||
159 | continue; | ||
160 | desc.error = 0; | ||
161 | do_xip_mapping_read(filp->f_mapping, &filp->f_ra, filp, | ||
162 | ppos, &desc, file_read_actor); | ||
163 | retval += desc.written; | ||
164 | if (!retval) { | ||
165 | retval = desc.error; | ||
166 | break; | ||
167 | } | ||
168 | } | ||
169 | } | ||
170 | return retval; | ||
171 | } | ||
172 | |||
173 | ssize_t | 117 | ssize_t |
174 | xip_file_aio_read(struct kiocb *iocb, char __user *buf, size_t count, | 118 | xip_file_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos) |
175 | loff_t pos) | ||
176 | { | 119 | { |
177 | struct iovec local_iov = { .iov_base = buf, .iov_len = count }; | 120 | read_descriptor_t desc; |
178 | 121 | ||
179 | BUG_ON(iocb->ki_pos != pos); | 122 | if (!access_ok(VERIFY_WRITE, buf, len)) |
180 | return __xip_file_aio_read(iocb, &local_iov, 1, &iocb->ki_pos); | 123 | return -EFAULT; |
181 | } | ||
182 | EXPORT_SYMBOL_GPL(xip_file_aio_read); | ||
183 | 124 | ||
184 | ssize_t | 125 | desc.written = 0; |
185 | xip_file_readv(struct file *filp, const struct iovec *iov, | 126 | desc.arg.buf = buf; |
186 | unsigned long nr_segs, loff_t *ppos) | 127 | desc.count = len; |
187 | { | 128 | desc.error = 0; |
188 | struct kiocb kiocb; | ||
189 | 129 | ||
190 | init_sync_kiocb(&kiocb, filp); | 130 | do_xip_mapping_read(filp->f_mapping, &filp->f_ra, filp, |
191 | return __xip_file_aio_read(&kiocb, iov, nr_segs, ppos); | 131 | ppos, &desc, file_read_actor); |
132 | |||
133 | if (desc.written) | ||
134 | return desc.written; | ||
135 | else | ||
136 | return desc.error; | ||
192 | } | 137 | } |
193 | EXPORT_SYMBOL_GPL(xip_file_readv); | 138 | EXPORT_SYMBOL_GPL(xip_file_read); |
194 | 139 | ||
195 | ssize_t | 140 | ssize_t |
196 | xip_file_sendfile(struct file *in_file, loff_t *ppos, | 141 | xip_file_sendfile(struct file *in_file, loff_t *ppos, |
@@ -326,25 +271,19 @@ int xip_file_mmap(struct file * file, struct vm_area_struct * vma) | |||
326 | EXPORT_SYMBOL_GPL(xip_file_mmap); | 271 | EXPORT_SYMBOL_GPL(xip_file_mmap); |
327 | 272 | ||
328 | static ssize_t | 273 | static ssize_t |
329 | do_xip_file_write(struct kiocb *iocb, const struct iovec *iov, | 274 | __xip_file_write(struct file *filp, const char __user *buf, |
330 | unsigned long nr_segs, loff_t pos, loff_t *ppos, | 275 | size_t count, loff_t pos, loff_t *ppos) |
331 | size_t count) | ||
332 | { | 276 | { |
333 | struct file *file = iocb->ki_filp; | 277 | struct address_space * mapping = filp->f_mapping; |
334 | struct address_space * mapping = file->f_mapping; | ||
335 | struct address_space_operations *a_ops = mapping->a_ops; | 278 | struct address_space_operations *a_ops = mapping->a_ops; |
336 | struct inode *inode = mapping->host; | 279 | struct inode *inode = mapping->host; |
337 | long status = 0; | 280 | long status = 0; |
338 | struct page *page; | 281 | struct page *page; |
339 | size_t bytes; | 282 | size_t bytes; |
340 | const struct iovec *cur_iov = iov; /* current iovec */ | ||
341 | size_t iov_base = 0; /* offset in the current iovec */ | ||
342 | char __user *buf; | ||
343 | ssize_t written = 0; | 283 | ssize_t written = 0; |
344 | 284 | ||
345 | BUG_ON(!mapping->a_ops->get_xip_page); | 285 | BUG_ON(!mapping->a_ops->get_xip_page); |
346 | 286 | ||
347 | buf = iov->iov_base; | ||
348 | do { | 287 | do { |
349 | unsigned long index; | 288 | unsigned long index; |
350 | unsigned long offset; | 289 | unsigned long offset; |
@@ -365,15 +304,14 @@ do_xip_file_write(struct kiocb *iocb, const struct iovec *iov, | |||
365 | fault_in_pages_readable(buf, bytes); | 304 | fault_in_pages_readable(buf, bytes); |
366 | 305 | ||
367 | page = a_ops->get_xip_page(mapping, | 306 | page = a_ops->get_xip_page(mapping, |
368 | index*(PAGE_SIZE/512), 0); | 307 | index*(PAGE_SIZE/512), 0); |
369 | if (IS_ERR(page) && (PTR_ERR(page) == -ENODATA)) { | 308 | if (IS_ERR(page) && (PTR_ERR(page) == -ENODATA)) { |
370 | /* we allocate a new page unmap it */ | 309 | /* we allocate a new page unmap it */ |
371 | page = a_ops->get_xip_page(mapping, | 310 | page = a_ops->get_xip_page(mapping, |
372 | index*(PAGE_SIZE/512), 1); | 311 | index*(PAGE_SIZE/512), 1); |
373 | if (!IS_ERR(page)) | 312 | if (!IS_ERR(page)) |
374 | /* unmap page at pgoff from all other vmas */ | 313 | /* unmap page at pgoff from all other vmas */ |
375 | __xip_unmap(mapping, index); | 314 | __xip_unmap(mapping, index); |
376 | |||
377 | } | 315 | } |
378 | 316 | ||
379 | if (IS_ERR(page)) { | 317 | if (IS_ERR(page)) { |
@@ -383,12 +321,7 @@ do_xip_file_write(struct kiocb *iocb, const struct iovec *iov, | |||
383 | 321 | ||
384 | BUG_ON(!PageUptodate(page)); | 322 | BUG_ON(!PageUptodate(page)); |
385 | 323 | ||
386 | if (likely(nr_segs == 1)) | 324 | copied = filemap_copy_from_user(page, offset, buf, bytes); |
387 | copied = filemap_copy_from_user(page, offset, | ||
388 | buf, bytes); | ||
389 | else | ||
390 | copied = filemap_copy_from_user_iovec(page, offset, | ||
391 | cur_iov, iov_base, bytes); | ||
392 | flush_dcache_page(page); | 325 | flush_dcache_page(page); |
393 | if (likely(copied > 0)) { | 326 | if (likely(copied > 0)) { |
394 | status = copied; | 327 | status = copied; |
@@ -398,9 +331,6 @@ do_xip_file_write(struct kiocb *iocb, const struct iovec *iov, | |||
398 | count -= status; | 331 | count -= status; |
399 | pos += status; | 332 | pos += status; |
400 | buf += status; | 333 | buf += status; |
401 | if (unlikely(nr_segs > 1)) | ||
402 | filemap_set_next_iovec(&cur_iov, | ||
403 | &iov_base, status); | ||
404 | } | 334 | } |
405 | } | 335 | } |
406 | if (unlikely(copied != bytes)) | 336 | if (unlikely(copied != bytes)) |
@@ -422,110 +352,52 @@ do_xip_file_write(struct kiocb *iocb, const struct iovec *iov, | |||
422 | return written ? written : status; | 352 | return written ? written : status; |
423 | } | 353 | } |
424 | 354 | ||
425 | static ssize_t | 355 | ssize_t |
426 | xip_file_aio_write_nolock(struct kiocb *iocb, const struct iovec *iov, | 356 | xip_file_write(struct file *filp, const char __user *buf, size_t len, |
427 | unsigned long nr_segs, loff_t *ppos) | 357 | loff_t *ppos) |
428 | { | 358 | { |
429 | struct file *file = iocb->ki_filp; | 359 | struct address_space *mapping = filp->f_mapping; |
430 | struct address_space * mapping = file->f_mapping; | 360 | struct inode *inode = mapping->host; |
431 | size_t ocount; /* original count */ | 361 | size_t count; |
432 | size_t count; /* after file limit checks */ | 362 | loff_t pos; |
433 | struct inode *inode = mapping->host; | 363 | ssize_t ret; |
434 | unsigned long seg; | ||
435 | loff_t pos; | ||
436 | ssize_t written; | ||
437 | ssize_t err; | ||
438 | 364 | ||
439 | ocount = 0; | 365 | down(&inode->i_sem); |
440 | for (seg = 0; seg < nr_segs; seg++) { | ||
441 | const struct iovec *iv = &iov[seg]; | ||
442 | 366 | ||
443 | /* | 367 | if (!access_ok(VERIFY_READ, buf, len)) { |
444 | * If any segment has a negative length, or the cumulative | 368 | ret=-EFAULT; |
445 | * length ever wraps negative then return -EINVAL. | 369 | goto out_up; |
446 | */ | ||
447 | ocount += iv->iov_len; | ||
448 | if (unlikely((ssize_t)(ocount|iv->iov_len) < 0)) | ||
449 | return -EINVAL; | ||
450 | if (access_ok(VERIFY_READ, iv->iov_base, iv->iov_len)) | ||
451 | continue; | ||
452 | if (seg == 0) | ||
453 | return -EFAULT; | ||
454 | nr_segs = seg; | ||
455 | ocount -= iv->iov_len; /* This segment is no good */ | ||
456 | break; | ||
457 | } | 370 | } |
458 | 371 | ||
459 | count = ocount; | ||
460 | pos = *ppos; | 372 | pos = *ppos; |
373 | count = len; | ||
461 | 374 | ||
462 | vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE); | 375 | vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE); |
463 | 376 | ||
464 | written = 0; | 377 | /* We can write back this queue in page reclaim */ |
465 | 378 | current->backing_dev_info = mapping->backing_dev_info; | |
466 | err = generic_write_checks(file, &pos, &count, S_ISBLK(inode->i_mode)); | ||
467 | if (err) | ||
468 | goto out; | ||
469 | 379 | ||
380 | ret = generic_write_checks(filp, &pos, &count, S_ISBLK(inode->i_mode)); | ||
381 | if (ret) | ||
382 | goto out_backing; | ||
470 | if (count == 0) | 383 | if (count == 0) |
471 | goto out; | 384 | goto out_backing; |
472 | 385 | ||
473 | err = remove_suid(file->f_dentry); | 386 | ret = remove_suid(filp->f_dentry); |
474 | if (err) | 387 | if (ret) |
475 | goto out; | 388 | goto out_backing; |
476 | 389 | ||
477 | inode_update_time(inode, 1); | 390 | inode_update_time(inode, 1); |
478 | 391 | ||
479 | /* use execute in place to copy directly to disk */ | 392 | ret = __xip_file_write (filp, buf, count, pos, ppos); |
480 | written = do_xip_file_write (iocb, iov, | ||
481 | nr_segs, pos, ppos, count); | ||
482 | out: | ||
483 | return written ? written : err; | ||
484 | } | ||
485 | |||
486 | static ssize_t | ||
487 | __xip_file_write_nolock(struct file *file, const struct iovec *iov, | ||
488 | unsigned long nr_segs, loff_t *ppos) | ||
489 | { | ||
490 | struct kiocb kiocb; | ||
491 | |||
492 | init_sync_kiocb(&kiocb, file); | ||
493 | return xip_file_aio_write_nolock(&kiocb, iov, nr_segs, ppos); | ||
494 | } | ||
495 | |||
496 | ssize_t | ||
497 | xip_file_aio_write(struct kiocb *iocb, const char __user *buf, | ||
498 | size_t count, loff_t pos) | ||
499 | { | ||
500 | struct file *file = iocb->ki_filp; | ||
501 | struct address_space *mapping = file->f_mapping; | ||
502 | struct inode *inode = mapping->host; | ||
503 | ssize_t ret; | ||
504 | struct iovec local_iov = { .iov_base = (void __user *)buf, | ||
505 | .iov_len = count }; | ||
506 | 393 | ||
507 | BUG_ON(iocb->ki_pos != pos); | 394 | out_backing: |
508 | 395 | current->backing_dev_info = NULL; | |
509 | down(&inode->i_sem); | 396 | out_up: |
510 | ret = xip_file_aio_write_nolock(iocb, &local_iov, 1, &iocb->ki_pos); | ||
511 | up(&inode->i_sem); | 397 | up(&inode->i_sem); |
512 | return ret; | 398 | return ret; |
513 | } | 399 | } |
514 | EXPORT_SYMBOL_GPL(xip_file_aio_write); | 400 | EXPORT_SYMBOL_GPL(xip_file_write); |
515 | |||
516 | ssize_t xip_file_writev(struct file *file, const struct iovec *iov, | ||
517 | unsigned long nr_segs, loff_t *ppos) | ||
518 | { | ||
519 | struct address_space *mapping = file->f_mapping; | ||
520 | struct inode *inode = mapping->host; | ||
521 | ssize_t ret; | ||
522 | |||
523 | down(&inode->i_sem); | ||
524 | ret = __xip_file_write_nolock(file, iov, nr_segs, ppos); | ||
525 | up(&inode->i_sem); | ||
526 | return ret; | ||
527 | } | ||
528 | EXPORT_SYMBOL_GPL(xip_file_writev); | ||
529 | 401 | ||
530 | /* | 402 | /* |
531 | * truncate a page used for execute in place | 403 | * truncate a page used for execute in place |
@@ -541,7 +413,6 @@ xip_truncate_page(struct address_space *mapping, loff_t from) | |||
541 | unsigned length; | 413 | unsigned length; |
542 | struct page *page; | 414 | struct page *page; |
543 | void *kaddr; | 415 | void *kaddr; |
544 | int err; | ||
545 | 416 | ||
546 | BUG_ON(!mapping->a_ops->get_xip_page); | 417 | BUG_ON(!mapping->a_ops->get_xip_page); |
547 | 418 | ||
@@ -556,17 +427,14 @@ xip_truncate_page(struct address_space *mapping, loff_t from) | |||
556 | 427 | ||
557 | page = mapping->a_ops->get_xip_page(mapping, | 428 | page = mapping->a_ops->get_xip_page(mapping, |
558 | index*(PAGE_SIZE/512), 0); | 429 | index*(PAGE_SIZE/512), 0); |
559 | err = -ENOMEM; | ||
560 | if (!page) | 430 | if (!page) |
561 | goto out; | 431 | return -ENOMEM; |
562 | if (unlikely(IS_ERR(page))) { | 432 | if (unlikely(IS_ERR(page))) { |
563 | if (PTR_ERR(page) == -ENODATA) { | 433 | if (PTR_ERR(page) == -ENODATA) |
564 | /* Hole? No need to truncate */ | 434 | /* Hole? No need to truncate */ |
565 | return 0; | 435 | return 0; |
566 | } else { | 436 | else |
567 | err = PTR_ERR(page); | 437 | return PTR_ERR(page); |
568 | goto out; | ||
569 | } | ||
570 | } else | 438 | } else |
571 | BUG_ON(!PageUptodate(page)); | 439 | BUG_ON(!PageUptodate(page)); |
572 | kaddr = kmap_atomic(page, KM_USER0); | 440 | kaddr = kmap_atomic(page, KM_USER0); |
@@ -574,8 +442,6 @@ xip_truncate_page(struct address_space *mapping, loff_t from) | |||
574 | kunmap_atomic(kaddr, KM_USER0); | 442 | kunmap_atomic(kaddr, KM_USER0); |
575 | 443 | ||
576 | flush_dcache_page(page); | 444 | flush_dcache_page(page); |
577 | err = 0; | 445 | return 0; |
578 | out: | ||
579 | return err; | ||
580 | } | 446 | } |
581 | EXPORT_SYMBOL_GPL(xip_truncate_page); | 447 | EXPORT_SYMBOL_GPL(xip_truncate_page); |