]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - fs/f2fs/segment.c
Merge remote-tracking branch 'f2fs/dev'
[karo-tx-linux.git] / fs / f2fs / segment.c
index 78e6d06968477d023cbd4f8c71ccc1e3fd6329fe..f77b3258454a6f9b55810a72982436e9bf7a162e 100644 (file)
@@ -14,8 +14,8 @@
 #include <linux/blkdev.h>
 #include <linux/prefetch.h>
 #include <linux/kthread.h>
-#include <linux/vmalloc.h>
 #include <linux/swap.h>
+#include <linux/timer.h>
 
 #include "f2fs.h"
 #include "segment.h"
@@ -29,6 +29,21 @@ static struct kmem_cache *discard_entry_slab;
 static struct kmem_cache *sit_entry_set_slab;
 static struct kmem_cache *inmem_entry_slab;
 
+static unsigned long __reverse_ulong(unsigned char *str)
+{
+       unsigned long tmp = 0;
+       int shift = 24, idx = 0;
+
+#if BITS_PER_LONG == 64
+       shift = 56;
+#endif
+       while (shift >= 0) {
+               tmp |= (unsigned long)str[idx++] << shift;
+               shift -= BITS_PER_BYTE;
+       }
+       return tmp;
+}
+
 /*
  * __reverse_ffs is copied from include/asm-generic/bitops/__ffs.h since
  * MSB and LSB are reversed in a byte by f2fs_set_bit.
@@ -38,27 +53,31 @@ static inline unsigned long __reverse_ffs(unsigned long word)
        int num = 0;
 
 #if BITS_PER_LONG == 64
-       if ((word & 0xffffffff) == 0) {
+       if ((word & 0xffffffff00000000UL) == 0)
                num += 32;
+       else
                word >>= 32;
-       }
 #endif
-       if ((word & 0xffff) == 0) {
+       if ((word & 0xffff0000) == 0)
                num += 16;
+       else
                word >>= 16;
-       }
-       if ((word & 0xff) == 0) {
+
+       if ((word & 0xff00) == 0)
                num += 8;
+       else
                word >>= 8;
-       }
+
        if ((word & 0xf0) == 0)
                num += 4;
        else
                word >>= 4;
+
        if ((word & 0xc) == 0)
                num += 2;
        else
                word >>= 2;
+
        if ((word & 0x2) == 0)
                num += 1;
        return num;
@@ -68,26 +87,16 @@ static inline unsigned long __reverse_ffs(unsigned long word)
  * __find_rev_next(_zero)_bit is copied from lib/find_next_bit.c because
  * f2fs_set_bit makes MSB and LSB reversed in a byte.
  * Example:
- *                             LSB <--> MSB
- *   f2fs_set_bit(0, bitmap) => 0000 0001
- *   f2fs_set_bit(7, bitmap) => 1000 0000
+ *                             MSB <--> LSB
+ *   f2fs_set_bit(0, bitmap) => 1000 0000
+ *   f2fs_set_bit(7, bitmap) => 0000 0001
  */
 static unsigned long __find_rev_next_bit(const unsigned long *addr,
                        unsigned long size, unsigned long offset)
 {
-       while (!f2fs_test_bit(offset, (unsigned char *)addr))
-               offset++;
-
-       if (offset > size)
-               offset = size;
-
-       return offset;
-#if 0
        const unsigned long *p = addr + BIT_WORD(offset);
        unsigned long result = offset & ~(BITS_PER_LONG - 1);
        unsigned long tmp;
-       unsigned long mask, submask;
-       unsigned long quot, rest;
 
        if (offset >= size)
                return size;
@@ -97,14 +106,9 @@ static unsigned long __find_rev_next_bit(const unsigned long *addr,
        if (!offset)
                goto aligned;
 
-       tmp = *(p++);
-       quot = (offset >> 3) << 3;
-       rest = offset & 0x7;
-       mask = ~0UL << quot;
-       submask = (unsigned char)(0xff << rest) >> rest;
-       submask <<= quot;
-       mask &= submask;
-       tmp &= mask;
+       tmp = __reverse_ulong((unsigned char *)p);
+       tmp &= ~0UL >> offset;
+
        if (size < BITS_PER_LONG)
                goto found_first;
        if (tmp)
@@ -112,42 +116,34 @@ static unsigned long __find_rev_next_bit(const unsigned long *addr,
 
        size -= BITS_PER_LONG;
        result += BITS_PER_LONG;
+       p++;
 aligned:
        while (size & ~(BITS_PER_LONG-1)) {
-               tmp = *(p++);
+               tmp = __reverse_ulong((unsigned char *)p);
                if (tmp)
                        goto found_middle;
                result += BITS_PER_LONG;
                size -= BITS_PER_LONG;
+               p++;
        }
        if (!size)
                return result;
-       tmp = *p;
+
+       tmp = __reverse_ulong((unsigned char *)p);
 found_first:
-       tmp &= (~0UL >> (BITS_PER_LONG - size));
-       if (tmp == 0UL)         /* Are any bits set? */
+       tmp &= (~0UL << (BITS_PER_LONG - size));
+       if (!tmp)               /* Are any bits set? */
                return result + size;   /* Nope. */
 found_middle:
        return result + __reverse_ffs(tmp);
-#endif
 }
 
 static unsigned long __find_rev_next_zero_bit(const unsigned long *addr,
                        unsigned long size, unsigned long offset)
 {
-       while (f2fs_test_bit(offset, (unsigned char *)addr))
-               offset++;
-
-       if (offset > size)
-               offset = size;
-
-       return offset;
-#if 0
        const unsigned long *p = addr + BIT_WORD(offset);
        unsigned long result = offset & ~(BITS_PER_LONG - 1);
        unsigned long tmp;
-       unsigned long mask, submask;
-       unsigned long quot, rest;
 
        if (offset >= size)
                return size;
@@ -157,40 +153,36 @@ static unsigned long __find_rev_next_zero_bit(const unsigned long *addr,
        if (!offset)
                goto aligned;
 
-       tmp = *(p++);
-       quot = (offset >> 3) << 3;
-       rest = offset & 0x7;
-       mask = ~(~0UL << quot);
-       submask = (unsigned char)~((unsigned char)(0xff << rest) >> rest);
-       submask <<= quot;
-       mask += submask;
-       tmp |= mask;
+       tmp = __reverse_ulong((unsigned char *)p);
+       tmp |= ~((~0UL << offset) >> offset);
+
        if (size < BITS_PER_LONG)
                goto found_first;
-       if (~tmp)
+       if (tmp != ~0UL)
                goto found_middle;
 
        size -= BITS_PER_LONG;
        result += BITS_PER_LONG;
+       p++;
 aligned:
        while (size & ~(BITS_PER_LONG - 1)) {
-               tmp = *(p++);
-               if (~tmp)
+               tmp = __reverse_ulong((unsigned char *)p);
+               if (tmp != ~0UL)
                        goto found_middle;
                result += BITS_PER_LONG;
                size -= BITS_PER_LONG;
+               p++;
        }
        if (!size)
                return result;
-       tmp = *p;
 
+       tmp = __reverse_ulong((unsigned char *)p);
 found_first:
-       tmp |= ~0UL << size;
-       if (tmp == ~0UL)        /* Are any bits zero? */
+       tmp |= ~(~0UL << (BITS_PER_LONG - size));
+       if (tmp == ~0UL)        /* Are any bits zero? */
                return result + size;   /* Nope. */
 found_middle:
        return result + __reverse_ffz(tmp);
-#endif
 }
 
 void register_inmem_page(struct inode *inode, struct page *page)
@@ -257,11 +249,12 @@ int commit_inmem_pages(struct inode *inode, bool abort)
                                trace_f2fs_commit_inmem_page(cur->page, INMEM);
                                fio.page = cur->page;
                                err = do_write_data_page(&fio);
-                               submit_bio = true;
                                if (err) {
                                        unlock_page(cur->page);
                                        break;
                                }
+                               clear_cold_data(cur->page);
+                               submit_bio = true;
                        }
                } else {
                        trace_f2fs_commit_inmem_page(cur->page, INMEM_DROP);
@@ -296,7 +289,7 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi)
         */
        if (has_not_enough_free_secs(sbi, 0)) {
                mutex_lock(&sbi->gc_mutex);
-               f2fs_gc(sbi);
+               f2fs_gc(sbi, false);
        }
 }
 
