diff options
Diffstat (limited to 'fs/ocfs2/ioctl.c')
-rw-r--r-- | fs/ocfs2/ioctl.c | 290 |
1 files changed, 290 insertions, 0 deletions
diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c index 4216739e163c..fd248ed53df7 100644 --- a/fs/ocfs2/ioctl.c +++ b/fs/ocfs2/ioctl.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include "sysfile.h" | 25 | #include "sysfile.h" |
26 | #include "dir.h" | 26 | #include "dir.h" |
27 | #include "buffer_head_io.h" | 27 | #include "buffer_head_io.h" |
28 | #include "suballoc.h" | ||
28 | 29 | ||
29 | #include <linux/ext2_fs.h> | 30 | #include <linux/ext2_fs.h> |
30 | 31 | ||
@@ -433,6 +434,291 @@ bail: | |||
433 | return status; | 434 | return status; |
434 | } | 435 | } |
435 | 436 | ||
437 | static void o2ffg_update_histogram(struct ocfs2_info_free_chunk_list *hist, | ||
438 | unsigned int chunksize) | ||
439 | { | ||
440 | int index; | ||
441 | |||
442 | index = __ilog2_u32(chunksize); | ||
443 | if (index >= OCFS2_INFO_MAX_HIST) | ||
444 | index = OCFS2_INFO_MAX_HIST - 1; | ||
445 | |||
446 | hist->fc_chunks[index]++; | ||
447 | hist->fc_clusters[index] += chunksize; | ||
448 | } | ||
449 | |||
450 | static void o2ffg_update_stats(struct ocfs2_info_freefrag_stats *stats, | ||
451 | unsigned int chunksize) | ||
452 | { | ||
453 | if (chunksize > stats->ffs_max) | ||
454 | stats->ffs_max = chunksize; | ||
455 | |||
456 | if (chunksize < stats->ffs_min) | ||
457 | stats->ffs_min = chunksize; | ||
458 | |||
459 | stats->ffs_avg += chunksize; | ||
460 | stats->ffs_free_chunks_real++; | ||
461 | } | ||
462 | |||
463 | void ocfs2_info_update_ffg(struct ocfs2_info_freefrag *ffg, | ||
464 | unsigned int chunksize) | ||
465 | { | ||
466 | o2ffg_update_histogram(&(ffg->iff_ffs.ffs_fc_hist), chunksize); | ||
467 | o2ffg_update_stats(&(ffg->iff_ffs), chunksize); | ||
468 | } | ||
469 | |||
470 | int ocfs2_info_freefrag_scan_chain(struct ocfs2_super *osb, | ||
471 | struct inode *gb_inode, | ||
472 | struct ocfs2_dinode *gb_dinode, | ||
473 | struct ocfs2_chain_rec *rec, | ||
474 | struct ocfs2_info_freefrag *ffg, | ||
475 | u32 chunks_in_group) | ||
476 | { | ||
477 | int status = 0, used; | ||
478 | u64 blkno; | ||
479 | |||
480 | struct buffer_head *bh = NULL; | ||
481 | struct ocfs2_group_desc *bg = NULL; | ||
482 | |||
483 | unsigned int max_bits, num_clusters; | ||
484 | unsigned int offset = 0, cluster, chunk; | ||
485 | unsigned int chunk_free, last_chunksize = 0; | ||
486 | |||
487 | if (!le32_to_cpu(rec->c_free)) | ||
488 | goto bail; | ||
489 | |||
490 | do { | ||
491 | if (!bg) | ||
492 | blkno = le64_to_cpu(rec->c_blkno); | ||
493 | else | ||
494 | blkno = le64_to_cpu(bg->bg_next_group); | ||
495 | |||
496 | if (bh) { | ||
497 | brelse(bh); | ||
498 | bh = NULL; | ||
499 | } | ||
500 | |||
501 | if (o2info_coherent(&ffg->iff_req)) | ||
502 | status = ocfs2_read_group_descriptor(gb_inode, | ||
503 | gb_dinode, | ||
504 | blkno, &bh); | ||
505 | else | ||
506 | status = ocfs2_read_blocks_sync(osb, blkno, 1, &bh); | ||
507 | |||
508 | if (status < 0) { | ||
509 | mlog(ML_ERROR, "Can't read the group descriptor # " | ||
510 | "%llu from device.", (unsigned long long)blkno); | ||
511 | status = -EIO; | ||
512 | goto bail; | ||
513 | } | ||
514 | |||
515 | bg = (struct ocfs2_group_desc *)bh->b_data; | ||
516 | |||
517 | if (!le16_to_cpu(bg->bg_free_bits_count)) | ||
518 | continue; | ||
519 | |||
520 | max_bits = le16_to_cpu(bg->bg_bits); | ||
521 | offset = 0; | ||
522 | |||
523 | for (chunk = 0; chunk < chunks_in_group; chunk++) { | ||
524 | /* | ||
525 | * last chunk may be not an entire one. | ||
526 | */ | ||
527 | if ((offset + ffg->iff_chunksize) > max_bits) | ||
528 | num_clusters = max_bits - offset; | ||
529 | else | ||
530 | num_clusters = ffg->iff_chunksize; | ||
531 | |||
532 | chunk_free = 0; | ||
533 | for (cluster = 0; cluster < num_clusters; cluster++) { | ||
534 | used = ocfs2_test_bit(offset, | ||
535 | (unsigned long *)bg->bg_bitmap); | ||
536 | /* | ||
537 | * - chunk_free counts free clusters in #N chunk. | ||
538 | * - last_chunksize records the size(in) clusters | ||
539 | * for the last real free chunk being counted. | ||
540 | */ | ||
541 | if (!used) { | ||
542 | last_chunksize++; | ||
543 | chunk_free++; | ||
544 | } | ||
545 | |||
546 | if (used && last_chunksize) { | ||
547 | ocfs2_info_update_ffg(ffg, | ||
548 | last_chunksize); | ||
549 | last_chunksize = 0; | ||
550 | } | ||
551 | |||
552 | offset++; | ||
553 | } | ||
554 | |||
555 | if (chunk_free == ffg->iff_chunksize) | ||
556 | ffg->iff_ffs.ffs_free_chunks++; | ||
557 | } | ||
558 | |||
559 | /* | ||
560 | * need to update the info for last free chunk. | ||
561 | */ | ||
562 | if (last_chunksize) | ||
563 | ocfs2_info_update_ffg(ffg, last_chunksize); | ||
564 | |||
565 | } while (le64_to_cpu(bg->bg_next_group)); | ||
566 | |||
567 | bail: | ||
568 | brelse(bh); | ||
569 | |||
570 | return status; | ||
571 | } | ||
572 | |||
573 | int ocfs2_info_freefrag_scan_bitmap(struct ocfs2_super *osb, | ||
574 | struct inode *gb_inode, u64 blkno, | ||
575 | struct ocfs2_info_freefrag *ffg) | ||
576 | { | ||
577 | u32 chunks_in_group; | ||
578 | int status = 0, unlock = 0, i; | ||
579 | |||
580 | struct buffer_head *bh = NULL; | ||
581 | struct ocfs2_chain_list *cl = NULL; | ||
582 | struct ocfs2_chain_rec *rec = NULL; | ||
583 | struct ocfs2_dinode *gb_dinode = NULL; | ||
584 | |||
585 | if (gb_inode) | ||
586 | mutex_lock(&gb_inode->i_mutex); | ||
587 | |||
588 | if (o2info_coherent(&ffg->iff_req)) { | ||
589 | status = ocfs2_inode_lock(gb_inode, &bh, 0); | ||
590 | if (status < 0) { | ||
591 | mlog_errno(status); | ||
592 | goto bail; | ||
593 | } | ||
594 | unlock = 1; | ||
595 | } else { | ||
596 | status = ocfs2_read_blocks_sync(osb, blkno, 1, &bh); | ||
597 | if (status < 0) { | ||
598 | mlog_errno(status); | ||
599 | goto bail; | ||
600 | } | ||
601 | } | ||
602 | |||
603 | gb_dinode = (struct ocfs2_dinode *)bh->b_data; | ||
604 | cl = &(gb_dinode->id2.i_chain); | ||
605 | |||
606 | /* | ||
607 | * Chunksize(in) clusters from userspace should be | ||
608 | * less than clusters in a group. | ||
609 | */ | ||
610 | if (ffg->iff_chunksize > le16_to_cpu(cl->cl_cpg)) { | ||
611 | status = -EINVAL; | ||
612 | goto bail; | ||
613 | } | ||
614 | |||
615 | memset(&ffg->iff_ffs, 0, sizeof(struct ocfs2_info_freefrag_stats)); | ||
616 | |||
617 | ffg->iff_ffs.ffs_min = ~0U; | ||
618 | ffg->iff_ffs.ffs_clusters = | ||
619 | le32_to_cpu(gb_dinode->id1.bitmap1.i_total); | ||
620 | ffg->iff_ffs.ffs_free_clusters = ffg->iff_ffs.ffs_clusters - | ||
621 | le32_to_cpu(gb_dinode->id1.bitmap1.i_used); | ||
622 | |||
623 | chunks_in_group = le16_to_cpu(cl->cl_cpg) / ffg->iff_chunksize + 1; | ||
624 | |||
625 | for (i = 0; i < le16_to_cpu(cl->cl_next_free_rec); i++) { | ||
626 | rec = &(cl->cl_recs[i]); | ||
627 | status = ocfs2_info_freefrag_scan_chain(osb, gb_inode, | ||
628 | gb_dinode, | ||
629 | rec, ffg, | ||
630 | chunks_in_group); | ||
631 | if (status) | ||
632 | goto bail; | ||
633 | } | ||
634 | |||
635 | if (ffg->iff_ffs.ffs_free_chunks_real) | ||
636 | ffg->iff_ffs.ffs_avg = (ffg->iff_ffs.ffs_avg / | ||
637 | ffg->iff_ffs.ffs_free_chunks_real); | ||
638 | bail: | ||
639 | if (unlock) | ||
640 | ocfs2_inode_unlock(gb_inode, 0); | ||
641 | |||
642 | if (gb_inode) | ||
643 | mutex_unlock(&gb_inode->i_mutex); | ||
644 | |||
645 | if (gb_inode) | ||
646 | iput(gb_inode); | ||
647 | |||
648 | brelse(bh); | ||
649 | |||
650 | return status; | ||
651 | } | ||
652 | |||
653 | int ocfs2_info_handle_freefrag(struct inode *inode, | ||
654 | struct ocfs2_info_request __user *req) | ||
655 | { | ||
656 | u64 blkno = -1; | ||
657 | char namebuf[40]; | ||
658 | int status = -EFAULT, type = GLOBAL_BITMAP_SYSTEM_INODE; | ||
659 | |||
660 | struct ocfs2_info_freefrag *oiff; | ||
661 | struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); | ||
662 | struct inode *gb_inode = NULL; | ||
663 | |||
664 | oiff = kzalloc(sizeof(struct ocfs2_info_freefrag), GFP_KERNEL); | ||
665 | if (!oiff) { | ||
666 | status = -ENOMEM; | ||
667 | mlog_errno(status); | ||
668 | goto bail; | ||
669 | } | ||
670 | |||
671 | if (o2info_from_user(*oiff, req)) | ||
672 | goto bail; | ||
673 | /* | ||
674 | * chunksize from userspace should be power of 2. | ||
675 | */ | ||
676 | if ((oiff->iff_chunksize & (oiff->iff_chunksize - 1)) || | ||
677 | (!oiff->iff_chunksize)) { | ||
678 | status = -EINVAL; | ||
679 | goto bail; | ||
680 | } | ||
681 | |||
682 | if (o2info_coherent(&oiff->iff_req)) { | ||
683 | gb_inode = ocfs2_get_system_file_inode(osb, type, | ||
684 | OCFS2_INVALID_SLOT); | ||
685 | if (!gb_inode) { | ||
686 | mlog(ML_ERROR, "unable to get global_bitmap inode\n"); | ||
687 | status = -EIO; | ||
688 | goto bail; | ||
689 | } | ||
690 | } else { | ||
691 | ocfs2_sprintf_system_inode_name(namebuf, sizeof(namebuf), type, | ||
692 | OCFS2_INVALID_SLOT); | ||
693 | status = ocfs2_lookup_ino_from_name(osb->sys_root_inode, | ||
694 | namebuf, | ||
695 | strlen(namebuf), | ||
696 | &blkno); | ||
697 | if (status < 0) { | ||
698 | status = -ENOENT; | ||
699 | goto bail; | ||
700 | } | ||
701 | } | ||
702 | |||
703 | status = ocfs2_info_freefrag_scan_bitmap(osb, gb_inode, blkno, oiff); | ||
704 | if (status < 0) | ||
705 | goto bail; | ||
706 | |||
707 | o2info_set_request_filled(&oiff->iff_req); | ||
708 | |||
709 | if (o2info_to_user(*oiff, req)) | ||
710 | goto bail; | ||
711 | |||
712 | status = 0; | ||
713 | bail: | ||
714 | if (status) | ||
715 | o2info_set_request_error(&oiff->iff_req, req); | ||
716 | |||
717 | kfree(oiff); | ||
718 | |||
719 | return status; | ||
720 | } | ||
721 | |||
436 | int ocfs2_info_handle_unknown(struct inode *inode, | 722 | int ocfs2_info_handle_unknown(struct inode *inode, |
437 | struct ocfs2_info_request __user *req) | 723 | struct ocfs2_info_request __user *req) |
438 | { | 724 | { |
@@ -508,6 +794,10 @@ int ocfs2_info_handle_request(struct inode *inode, | |||
508 | if (oir.ir_size == sizeof(struct ocfs2_info_freeinode)) | 794 | if (oir.ir_size == sizeof(struct ocfs2_info_freeinode)) |
509 | status = ocfs2_info_handle_freeinode(inode, req); | 795 | status = ocfs2_info_handle_freeinode(inode, req); |
510 | break; | 796 | break; |
797 | case OCFS2_INFO_FREEFRAG: | ||
798 | if (oir.ir_size == sizeof(struct ocfs2_info_freefrag)) | ||
799 | status = ocfs2_info_handle_freefrag(inode, req); | ||
800 | break; | ||
511 | default: | 801 | default: |
512 | status = ocfs2_info_handle_unknown(inode, req); | 802 | status = ocfs2_info_handle_unknown(inode, req); |
513 | break; | 803 | break; |