static int f2fs_statfs(struct dentry *dentry, struct kstatfs *buf) { struct super_block *sb = dentry->d_sb; struct f2fs_sb_info *sbi = F2FS_SB(sb); u64 id = huge_encode_dev(sb->s_bdev->bd_dev); block_t total_count, user_block_count, start_count, ovp_count; total_count = le64_to_cpu(sbi->raw_super->block_count); user_block_count = sbi->user_block_count; start_count = le32_to_cpu(sbi->raw_super->segment0_blkaddr); ovp_count = SM_I(sbi)->ovp_segments << sbi->log_blocks_per_seg; buf->f_type = F2FS_SUPER_MAGIC; buf->f_bsize = sbi->blocksize; buf->f_blocks = total_count - start_count; buf->f_bfree = buf->f_blocks - valid_user_blocks(sbi) - ovp_count; buf->f_bavail = user_block_count - valid_user_blocks(sbi); buf->f_files = sbi->total_node_count - F2FS_RESERVED_NODE_NUM; buf->f_ffree = buf->f_files - valid_inode_count(sbi); buf->f_namelen = F2FS_NAME_LEN; buf->f_fsid.val[0] = (u32)id; buf->f_fsid.val[1] = (u32)(id >> 32); return 0; }
static unsigned char *__struct_ptr(struct f2fs_sb_info *sbi, int struct_type) { if (struct_type == GC_THREAD) return (unsigned char *)sbi->gc_thread; else if (struct_type == SM_INFO) return (unsigned char *)SM_I(sbi); return NULL; }
static unsigned char *__struct_ptr(struct f2fs_sb_info *sbi, int struct_type) { if (struct_type == GC_THREAD) return (unsigned char *)sbi->gc_thread; else if (struct_type == SM_INFO) return (unsigned char *)SM_I(sbi); else if (struct_type == DCC_INFO) return (unsigned char *)SM_I(sbi)->dcc_info; else if (struct_type == NM_INFO) return (unsigned char *)NM_I(sbi); else if (struct_type == F2FS_SBI || struct_type == RESERVED_BLOCKS) return (unsigned char *)sbi; #ifdef CONFIG_F2FS_FAULT_INJECTION else if (struct_type == FAULT_INFO_RATE || struct_type == FAULT_INFO_TYPE) return (unsigned char *)&sbi->fault_info; #endif return NULL; }
void sit_dump(struct f2fs_sb_info *sbi, int start_sit, int end_sit) { struct seg_entry *se; int segno; char buf[BUF_SZ]; u32 free_segs = 0;; u64 valid_blocks = 0; int ret; int fd; fd = open("dump_sit", O_CREAT|O_WRONLY|O_TRUNC, 0666); ASSERT(fd >= 0); for (segno = start_sit; segno < end_sit; segno++) { se = get_seg_entry(sbi, segno); memset(buf, 0, BUF_SZ); snprintf(buf, BUF_SZ, "%5d %8d\n", segno, se->valid_blocks); ret = write(fd, buf, strlen(buf)); ASSERT(ret >= 0); DBG(4, "SIT[0x%3x] : 0x%x\n", segno, se->valid_blocks); if (se->valid_blocks == 0x0) { free_segs++; } else { ASSERT(se->valid_blocks <= 512); valid_blocks += se->valid_blocks; } } memset(buf, 0, BUF_SZ); snprintf(buf, BUF_SZ, "valid_segs:%d\t free_segs:%d\n", SM_I(sbi)->main_segments - free_segs, free_segs); ret = write(fd, buf, strlen(buf)); ASSERT(ret >= 0); close(fd); DBG(1, "Blocks [0x%lx] Free Segs [0x%x]\n", valid_blocks, free_segs); }
int fsck_init(struct f2fs_sb_info *sbi) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct f2fs_sm_info *sm_i = SM_I(sbi); /* * We build three bitmap for main/sit/nat so that may check consistency of filesystem. * 1. main_area_bitmap will be used to check whether all blocks of main area is used or not. * 2. nat_area_bitmap has bitmap information of used nid in NAT. * 3. sit_area_bitmap has bitmap information of used main block. * At Last sequence, we compare main_area_bitmap with sit_area_bitmap. */ fsck->nr_main_blks = sm_i->main_segments << sbi->log_blocks_per_seg; fsck->main_area_bitmap_sz = (fsck->nr_main_blks + 7) / 8; fsck->main_area_bitmap = calloc(fsck->main_area_bitmap_sz, 1); ASSERT(fsck->main_area_bitmap != NULL); build_nat_area_bitmap(sbi); build_sit_area_bitmap(sbi); tree_mark = calloc(tree_mark_size, 1); return 0; }
static int f2fs_remount(struct super_block *sb, int *flags, char *data) { struct f2fs_sb_info *sbi = F2FS_SB(sb); struct f2fs_mount_info org_mount_opt; int err, active_logs; bool need_restart_gc = false; bool need_stop_gc = false; sync_filesystem(sb); /* * Save the old mount options in case we * need to restore them. */ org_mount_opt = sbi->mount_opt; active_logs = sbi->active_logs; sbi->mount_opt.opt = 0; default_options(sbi); /* parse mount options */ err = parse_options(sb, data); if (err) goto restore_opts; /* * Previous and new state of filesystem is RO, * so skip checking GC and FLUSH_MERGE conditions. */ if (f2fs_readonly(sb) && (*flags & MS_RDONLY)) goto skip; /* * We stop the GC thread if FS is mounted as RO * or if background_gc = off is passed in mount * option. Also sync the filesystem. */ if ((*flags & MS_RDONLY) || !test_opt(sbi, BG_GC)) { if (sbi->gc_thread) { stop_gc_thread(sbi); f2fs_sync_fs(sb, 1); need_restart_gc = true; } } else if (!sbi->gc_thread) { err = start_gc_thread(sbi); if (err) goto restore_opts; need_stop_gc = true; } /* * We stop issue flush thread if FS is mounted as RO * or if flush_merge is not passed in mount option. */ if ((*flags & MS_RDONLY) || !test_opt(sbi, FLUSH_MERGE)) { destroy_flush_cmd_control(sbi); } else if (!SM_I(sbi)->cmd_control_info) { err = create_flush_cmd_control(sbi); if (err) goto restore_gc; } skip: /* Update the POSIXACL Flag */ sb->s_flags = (sb->s_flags & ~MS_POSIXACL) | (test_opt(sbi, POSIX_ACL) ? MS_POSIXACL : 0); return 0; restore_gc: if (need_restart_gc) { if (start_gc_thread(sbi)) f2fs_msg(sbi->sb, KERN_WARNING, "background gc thread has stopped"); } else if (need_stop_gc) { stop_gc_thread(sbi); } restore_opts: sbi->mount_opt = org_mount_opt; sbi->active_logs = active_logs; return err; }
int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) { struct inode *inode = file->f_mapping->host; struct f2fs_inode_info *fi = F2FS_I(inode); struct f2fs_sb_info *sbi = F2FS_I_SB(inode); nid_t ino = inode->i_ino; int ret = 0; bool need_cp = false; struct writeback_control wbc = { .sync_mode = WB_SYNC_ALL, .nr_to_write = LONG_MAX, .for_reclaim = 0, }; if (unlikely(f2fs_readonly(inode->i_sb))) return 0; trace_f2fs_sync_file_enter(inode); /* if fdatasync is triggered, let's do in-place-update */ if (get_dirty_pages(inode) <= SM_I(sbi)->min_fsync_blocks) set_inode_flag(fi, FI_NEED_IPU); ret = filemap_write_and_wait_range(inode->i_mapping, start, end); clear_inode_flag(fi, FI_NEED_IPU); if (ret) { trace_f2fs_sync_file_exit(inode, need_cp, datasync, ret); return ret; } /* if the inode is dirty, let's recover all the time */ if (!datasync && is_inode_flag_set(fi, FI_DIRTY_INODE)) { update_inode_page(inode); goto go_write; } /* * if there is no written data, don't waste time to write recovery info. */ if (!is_inode_flag_set(fi, FI_APPEND_WRITE) && !exist_written_data(sbi, ino, APPEND_INO)) { /* it may call write_inode just prior to fsync */ if (need_inode_page_update(sbi, ino)) goto go_write; if (is_inode_flag_set(fi, FI_UPDATE_WRITE) || exist_written_data(sbi, ino, UPDATE_INO)) goto flush_out; goto out; } go_write: /* guarantee free sections for fsync */ f2fs_balance_fs(sbi); /* * Both of fdatasync() and fsync() are able to be recovered from * sudden-power-off. */ down_read(&fi->i_sem); need_cp = need_do_checkpoint(inode); up_read(&fi->i_sem); if (need_cp) { /* all the dirty node pages should be flushed for POR */ ret = f2fs_sync_fs(inode->i_sb, 1); /* * We've secured consistency through sync_fs. Following pino * will be used only for fsynced inodes after checkpoint. */ try_to_fix_pino(inode); clear_inode_flag(fi, FI_APPEND_WRITE); clear_inode_flag(fi, FI_UPDATE_WRITE); goto out; } sync_nodes: sync_node_pages(sbi, ino, &wbc); /* if cp_error was enabled, we should avoid infinite loop */ if (unlikely(f2fs_cp_error(sbi))) goto out; if (need_inode_block_update(sbi, ino)) { mark_inode_dirty_sync(inode); f2fs_write_inode(inode, NULL); goto sync_nodes; } ret = wait_on_node_pages_writeback(sbi, ino); if (ret) goto out; /* once recovery info is written, don't need to tack this */ remove_dirty_inode(sbi, ino, APPEND_INO); clear_inode_flag(fi, FI_APPEND_WRITE); flush_out: remove_dirty_inode(sbi, ino, UPDATE_INO); clear_inode_flag(fi, FI_UPDATE_WRITE); ret = f2fs_issue_flush(sbi); out: trace_f2fs_sync_file_exit(inode, need_cp, datasync, ret); f2fs_trace_ios(NULL, 1); return ret; } static pgoff_t __get_first_dirty_index(struct address_space *mapping, pgoff_t pgofs, int whence) { struct pagevec pvec; int nr_pages; if (whence != SEEK_DATA) return 0; /* find first dirty page index */ pagevec_init(&pvec, 0); nr_pages = pagevec_lookup_tag(&pvec, mapping, &pgofs, PAGECACHE_TAG_DIRTY, 1); pgofs = nr_pages ? pvec.pages[0]->index : LONG_MAX; pagevec_release(&pvec); return pgofs; } static bool __found_offset(block_t blkaddr, pgoff_t dirty, pgoff_t pgofs, int whence) { switch (whence) { case SEEK_DATA: if ((blkaddr == NEW_ADDR && dirty == pgofs) || (blkaddr != NEW_ADDR && blkaddr != NULL_ADDR)) return true; break; case SEEK_HOLE: if (blkaddr == NULL_ADDR) return true; break; } return false; } static inline int unsigned_offsets(struct file *file) { return file->f_mode & FMODE_UNSIGNED_OFFSET; } static loff_t vfs_setpos(struct file *file, loff_t offset, loff_t maxsize) { if (offset < 0 && !unsigned_offsets(file)) return -EINVAL; if (offset > maxsize) return -EINVAL; if (offset != file->f_pos) { file->f_pos = offset; file->f_version = 0; } return offset; } static loff_t f2fs_seek_block(struct file *file, loff_t offset, int whence) { struct inode *inode = file->f_mapping->host; loff_t maxbytes = inode->i_sb->s_maxbytes; struct dnode_of_data dn; pgoff_t pgofs, end_offset, dirty; loff_t data_ofs = offset; loff_t isize; int err = 0; mutex_lock(&inode->i_mutex); isize = i_size_read(inode); if (offset >= isize) goto fail; /* handle inline data case */ if (f2fs_has_inline_data(inode) || f2fs_has_inline_dentry(inode)) { if (whence == SEEK_HOLE) data_ofs = isize; goto found; } pgofs = (pgoff_t)(offset >> PAGE_CACHE_SHIFT); dirty = __get_first_dirty_index(inode->i_mapping, pgofs, whence); for (; data_ofs < isize; data_ofs = pgofs << PAGE_CACHE_SHIFT) { set_new_dnode(&dn, inode, NULL, NULL, 0); err = get_dnode_of_data(&dn, pgofs, LOOKUP_NODE_RA); if (err && err != -ENOENT) { goto fail; } else if (err == -ENOENT) { /* direct node does not exists */ if (whence == SEEK_DATA) { pgofs = PGOFS_OF_NEXT_DNODE(pgofs, F2FS_I(inode)); continue; } else { goto found; } } end_offset = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode)); /* find data/hole in dnode block */ for (; dn.ofs_in_node < end_offset; dn.ofs_in_node++, pgofs++, data_ofs = (loff_t)pgofs << PAGE_CACHE_SHIFT) { block_t blkaddr; blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node); if (__found_offset(blkaddr, dirty, pgofs, whence)) { f2fs_put_dnode(&dn); goto found; } } f2fs_put_dnode(&dn); } if (whence == SEEK_DATA) goto fail; found: if (whence == SEEK_HOLE && data_ofs > isize) data_ofs = isize; mutex_unlock(&inode->i_mutex); return vfs_setpos(file, data_ofs, maxbytes); fail: mutex_unlock(&inode->i_mutex); return -ENXIO; } static loff_t f2fs_llseek(struct file *file, loff_t offset, int whence) { struct inode *inode = file->f_mapping->host; loff_t maxbytes = inode->i_sb->s_maxbytes; switch (whence) { case SEEK_SET: case SEEK_CUR: case SEEK_END: return generic_file_llseek_size(file, offset, whence, maxbytes); case SEEK_DATA: case SEEK_HOLE: if (offset < 0) return -ENXIO; return f2fs_seek_block(file, offset, whence); } return -EINVAL; } static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma) { struct inode *inode = file_inode(file); if (f2fs_encrypted_inode(inode)) { int err = f2fs_get_encryption_info(inode); if (err) return 0; } /* we don't need to use inline_data strictly */ if (f2fs_has_inline_data(inode)) { int err = f2fs_convert_inline_inode(inode); if (err) return err; } file_accessed(file); vma->vm_ops = &f2fs_file_vm_ops; return 0; } static int f2fs_file_open(struct inode *inode, struct file *filp) { int ret = generic_file_open(inode, filp); if (!ret && f2fs_encrypted_inode(inode)) { ret = f2fs_get_encryption_info(inode); if (ret) ret = -EACCES; } return ret; }
static ssize_t f2fs_sbi_store(struct f2fs_attr *a, struct f2fs_sb_info *sbi, const char *buf, size_t count) { unsigned char *ptr; unsigned long t; unsigned int *ui; ssize_t ret; ptr = __struct_ptr(sbi, a->struct_type); if (!ptr) return -EINVAL; ui = (unsigned int *)(ptr + a->offset); ret = kstrtoul(skip_spaces(buf), 0, &t); if (ret < 0) return ret; #ifdef CONFIG_F2FS_FAULT_INJECTION if (a->struct_type == FAULT_INFO_TYPE && t >= (1 << FAULT_MAX)) return -EINVAL; #endif if (a->struct_type == RESERVED_BLOCKS) { spin_lock(&sbi->stat_lock); if ((unsigned long)sbi->total_valid_block_count + t > (unsigned long)sbi->user_block_count) { spin_unlock(&sbi->stat_lock); return -EINVAL; } *ui = t; spin_unlock(&sbi->stat_lock); return count; } if (!strcmp(a->attr.name, "discard_granularity")) { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; int i; if (t == 0 || t > MAX_PLIST_NUM) return -EINVAL; if (t == *ui) return count; mutex_lock(&dcc->cmd_lock); for (i = 0; i < MAX_PLIST_NUM; i++) { if (i >= t - 1) dcc->pend_list_tag[i] |= P_ACTIVE; else dcc->pend_list_tag[i] &= (~P_ACTIVE); } mutex_unlock(&dcc->cmd_lock); *ui = t; return count; } *ui = t; if (!strcmp(a->attr.name, "iostat_enable") && *ui == 0) f2fs_reset_iostat(sbi); if (!strcmp(a->attr.name, "gc_urgent") && t == 1 && sbi->gc_thread) { sbi->gc_thread->gc_wake = 1; wake_up_interruptible_all(&sbi->gc_thread->gc_wait_queue_head); wake_up_discard_thread(sbi, true); } return count; }
int dump_info_from_blkaddr(struct f2fs_sb_info *sbi, u32 blk_addr) { nid_t nid; int type; struct f2fs_summary sum_entry; struct node_info ni, ino_ni; int ret = 0; MSG(0, "\n== Dump data from block address ==\n\n"); if (blk_addr < SM_I(sbi)->seg0_blkaddr) { MSG(0, "\nFS Reserved Area for SEG #0: "); ret = -EINVAL; } else if (blk_addr < SIT_I(sbi)->sit_base_addr) { MSG(0, "\nFS Metadata Area: "); ret = -EINVAL; } else if (blk_addr < NM_I(sbi)->nat_blkaddr) { MSG(0, "\nFS SIT Area: "); ret = -EINVAL; } else if (blk_addr < SM_I(sbi)->ssa_blkaddr) { MSG(0, "\nFS NAT Area: "); ret = -EINVAL; } else if (blk_addr < SM_I(sbi)->main_blkaddr) { MSG(0, "\nFS SSA Area: "); ret = -EINVAL; } else if (blk_addr > __end_block_addr(sbi)) { MSG(0, "\nOut of address space: "); ret = -EINVAL; } if (ret) { MSG(0, "User data is from 0x%x to 0x%x\n\n", SM_I(sbi)->main_blkaddr, __end_block_addr(sbi)); return ret; } type = get_sum_entry(sbi, blk_addr, &sum_entry); nid = le32_to_cpu(sum_entry.nid); get_node_info(sbi, nid, &ni); DBG(1, "Note: blkaddr = main_blkaddr + segno * 512 + offset\n"); DBG(1, "Block_addr [0x%x]\n", blk_addr); DBG(1, " - Segno [0x%x]\n", GET_SEGNO(sbi, blk_addr)); DBG(1, " - Offset [0x%x]\n", OFFSET_IN_SEG(sbi, blk_addr)); DBG(1, "SUM.nid [0x%x]\n", nid); DBG(1, "SUM.type [%s]\n", seg_type_name[type]); DBG(1, "SUM.version [%d]\n", sum_entry.version); DBG(1, "SUM.ofs_in_node [0x%x]\n", sum_entry.ofs_in_node); DBG(1, "NAT.blkaddr [0x%x]\n", ni.blk_addr); DBG(1, "NAT.ino [0x%x]\n", ni.ino); get_node_info(sbi, ni.ino, &ino_ni); /* inode block address */ if (ni.blk_addr == NULL_ADDR || ino_ni.blk_addr == NULL_ADDR) { MSG(0, "FS Userdata Area: Obsolete block from 0x%x\n", blk_addr); return -EINVAL; } /* print inode */ if (config.dbg_lv > 0) dump_node_from_blkaddr(ino_ni.blk_addr); if (type == SEG_TYPE_CUR_DATA || type == SEG_TYPE_DATA) { MSG(0, "FS Userdata Area: Data block from 0x%x\n", blk_addr); MSG(0, " - Direct node block : id = 0x%x from 0x%x\n", nid, ni.blk_addr); MSG(0, " - Inode block : id = 0x%x from 0x%x\n", ni.ino, ino_ni.blk_addr); dump_node_from_blkaddr(ino_ni.blk_addr); dump_data_offset(ni.blk_addr, le16_to_cpu(sum_entry.ofs_in_node)); } else { MSG(0, "FS Userdata Area: Node block from 0x%x\n", blk_addr); if (ni.ino == ni.nid) { MSG(0, " - Inode block : id = 0x%x from 0x%x\n", ni.ino, ino_ni.blk_addr); dump_node_from_blkaddr(ino_ni.blk_addr); } else { MSG(0, " - Node block : id = 0x%x from 0x%x\n", nid, ni.blk_addr); MSG(0, " - Inode block : id = 0x%x from 0x%x\n", ni.ino, ino_ni.blk_addr); dump_node_from_blkaddr(ino_ni.blk_addr); dump_node_offset(ni.blk_addr); } } return 0; }