@@ -316,7 +309,8 @@ void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi)
        /* checkpoint is the only way to shrink partial cached entries */
        if (!available_free_memory(sbi, NAT_ENTRIES) ||
                        excess_prefree_segs(sbi) ||
-                       !available_free_memory(sbi, INO_ENTRIES))
+                       !available_free_memory(sbi, INO_ENTRIES) ||
+                       jiffies > sbi->cp_expires)
                f2fs_sync_fs(sbi->sb, true);
 }
 
@@ -767,6 +761,30 @@ void invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr)
        mutex_unlock(&sit_i->sentry_lock);
 }
 
+bool is_checkpointed_data(struct f2fs_sb_info *sbi, block_t blkaddr)
+{
+       struct sit_info *sit_i = SIT_I(sbi);
+       unsigned int segno, offset;
+       struct seg_entry *se;
+       bool is_cp = false;
+
+       if (blkaddr == NEW_ADDR || blkaddr == NULL_ADDR)
+               return true;
+
+       mutex_lock(&sit_i->sentry_lock);
+
+       segno = GET_SEGNO(sbi, blkaddr);
+       se = get_seg_entry(sbi, segno);
+       offset = GET_BLKOFF_FROM_SEG0(sbi, blkaddr);
+
+       if (f2fs_test_bit(offset, se->ckpt_valid_map))
+               is_cp = true;
+
+       mutex_unlock(&sit_i->sentry_lock);
+
+       return is_cp;
+}
+
 /*
  * This function should be resided under the curseg_mutex lock
  */
@@ -1292,6 +1310,9 @@ void write_meta_page(struct f2fs_sb_info *sbi, struct page *page)
                .encrypted_page = NULL,
        };
 
+       if (unlikely(page->index >= MAIN_BLKADDR(sbi)))
+               fio.rw &= ~REQ_META;
+
        set_page_writeback(page);
        f2fs_submit_page_mbio(&fio);
 }
@@ -1369,7 +1390,14 @@ static void __f2fs_replace_block(struct f2fs_sb_info *sbi,
        curseg->next_blkoff = GET_BLKOFF_FROM_SEG0(sbi, new_blkaddr);
        __add_sum_entry(sbi, type, sum);
 
-       refresh_sit_entry(sbi, old_blkaddr, new_blkaddr);
+       if (!recover_curseg)
+               update_sit_entry(sbi, new_blkaddr, 1);
+       if (GET_SEGNO(sbi, old_blkaddr) != NULL_SEGNO)
+               update_sit_entry(sbi, old_blkaddr, -1);
+
+       locate_dirty_segment(sbi, GET_SEGNO(sbi, old_blkaddr));
+       locate_dirty_segment(sbi, GET_SEGNO(sbi, new_blkaddr));
+
        locate_dirty_segment(sbi, old_cursegno);
 
        if (recover_curseg) {
@@ -1449,6 +1477,23 @@ void f2fs_wait_on_page_writeback(struct page *page,
        }
 }
 
+void f2fs_wait_on_encrypted_page_writeback(struct f2fs_sb_info *sbi,
+                                                       block_t blkaddr)
+{
+       struct page *cpage;
+
+       if (blkaddr == NEW_ADDR)
+               return;
+
+       f2fs_bug_on(sbi, blkaddr == NULL_ADDR);
+
+       cpage = find_lock_page(META_MAPPING(sbi), blkaddr);
+       if (cpage) {
+               f2fs_wait_on_page_writeback(cpage, DATA);
+               f2fs_put_page(cpage, 1);
+       }
+}
+
 static int read_compacted_summaries(struct f2fs_sb_info *sbi)
 {
        struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
@@ -1586,7 +1631,7 @@ static int restore_curseg_summaries(struct f2fs_sb_info *sbi)
 
                if (npages >= 2)
                        ra_meta_pages(sbi, start_sum_block(sbi), npages,
-                                                               META_CP);
+                                                       META_CP, true);
 
                /* restore for compacted data summary */
                if (read_compacted_summaries(sbi))
@@ -1596,7 +1641,7 @@ static int restore_curseg_summaries(struct f2fs_sb_info *sbi)
 
        if (__exist_node_summaries(sbi))
                ra_meta_pages(sbi, sum_blk_addr(sbi, NR_CURSEG_TYPE, type),
-                                       NR_CURSEG_TYPE - type, META_CP);
+                                       NR_CURSEG_TYPE - type, META_CP, true);
 
        for (; type <= CURSEG_COLD_NODE; type++) {
                err = read_normal_summaries(sbi, type);
@@ -1955,12 +2000,13 @@ static int build_sit_info(struct f2fs_sb_info *sbi)
 
        SM_I(sbi)->sit_info = sit_i;
 
-       sit_i->sentries = vzalloc(MAIN_SEGS(sbi) * sizeof(struct seg_entry));
+       sit_i->sentries = f2fs_kvzalloc(MAIN_SEGS(sbi) *
+                                       sizeof(struct seg_entry), GFP_KERNEL);
        if (!sit_i->sentries)
                return -ENOMEM;
 
        bitmap_size = f2fs_bitmap_size(MAIN_SEGS(sbi));
-       sit_i->dirty_sentries_bitmap = kzalloc(bitmap_size, GFP_KERNEL);
+       sit_i->dirty_sentries_bitmap = f2fs_kvzalloc(bitmap_size, GFP_KERNEL);
        if (!sit_i->dirty_sentries_bitmap)
                return -ENOMEM;
 
@@ -1982,8 +2028,8 @@ static int build_sit_info(struct f2fs_sb_info *sbi)
                return -ENOMEM;
 
        if (sbi->segs_per_sec > 1) {
-               sit_i->sec_entries = vzalloc(MAIN_SECS(sbi) *
-                                       sizeof(struct sec_entry));
+               sit_i->sec_entries = f2fs_kvzalloc(MAIN_SECS(sbi) *
+                                       sizeof(struct sec_entry), GFP_KERNEL);
                if (!sit_i->sec_entries)
                        return -ENOMEM;
        }
@@ -2028,12 +2074,12 @@ static int build_free_segmap(struct f2fs_sb_info *sbi)
        SM_I(sbi)->free_info = free_i;
 
        bitmap_size = f2fs_bitmap_size(MAIN_SEGS(sbi));
-       free_i->free_segmap = kmalloc(bitmap_size, GFP_KERNEL);
+       free_i->free_segmap = f2fs_kvmalloc(bitmap_size, GFP_KERNEL);
        if (!free_i->free_segmap)
                return -ENOMEM;
 
        sec_bitmap_size = f2fs_bitmap_size(MAIN_SECS(sbi));
-       free_i->free_secmap = kmalloc(sec_bitmap_size, GFP_KERNEL);
+       free_i->free_secmap = f2fs_kvmalloc(sec_bitmap_size, GFP_KERNEL);
        if (!free_i->free_secmap)
                return -ENOMEM;
 
@@ -2082,7 +2128,7 @@ static void build_sit_entries(struct f2fs_sb_info *sbi)
        int nrpages = MAX_BIO_BLOCKS(sbi);
 
        do {
-               readed = ra_meta_pages(sbi, start_blk, nrpages, META_SIT);
+               readed = ra_meta_pages(sbi, start_blk, nrpages, META_SIT, true);
 
                start = start_blk * sit_i->sents_per_block;
                end = (start_blk + readed) * sit_i->sents_per_block;
@@ -2174,7 +2220,7 @@ static int init_victim_secmap(struct f2fs_sb_info *sbi)
        struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
        unsigned int bitmap_size = f2fs_bitmap_size(MAIN_SECS(sbi));
 
-       dirty_i->victim_secmap = kzalloc(bitmap_size, GFP_KERNEL);
+       dirty_i->victim_secmap = f2fs_kvzalloc(bitmap_size, GFP_KERNEL);
        if (!dirty_i->victim_secmap)
                return -ENOMEM;
        return 0;
@@ -2196,7 +2242,7 @@ static int build_dirty_segmap(struct f2fs_sb_info *sbi)
        bitmap_size = f2fs_bitmap_size(MAIN_SEGS(sbi));
 
        for (i = 0; i < NR_DIRTY_TYPE; i++) {
-               dirty_i->dirty_segmap[i] = kzalloc(bitmap_size, GFP_KERNEL);
+               dirty_i->dirty_segmap[i] = f2fs_kvzalloc(bitmap_size, GFP_KERNEL);
                if (!dirty_i->dirty_segmap[i])
                        return -ENOMEM;
        }
@@ -2301,7 +2347,7 @@ static void discard_dirty_segmap(struct f2fs_sb_info *sbi,
        struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
 
        mutex_lock(&dirty_i->seglist_lock);
-       kfree(dirty_i->dirty_segmap[dirty_type]);
+       kvfree(dirty_i->dirty_segmap[dirty_type]);
        dirty_i->nr_dirty[dirty_type] = 0;
        mutex_unlock(&dirty_i->seglist_lock);
 }
@@ -2309,7 +2355,7 @@ static void discard_dirty_segmap(struct f2fs_sb_info *sbi,
 static void destroy_victim_secmap(struct f2fs_sb_info *sbi)
 {
        struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
-       kfree(dirty_i->victim_secmap);
+       kvfree(dirty_i->victim_secmap);
 }
 
 static void destroy_dirty_segmap(struct f2fs_sb_info *sbi)
@@ -2348,8 +2394,8 @@ static void destroy_free_segmap(struct f2fs_sb_info *sbi)
        if (!free_i)
                return;
        SM_I(sbi)->free_info = NULL;
-       kfree(free_i->free_segmap);
-       kfree(free_i->free_secmap);
+       kvfree(free_i->free_segmap);
+       kvfree(free_i->free_secmap);
        kfree(free_i);
 }
 
@@ -2370,9 +2416,9 @@ static void destroy_sit_info(struct f2fs_sb_info *sbi)
        }
        kfree(sit_i->tmp_map);
 
-       vfree(sit_i->sentries);
-       vfree(sit_i->sec_entries);
-       kfree(sit_i->dirty_sentries_bitmap);
+       kvfree(sit_i->sentries);
+       kvfree(sit_i->sec_entries);
+       kvfree(sit_i->dirty_sentries_bitmap);
 
        SM_I(sbi)->sit_info = NULL;
        kfree(sit_i->sit_bitmap);